目录
1.进程的创建
进程=内核数据结构+代码和数据
由于进程具有独立性,所以子进程必须有自己的内核数据结构。但是,为了提高效率,操作系统并没有给子进程单独开空间拷贝数据。换言之,就是父子进程控制着相同的代码和数据。但是这样若是有一个进程进行写入数据会产生一个数据多值的问题,所以会用到写时拷贝。
我们知道每段代码都会产生自己对应的地址,而进程随时都可能因为各种原因中断。CPU为了接下来重新执行时可以直接从之前的位置执行,就必须要寄存器的辅助。CPU只有一份寄存器,但寄存器可以存多份数据。那么就可以解释子进程的创建了,实际上就是子进程对应的EIP(寄存器的一种)就存的是fork()之后的代码。
2.进程的退出
a.操作系统实际就是在进程结束回收了它的内核数据结构和代码和数据,释放系统资源。
b.在遥远的曾今,我们的初识c语言时都写过这样的代码:
#include<stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
那这个return 0 的含义是什么呢?
实际上就是进程退出时返回的退出码是0!
其中,当退出码为0表示结果正确并退出,为其他值是表示结果错误。
Linux下可以用 echo $?查看最近一个进程的退出码。
c.除了用return 返回退出码 还可以用exit(库函数)和_exit(系统接口)退出进程。
3.进程的等待(阻塞和非阻塞)
a.我们都知道如果子进程退出但父进程并没有来回收就会导致子进程陷入僵尸状态。这时进程等待意义就体现出来了:如果父进程等待子进程退出,一退出就回收子进程就可以有效避免僵尸。
b.了解两个接口wait/waitpid
实际上wait是waitpid的子集,所以我们细说waitpid。
先看看声明: pid_t waitpid(pid_t id,int* status,int options)
第一个参数id表示需要等待的进程的pid,如果是-1,就是等待所有其他进程。第二个status是一个输出型参数,我们要按byte进行学习。我们只用关注32中的低16位。
其中最低的7位为终止信号,次低8位为退出码。
第三个参数options可以是0或1(代表阻塞等待和非阻塞等待),而Linux这里的1又被WNHANG替换。
4.用代码试试两种不同等待方式
a.阻塞等待
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<sys/wait.h>
4 #include<stdlib.h>
5 int main()
6 {
7 pid_t id = fork();
8 if(id == 0)
9 {
10 int cnt = 5;
11 while(cnt--)
12 {
13 printf("我是子进程,你还需要等待%d秒\n",cnt);
14 sleep(1);
15 }
16 exit(12); //设置12为退出码,仅仅为测试。
17 }
18 if(id > 0)
19 {
20 int status=0;
21 pid_t ret = waitpid(-1,&status,0);
22 if(ret > 0)
23 {
24 printf("阻塞等待成功,退出码为:%d\n",WEXITSTATUS(status));
25 // WEXITSTATUS(status) 和(staus>>8)&0xFF 是一样的只是前面为宏替换的结果。
26 }
27 else
28 {
29 printf("阻塞等待失败\n");
30 }
31 }
32
33 return 0;
阻塞等待即是父进程停止手上忙的东西,等子进程结束就立即回收子进程。
b.非阻塞等待
非阻塞等待即是父进程去瞅一眼子进程是否结束,手上的事情继续。
等子进程结束就立即回收子进程。