今天分享一下关于Linux进程控制的知识点
1.进程创建
fork( )函数
#include<unistd.h>
pid_t fork(void);
通过返回值判断是否创建成功,分流父子进程
对于父进程,返回的是子进程的pid>0
对于子进程,返回的是0
创建过程:通过复制调用父进程,创建一个新的进程(子进程)。
复制的父进程的pcb,复制了父进程大部分的数据,因此代码共享,但是数据独有,所以是一种写实拷贝。
因为在父子不再写入数据时,父子的数据是共享的,但是一方试图写入的话,便用写实拷贝的方式各自一份副本。
同时在这里也提到一下vfork( )这个进程创建函数:
子进程与父进程公用同一块虚拟地址空间—子进程先运行,等到子进程exit退出或者程序替换后,父进程才开始运行,子进程return后资源释放,父进程则调用栈混乱,因此vfork创建子进程不能在main中return退出。
vfork的存在意义:
快速创建子进程,并且子进程是专门用来运行其他程序,公用地址空间可以减少子进程数据拷贝父进程的消耗,因此速度快。但是vfork逐渐被淘汰了,因为fork函数的写实拷贝技术的出现。
xshell6中vfork( )函数代码实现部分:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4
5 int main()
6 {
7 int pid = vfork();
8 if(pid==0){
9 printf("child---%d\n",getpid());
10 }
11 else{
12 printf("parents---%d\n",getpid());
13 }
14 return 0;
15
16 while(1){
17 printf("---%d\n",getpid());
18 sleep(1);
19 }
20 return 0;
21 }
2.进程等待
进程等待就是为了等待子进程的退出,避免产生僵尸进程!
父进程不知道子进程什么时候退出,所以忙其他事情,这时候如果子进程退出了,操作
系统的通知,父进程可能就没有关注到,子进程成为僵尸进程,僵尸进程过多的话就会导致资源泄漏,无法创建创建新的子进程,危害很大。
关于僵尸进程概念一节请参考我的另一篇博客:
https://blog.csdn.net/Python_programer/article/details/88921155
wait(int)—阻塞函数;等待任意一个子进程退出。
wait(NULL)表示不关心子进程的返回值,让操作系统直接释放子进程。
阻塞:为了完成功能发起调用,如果当前不具备完成条件,则一直等待,直到完成后返回。
非阻塞:为了完成功能发起调用,如果当前不具备完成条件,则立即报错返回。
阻塞与非阻塞的区别:调用功能当前不具备完成条件是否立即返回。
pid_t waitpid(pid_t pid,int* statu,int opt)
pid:
-1 : 等待任意子进程退出
0 :等待指定子进程退出
status :用于获取退出原因
options:
WNOHANG 将waitpid设置为非阻塞
函数返回值: -1 错误 0 子进程没有退出 >0 退出子进程的pid
可以等待任意一个子进程退出或者等待指定子进程退出,waitpid可以通过opt将操作设置为非阻塞。
获取子进程status:
status不能简单的当作整形来看待,可以当作位图来看待如下图所示,只研究status低16比特位。
低十六位的高八位存放子进程退出的返回值。
低十六位的低八位的高一位叫1b(core dump标志) 程序异常退出时,是否保存运行信息。
低七位为存储异常退出信号值。
如何获取子进程退出码:(statu>>8)&0xff(status为int型)。
判断程序是否正常退出:status&0x7f --异常信号值为0,证明程序是正常退出,否则异常退出。
WIFEXITED(status):判断进程是否正常退出,正常退出则返回true
WEXITSTATUS(status):在进程正常退出时,获取子进程退出码
if(WIFEXITED(status)){
printf(“退出值为 %d\n”, WEXITSTATUS(status));
}
WIFSIGNALED(status)为非0 表明进程异常终止。
若上宏为真,此时可通过WTERMSIG(status)获取使得进程退出的信号编号
if(WIFSIGNALED(status)){
printf(“使得进程终止的信号编号: %d\n”,WTERMSIG(status));
}
xshell6中waitpid()函数代码实现部分:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4
5 int main()
6 {
7 int pid = fork();
8 if(pid<0){
9 perror("fork error!");
10 exit(-1);
11 }
12 else if(pid==0){
13 sleep(5);
14 exit(255);
15 }
16
17 int statu;
18 int ret;
19 while((ret = waitpid( pid, &statu, WNOHANG))==0){
20 printf("忙其他的事!\n");
21 sleep(1);
22 }
23 if(!(statu & 0xf7)){
24 printf("%d--%d---the child exit code:%d\n",ret,pid,
25 (statu>>8)&0xff);
26 }
27 if(WIFEXITED(statu)){
28 printf("the child exit code:%d\n",WEXITSTATUS(statu));
29 }
30 if(WIFSIGNALED(statu)){
31 printf("the exit signal:%d\n",WTERMSIG(statu));
32 }
33 while(1){
34 printf("-------\n");
35 sleep(1);
36 }
37 return 0;
38 }
3.进程终止
终止场景:
正常退出,结果符合预期
正常退出,结果不符合预期
异常退出
终止方式: 二者的区别要分清!
main函数中return
exit()是一个库函数,在退出前会刷新缓冲区,做退出的收尾工作
_exit()是一个系统调用接口,直接退出,释放资源
返回值:
echo $? 可以查看进程返回值
错误编号:每个系统调用执行完毕后都会重置进程中errno这么一个全局变量,这个全局变量
中存储的就是档次调用的系统调用接口错误编号,当系统调用接口出错用户就可以通过这个errno获取系统调用的错误原因。
void perror(const char* s);
char* strerror(int errnum);
xshell6中exit()函数代码实现部分:
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<unistd.h>
4 #include<string.h>
5 #include<errno.h>
6
7 int main()
8 {
9 int pid = fork();
10 if(pid<0){
11 perror("fork error");
12 printf("fork error %s\n",strerror(errno));
13 }
14 printf("-------");
15 int i=0;
16 for(;i<256;i++)
17 {
18 printf("%s\n",strerror(i));
19 }
20 sleep(3);
21 _exit(255);
22 exit(0);
23 return 0;
24 }