• Linux操作系统~什么是进程,进程的内部结构是什么?fork如何创建子进程?


    目录

     1.进程的概念

    2.先描述进程,再组织进程

    1).进程VS程序

    2).进程内部结构

    3.PCB(task_struct)内部构成

    4.fork()创建进程

     首先我们来看一个现象

    1).理解fork创建子进程

    2).fork的返回值

    Q:fork返回值为什么能有两个,分别给父进程和子进程

    利用fork返回值的不同,让父子进程执行不同的内容


     1.进程的概念

    1.程序的一个执行实例,正在执行的程序等

    2.担当分配系统资源(CPU时间,内存)的实体。

    进程(Process)是系统进行资源分配和调度的基本单位


    2.先描述进程,再组织进程

            管理进程我们要先描述,再组织,所以任何进程在形成的时候,操作系统要为该进程创建PCB,进程控制块都是一个个结构体的形式,里面存放进程的属性。

    1. 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
    2. 操作系统总的称呼为PCB(process control block),Linux操作系统下的PCB是: task_struct

    1).进程VS程序

    进程是程序的一个执行实例,担当分配系统资源(CPU时间,内存)的实体。

    曾经我们所有的启动程序的过程,本质都是在系统上面创建进程

    1.程序本质上都是文件(可执行程序/二进制文件),首先我们的可执行程序文件必须被加载到内存中,才能执行这个程序。

    2.我们要为进程创建一个PCB,也就是一个task_struct,这个task_struct加上刚刚加载进来的文件,总称为进程(进程 = 程序文件内容+相关的数据结构(比如PCB,task_struct就是一个数据结构))

    3.task_struct包含了进程内部的所有的属性信息,这些信息也是数据

    2).进程内部结构

    进程 = 进程代码数据文件+操作系统维护的相关数据结构(比如task_struct就是一种数据结构)

            一个进程里面有进程代码数据文件+PCB,PCB用双向链表的方式链接起来(组织进程),操作系统以后找进程的时候直接找PCB就行,而不是去找进程的代码和数据。(PCB里面存放了进程的信息)


    3.PCB(task_struct)内部构成

    • 标示符(PID): 描述本进程的唯一标示符,用来区别其他进程。
    • 状态: 任务状态+退出代码,退出信号等。
    • 优先级: 相对于其他进程的优先级。(对比权限,权限是决定能不能;优先级是决定先后)
    • 程序计数器: 程序中正在执行指令的下一条指令的地址
    • 内存指针(可以通过这个指针找到进程的代码和数据): 包括程序代码和进程相关数据的
    • 指针,还有和其他进程共享的内存块的指针
    • 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
    • I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
    • 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。(用于调度模块中:调度模块可以让每个进程更为公平的获取CPU资源。)
    • 其他信息

    4.fork()创建进程

    1. fork有两个返回值
    2. 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)

     首先我们来看一个现象

    1. #include
    2. #include
    3. using namespace std;
    4. int main() {
    5. fork();
    6. cout << "now: " << getpid() << " father: " << getppid() << endl;
    7. return 0;
    8. }

    结果: 

            可以看到cout输出执行了两次,上面的父进程的PID是22281,PPID是22413;下面子进程PID是29282,PPID是父进程的PID29282。

    父进程的父进程是bash,对应的父进程的PPID

    所以实际上是bash创建父进程,父进程调用fork创建子进程。        

    1).理解fork创建子进程

    1.不管是命令行上执行命令运行程序来创建进程还是通过fork调用创建进程,对操作系统来说都是多了一个进程,两者没有差别

    2.fork本质是创建进程,系统中会多一个进程,因此理论上说与进程相关的内核数据结构(PCB-task_struct)+进程的代码和数据会在系统里面多一份

    3.我们只是fork创建了子进程,有自己的task_struct,在默认情况下,子进程和父进程共用一份代码(代码是不可被修改的,数据可能会被修改,所以代码只存储一份),数据也是共享的,但是需要考虑数据被修改的情况(一旦有一个人想要写入数据,就不是共享的了,进行写时拷贝)。

    4.其task_struct也会以父进程为模板初始化子进程的task_struct。

    5.进程是具有独立性的,就算父进程挂了(修改数据),也不会影响子进程(不影响子进程的数据);子进程挂了也不会影响父进程,因此数据通过写时拷贝来保证进程数据的独立性(如果都是只读,那就用同一个数据;如果有一个进程要写入数据,就拷贝一份,然后写入数据

    写时拷贝:

    在数据第一次写入到某个存储位置时,首先将原有内容拷贝出来,写到另一位置处,然后再将数据写入到存储设备中,该技术只拷贝在拷贝初始化开始之后修改过的数据。


    2).fork的返回值

    1.我们创建子进程的时候,fork以后,如果直接跟上代码,那么子进程和父进程相当于重复做了同一件事情。我们创建子进程的目的应该是要和父进程执行不一样的任务的。

    2.对于子进程和父进程,fork的返回值是不相同的。如果fork成功了,对于父进程就会返回子进程的PID,对于子进程就会返回0。如果fork失败,返回0

    因此我们可以使用if else来进行分流,让父进程和子进程做不一样的事情。

    如果返回值 <0,表示创建子进程失败

    如果返回值 = 0,表示是子进程

    如果返回值 > 0,表示是父进程,此时返回值是子进程的pid

    Q:fork返回值为什么能有两个,分别给父进程和子进程

            调用fork才会创建子进程,那fork前面的代码还有吗?为什么父子进程会有两个不同的id?fork执行了两次吗?

    A:代码只保留一份,父进程和子进程拿到的都是整个完整的代码,他们的task_struct中有程序计数器,存放的下一条指令的地址

    为什么会有两个返回值,而且不相同,具体原因如下:

    Q:如果一个函数都开始return了,这个函数的核心功能执行完了吗?

    A:肯定是执行完了,所以在fork中,return返回之前,子进程已经创建好了,此时父子进程都会向下执行,都会return,所以也就有两个返回值了(fork那条语句,子进程也会执行一部分,就是最后返回的那部分,所以会有两个返回值)

    1. pid_t fork() {
    2. //创建子进程的逻辑
    3. return XXX;
    4. }

    Q:这个返回值是数据吗?return的时候,会写入吗

    A:是数据,会写入,这里发生了写时拷贝,所以会有两个不同的返回值。

    写时拷贝:

            在数据第一次写入到某个存储位置时,首先将原有内容拷贝出来,写到另一位置处,然后再将数据写入到存储设备中,该技术只拷贝在拷贝初始化开始之后修改过的数据。

    利用fork返回值的不同,让父子进程执行不同的内容

    1. #include
    2. #include
    3. using namespace std;
    4. int main()
    5. {
    6. pid_t id = fork();
    7. if(id == 0){
    8. //child
    9. while(true){
    10. cout << " I am child, running!" << endl;
    11. sleep(2);
    12. }
    13. }
    14. else{
    15. //parent
    16. cout << "father do nothing!\n" << endl;
    17. sleep(10);
    18. exit(1);
    19. }
    20. return 0;
    21. }

  • 相关阅读:
    bean生命周期
    2020年亚太杯APMCM数学建模大赛A题激光标记舱口轮廓生成求解全过程文档及程序
    关于Win10安装keil并且电脑连接jlink踩过的坑
    第P8周—YOLOv5-C3模块实现
    Java 性能基准测试:从 OpenJDK 8 到 OpenJDK 19
    MySQL之账号管理、建库以及四大引擎
    嵌入式软件打log的一些心得
    深入JS 中三类循环原理和性能
    【C++】继承(定义、菱形继承、虚拟继承)
    三、nginx设置浏览器缓存[expires]
  • 原文地址:https://blog.csdn.net/qq_24016309/article/details/127631431