零、前言
前篇我们讲解学习了关于进程的概念知识,本章主要讲解关于进程的控制,深入学习进程
一、进程创建
1、fork函数
- 概念:
在linux中fork函数从已存在进程中创建一个新进程(子进程),而原进程为父进程
- fork函数原型:
pid_t fork(void);
注意:
使用fork()函数需要包含头文件<unistd.h>;pid_t类型需要包含头文件<sys/types.h>
fork成功后对子进程返回0,对父进程返回子进程id,fork出错返回-1
内核视角看待fork:
进程调用fork,内核分配新的内存块和内核数据结构给子进程
将父进程部分数据结构内容拷贝至子进程(例如PCB进程控制块,进程地址空间,页表等)
添加子进程到系统进程列表当中,当fork返回后开始调度器调度进程
示图:
fork后执行问题:
当一个进程调用fork之后,父子进程共享同一份代码,也就是说整个代码父子进程都可以看到,但是此时父子进程的执行位置都是相同的,也就是说fork返回后子进程也是往fork之后的代码执行(并非再从头执行)
示例:
#include<stdio.h> #include<unistd.h> #include<sys/types.h> int main() { printf("Before fork: pid is %d\n", getpid()); pid_t pid=fork(); if (pid== -1 )//fork错误 { perror("fork fail"); exit(1); } printf("After fork:pid is %d, fork return %d\n", getpid(), pid); sleep(1); return 0; }
结果:
示图:
2、fork返回值
返回值:
fork成功对子进程返回0,对父进程返回子进程的pid
写时拷贝
概念:
fork成功之后父子代码共享,当父子不写入数据时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本
为什么数据要进行写时拷贝:
进程具有独立性,多进程运行,需要独享各种资源,多进程运行期间互不干扰,不能让子进程的修改影响到父进程
为什么不在创建子进程的时候就进行数据的拷贝:
子进程不一定会使用父进程的所有数据,并且在子进程不对数据进行写入的情况下,没有必要对数据进行拷贝,我们应该按需分配,在需要修改数据的时候再分配(延时分配),这样可以高效的使用内存空间,提高fork效率,以及fork的成功率
代码会不会进行写时拷贝:
90%的情况下是不会的,但这并不代表代码不能进行写时拷贝,例如在进行进程替换的时候,则需要进行代码的写时拷贝
示图:
fork函数为什么要给子进程返回0,给父进程返回子进程的PID:
一个父进程可以创建多个子进程,而一个子进程只能有一个父进程。因此,对于子进程来说,父进程是不需要被标识的;而对于父进程来说,子进程是需要被标识的,因为父进程创建子进程的目的是让其执行任务的,父进程只有知道了子进程的PID才能很好的对该子进程进行深入操作
为什么fork存在“两个”返回值:
父进程创建子进程时,子进程以父进程为模板构建进程,代码数据父子共享,返回时也是父子进程进行修改数据时,由页表发现该数据是父子进程共享的,所以系统会找到另一个物理空间进行拷贝数据,拷贝数据后再修改数据,达到数据各有一份互不干扰的目的,保证进程的独立性
3、fork用法
我们创建子进程并不是为了父进程执行一样的代码,而是为了使父子进程同时执行不同的代码段
例如:父进程等待客户端请求,生成子进程来处理请求
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<sys/types.h> int main() { pid_t id=fork();//创建子进程 if(id==0) { //child int cnt=0; while(1) { printf("I am child: pid:%d ppid:%d\n",getpid(),getppid()); sleep(1); if(cnt==8) break; cnt++; } exit(1);//终止进程 } else if(id>0) { //father int cnt=0; while(1) { printf("I am father: pid:%d ppid:%d\n",getpid(),getppid()); sleep(1); if(cnt==8) break; cnt++; } } return 0; }
- 用法2:fork返回后调用exec函数替换进程
注:在下文有着重讲解
4、fork失败
fork本质就是向系统要资源,当某个资源不够时则会发生fork失败
- 失败原因:
1.系统中有太多的进程
2.实际用户的进程数超过了限制
用法1:fork返回后分流执行不同代码
示例: