之前了解了进程的相关基础知识,现在来了解一下进程的实际应用
1进程创建的一般过程:
1)给新进程分配一个标识符。内核中分配一个PCB
2)复制父进程的环境
3)分配程序.数据.栈等内存资源
4)复制父进程的地址空间的内容
5)将进程置成就绪状态,放入就绪队列
2.fork
1)首先调用fork之前,要加头文件#include
2)函数原型 pid_t fork(void)
3)返回值:子进程返回0,父进程返回子进程id,出错返回-1,创建子进程失败。
4)fork调用失败原因:系统内有太多进程或者实际用户的进程数超过了限制。
下面验证一下fork()的返回值
图中箭头和数字代表程序执行步骤
运行结果:
解释一下:程序开始执行,首先进入父进程,在调用fork之前,先输出父进程的id,执行fork后,父子进程会从fork之后的代码来继续执行,继续执行父进程,pid接受fork返回值,父进程返回子进程id,然后进入子进程,pid接收fork返回值,子进程返回0。
注意:
1)父子进程执行先后顺序不确定
2)调用fork以后,父子进程交替运行。
3)如果父进程先死,子进程就变成孤儿进程,孤儿进程会被1号进程收养。
4)如果子进程先死,子进程就会变成僵尸进程(清理僵尸进程,用wait函数)
另一种执行步骤:(fork后先执行子进程)
3.子进程与父进程
子进程继承父进程:
地址空间,进程上下文,进程堆栈,内存信息,文件描述符(父进程打开的文件),信号设置,进程调度优先级,当前路径,根路径,控制终端,进程组,资源限制情况...
子进程不继承父进程:
1>父进程的锁子进程不继承
2>父进程id子进程不继承
3>父进程未决的信号子进程不继承
4>父进程的闹钟子进程不继承
子进程与父进程的关系:
子进程以父进程为模板,首先复制父进程的PCB,并进行简单修改;复制父进程的虚拟地址空间(页表)对应的物理内存会进行写时拷贝。
4.vfork
1)首先调用vfork之前,要加头文件#include
2)函数原型 pid_t vfork(void)
vfork 与fork的区别
1)vfork用于创建一个子进程,创建出来的子进程和父进程共用同一块虚拟空间。但fork出来的子进程具有独立的地址空间。
2)vfork出来的子进程一定会先执行,父进程会挂起,一直挂起到子进程调用了_exit或exec父进程才会继续执行。
实例:
#include#include #include #include int glob=100; int main(void){ pid_t pid; pid = vfork(); if(pid==-1) perror("fork"),exit(1); else if(pid ==0) {//子进程 sleep(3); glob=200; printf("child glob %d \n",glob); exit(0); } else { //父进程 printf("parent glob %d \n",glob); } return 0; }
运行结果:
解释一下:子进程先运行,修改了glob变量的值,调用了exit,父进程调度执行,父子进程共用一块虚拟内存,所以glob的值也被更改。