目录
1.程序的一个执行实例,正在执行的程序等
2.担当分配系统资源(CPU时间,内存)的实体。
进程(Process)是系统进行资源分配和调度的基本单位
管理进程我们要先描述,再组织,所以任何进程在形成的时候,操作系统要为该进程创建PCB,进程控制块都是一个个结构体的形式,里面存放进程的属性。
进程是程序的一个执行实例,担当分配系统资源(CPU时间,内存)的实体。
曾经我们所有的启动程序的过程,本质都是在系统上面创建进程
1.程序本质上都是文件(可执行程序/二进制文件),首先我们的可执行程序文件必须被加载到内存中,才能执行这个程序。
2.我们要为进程创建一个PCB,也就是一个task_struct,这个task_struct加上刚刚加载进来的文件,总称为进程(进程 = 程序文件内容+相关的数据结构(比如PCB,task_struct就是一个数据结构))
3.task_struct包含了进程内部的所有的属性信息,这些信息也是数据
进程 = 进程代码数据文件+操作系统维护的相关数据结构(比如task_struct就是一种数据结构)
一个进程里面有进程代码数据文件+PCB,PCB用双向链表的方式链接起来(组织进程),操作系统以后找进程的时候直接找PCB就行,而不是去找进程的代码和数据。(PCB里面存放了进程的信息)
- #include
- #include
-
- 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调用创建进程,对操作系统来说都是多了一个进程,两者没有差别
2.fork本质是创建进程,系统中会多一个进程,因此理论上说与进程相关的内核数据结构(PCB-task_struct)+进程的代码和数据会在系统里面多一份
3.我们只是fork创建了子进程,有自己的task_struct,在默认情况下,子进程和父进程共用一份代码(代码是不可被修改的,数据可能会被修改,所以代码只存储一份),数据也是共享的,但是需要考虑数据被修改的情况(一旦有一个人想要写入数据,就不是共享的了,进行写时拷贝)。
4.其task_struct也会以父进程为模板初始化子进程的task_struct。
5.进程是具有独立性的,就算父进程挂了(修改数据),也不会影响子进程(不影响子进程的数据);子进程挂了也不会影响父进程,因此数据通过写时拷贝来保证进程数据的独立性(如果都是只读,那就用同一个数据;如果有一个进程要写入数据,就拷贝一份,然后写入数据)
写时拷贝:
在数据第一次写入到某个存储位置时,首先将原有内容拷贝出来,写到另一位置处,然后再将数据写入到存储设备中,该技术只拷贝在拷贝初始化开始之后修改过的数据。
1.我们创建子进程的时候,fork以后,如果直接跟上代码,那么子进程和父进程相当于重复做了同一件事情。我们创建子进程的目的应该是要和父进程执行不一样的任务的。
2.对于子进程和父进程,fork的返回值是不相同的。如果fork成功了,对于父进程就会返回子进程的PID,对于子进程就会返回0。如果fork失败,返回0
因此我们可以使用if else来进行分流,让父进程和子进程做不一样的事情。
如果返回值 <0,表示创建子进程失败
如果返回值 = 0,表示是子进程
如果返回值 > 0,表示是父进程,此时返回值是子进程的pid
调用fork才会创建子进程,那fork前面的代码还有吗?为什么父子进程会有两个不同的id?fork执行了两次吗?
A:代码只保留一份,父进程和子进程拿到的都是整个完整的代码,他们的task_struct中有程序计数器,存放的下一条指令的地址
为什么会有两个返回值,而且不相同,具体原因如下:
Q:如果一个函数都开始return了,这个函数的核心功能执行完了吗?
A:肯定是执行完了,所以在fork中,return返回之前,子进程已经创建好了,此时父子进程都会向下执行,都会return,所以也就有两个返回值了(fork那条语句,子进程也会执行一部分,就是最后返回的那部分,所以会有两个返回值)
- pid_t fork() {
- //创建子进程的逻辑
-
- return XXX;
- }
Q:这个返回值是数据吗?return的时候,会写入吗
A:是数据,会写入,这里发生了写时拷贝,所以会有两个不同的返回值。
写时拷贝:
在数据第一次写入到某个存储位置时,首先将原有内容拷贝出来,写到另一位置处,然后再将数据写入到存储设备中,该技术只拷贝在拷贝初始化开始之后修改过的数据。
- #include
- #include
-
- 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;
- }