赞
踩
Linux中man fork,查看fork的相关信息
注意:vim中的底行模式也可以进行相关操作
- #include<iostream>
- #include<unistd.h> int main()
- {
- fork();
- std::cout<<"hello file.c:"<<getpid()<<"hello parent:"<<getppid()<<std::endl;
- return 0;
- }
从上述结果可以看到,fork之后的代码被执行了两次,接下来将深入理解fork。
fork的本质是创建进程,意味着系统中多了一个进程,也就是多了与该进程相关的内核数据结构和该进程的代码和数据。
fork创建的子进程,它的代码和数据从哪里来?
默认情况下,子进程会继承父进程的代码和数据,子进程的内核数据结构task_struct也会以父进程为模板来初始化子进程的task_struct。
fork之后,子进程和父进程的代码是共享的,也包括fork之前的代码,只不过执行流已经到达fork,往后执行。
因为代码是不可以被修改的,所以父子代码就只有一份。(不可能说运行之后,原本往屏幕上个打印hello world,变成了打印其他内容,所以代码是不可被修改的)
对于数据:默认情况下也是共享的,当父或子对数据要进行修改时,会发生“写时拷贝”。因为进程之间具有独立性。
从上面,我们已经知道了fork创建子进程,子进程的代码和数据从哪里来,以及父子进程要对数据进行修改时,会发生“写时拷贝”,问题来了,费这么大劲创建子进程就是为了让父子干一样的事?答案当然是否定的,如何让父子做不一样的事?
通过fork的返回值来完成,创建失败:返回值<0
创建成功:给父进程返回子进程的pid,给子进程返回0
- #include<iostream>
- #include<unistd.h>
- int main()
- {
- pid_t ret=fork();
- if(ret==0)
- {
- //子进程
- std::cout<<"I am child:"<<getpid()<<" parent:"<<getppid()<<std::endl;
- }
- else if(ret>0)
- {
- //父进程
- std::cout<<"I am parent:"<<getpid()<<" parent:"<<getppid()<<std::endl;
- }
- else{
- //创建失败
- }
- return 0;
- }
fork是Linux中的系统调用接口,而Linux的内核是用C语言写的,所以fork就是一个C函数,如图:
因为父与子是1:n,给父进程返回子进程pid,是为了父进程更好的管理子进程。
答:不确定。
进程状态信息在task_struct里,也就是pcb中。
进程状态的意义:方便操作系统快速判断进程,完成特定的功能,比如调度,本质就是分类。
注意:S+,这个加号说明进程在前台运行,就是./myproc,如果要在后台跑,可以./myproc &
R状态——运行状态
问:R状态一定正在占有cpu吗?
答:不一定。一个进程是R状态,说明该进程已经放入运行队列中,随时可以被cpu调度。
当我们想完成某种任务时,任务条件不具备,需要进程进行某种等待,就是S和D。
进程在运行的时候,有可能因为运行的需要,会在不同的队列中,在不同的队列里进程所处的状态是不一样的。
我们把从运行状态的task_struct交到等待队列中,称为挂起等待(阻塞)。
从等待队列放到运行队列,被cpu调度,称为唤醒。
S状态:浅度睡眠或者可中断睡眠(等待状态),休眠状态,Sleeping。
D状态:不可中断睡眠或者深度睡眠。如果进程处于D状态,不可被杀掉,操作系统也无权杀掉。
T(stopped):暂停。与S状态类似,S状态下进程相关的数据可能会更新。T状态是彻底的暂停,不会有数据的更新。
t(tracing step):追踪状态。类似于vs、gdb中的调试。、
X(dead):死亡状态。回收进程资源=进程相关内核数据结构+代码和数据。
Z(zombie):僵尸状态。
僵尸状态是为了辨别进程退出,也就是死亡原因,获取进程退出信息(数据)(task_struct)。
当一个进程退出时,它的所有资源并不是立即被释放,而是先进入僵尸状态,系统将进程退出信息写在pcb中,供父进程或OS来读取。
一个进程的父进程先于子进程一步退出,那么这个进程就会被操作系统领养——孤儿进程
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。