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. 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)

 首先我们来看一个现象

#include<iostream>    
#include<unistd.h>    
    
using namespace std;    
int main() {    
    fork();    
    cout << "now: " << getpid()  <<  "   father: " << getppid() << endl;                                                                                                                    
    
    return 0;    
}    

结果: 

        可以看到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那条语句,子进程也会执行一部分,就是最后返回的那部分,所以会有两个返回值)

  pid_t fork() {    
      //创建子进程的逻辑    
          
      return XXX;                                                                                                                                                                           
  }  

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

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

写时拷贝:

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

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

#include<iostream>  
#include<unistd.h>  
  
using namespace std;  
int main()                                                                                                                                                                                  
{            
    pid_t id = fork();                                                    
    if(id == 0){        
        //child   
        while(true){  
            cout << " I am child, running!" << endl;  
            sleep(2);                                 
        }               
    }             
    else{        
        //parent      
        cout << "father do nothing!\n" << endl;       
        sleep(10);                               
        exit(1);    
    }             
    return 0;  
}  

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值