目录
一、进程停止
1、进程退出场景
2、进程常见退出方法
a. 正常停止(可以通过 echo $? 检察进程退出码):
b. 异常退出:
3、分类
a. main函数返回值(return 退出)
I. 退出码:
II. 错误码:
III. 异常信号:用 kill -l 检察
b. _exit 函数&&exit 函数
二、进程等待
1、为什么要进行进程等待?
2、进程等待的方法
a. wait && waitpid
b. 获取子进程status
编辑 3、阻塞等待&&非阻塞等待
a. 阻塞等待
b. 非阻塞等待
三、进程程序更换
1、更换原理
2、更换函数
execlp:
execv:
execvp:
execle,execve,execvpe:
总结:
一、进程停止
1、进程退出场景
1、代码运行完毕,结果正确 2、代码运行完毕,结果错误 3、代码异常停止
2、进程常见退出方法
a. 正常停止(可以通过 echo $? 检察进程退出码):
I. 从main返回 II. 调用 exit III. _exit
b. 异常退出:
ctrl + c,信号停止
3、分类
a. main函数返回值(return 退出)
main函数返回值,叫做进程的退出码,一般0,表示进程实行乐成,非0表示失败。
I. 退出码:
进程的退出码(exit_code),也称为退出状态或返回值,是一个整数,用于表示进程实行结束时的状态,通常由进程的主函数(C语言中是main函数)返回,或者是在进程碰到异常或者错误是由操作体系进行设置。可以利用 echo $? 下令检察近来一次进程退出的退出码信息。$? 是一个特殊的shell变量,保存了上一个下令的退出码。
II. 错误码:
进程的错误码( errno ) 用于表示进程在实行体系调用或库函数时碰到的错误情况,每个错误码都对应一个特定的错误情况,是的程序能够辨认并处置惩罚这些错误。而进程的退出码是一个整数,用于表示进程实行结束时的状态。
错误码errno通常是一组预界说的数值,每个值对应一个特定的错误情况。这些界说通常包含在体系的头文件<errno.h>中。当体系调用或库函数失败时,它们会设置全局变量errno为相应的错误码。
假如errno的值为0,则表示体系调用乐成,假如errno的值不为0,则表示体系调用失败,并且其值对应着特定的错误代码。常见处置惩罚errno的方法,如利用perror函数或者strerror函数,perror函数可以将errno的值映射为对应错误信息,并将其打印到标准错误流(stderr),而strerror函数则可以将errno的值转化为对应的错误字符串。
III. 异常信号:用 kill -l 检察
所以,任何进程最终的实行情况,我们都可以利用两个数字表明具体的实行情况
b. _exit 函数&&exit 函数
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
-
- int main()
- {
- while(1)
- {
- printf("I am a process: %d\n", getpid());
- sleep(1);
-
- exit(3); // exit 终止进程,status:进程退出时候,退出码
- }
- }
复制代码
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
-
- void Print()
- {
- printf("hello\n");
- exit(5);
- }
-
- int main()
- {
- while(1)
- {
- printf("I am a process: %d\n", getpid());
- sleep(1);
-
- Print();
- //exit(3); // exit 终止进程,status:进程退出时候,退出码
- }
- }
复制代码
这就告诉我们,exit就是用来停止进程的,exit(退出码)。
那么这个_exit又是什么呢?把上面的代码中的 exit 改成 _exit 验证一下,结果和exit是一样。岂非说exit 和 _exit 是一样的吗?它们到底有什么区别?
所以,可以得出,exit 会支持刷新缓冲区,而_exit不支持。
二、进程等待
1、为什么要进行进程等待?
2、进程等待的方法
a. wait && waitpid
对于waitpid中的参数:
status:
WIFEXITED(status): 若为正常停止子进程返回的状态,则为真(用于检察进程是否退出)。
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码(用于检察你进程的退出码)。
options:
WNOHANG: 若pid指定的子进程还没有结束,则waitpid()函数返回0,不予等待。若正常结束,则返回该子进程的ID。
对于waitpid中的返回值:
正常返回的时间waitpid返回网络到的子进程的进程ID;
假如设置了选项WNOHANG,而调用中的waitpid发现没有已退出的子进程可网络,则返回0;
假如调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误地点;
假如子进程已经退出,调用wait/waitpid时,wait/waitpid会立即返回,并且释放资源,获得子进程退出信息。
假如在任意时间调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
假如不存在该子进程,则立即出错返回。
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
-
- int main()
- {
- pid_t id = fork();
- if(id == 0)
- {
- //child
- int cnt = 5;
- while(cnt)
- {
- printf("Child is running, pid: %d, ppid: %d\n",getpid(),getppid());
- sleep(1);
- cnt--;
- }
- exit(1);
- }
- int status = 0;
- pid_t rid = waitpid(id, &status, 0);//阻塞等待
- if(rid > 0)
- {
- printf("wait sucess, rid: %d, status: %d\n", rid, status);
- }
- return 0;
- }
复制代码
等待乐成,但是我们发现这个status怎么是256呢?这256从哪来的?
b. 获取子进程status
wait 和 waitpid,都有一个status参数,该参数是一个输出型参数,由操作体系填充。
假如传递NULL,则表示不关心子进程的退出状态信息,否则,操作体系会根据该参数,将子进程的退出信息反馈给父进程。status不能简单的当作整型来对待,可以当作位图来对待。
可以用下图表示(只研究status低16比特位):
那么回到刚刚的题目,上面那个代码的256是怎么算的呢?
我们上面说过,任何进程最终实行情况,我们都可以利用两个数字表明具体实行情况。
3、阻塞等待&&非阻塞等待
阻塞和非阻塞指的是调用者(程序)在等待返回结果(或输入)时的状态。阻塞时,在调用返回结果前,当前线程会被挂起,并在得到结果之后返回。非阻塞时,假如不能立刻得到结果,则该调用者不会阻塞当进步程。
a. 阻塞等待
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
-
-
- int main()
- {
- //阻塞等待
- pid_t id = fork();
- if(id<0) return 1;
- else if(id == 0)
- {
- printf("I am child, pid: %d, ppid: %d\n",getpid(), getppid());
- sleep(3);
- exit(100);
- }
- else{
- int status = 0;
- pid_t rid = waitpid(id, &status, 0);
- if(rid > 0)
- {
- printf("wait sucess, rid: %d, status: %d, exitnum: %d, signo1: %d, signo2:: %d, isexit: %d\n",
- rid, status, status&0x7f, (status>>8)&0xff, WEXITSTATUS(status), WIFEXITED(status));
- }
- }
- return 0;
- }
复制代码 看这串代码中,status&0x7f,(status>>8)&0xff,这两个是什么啊?
b. 非阻塞等待
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- int main()
- {
- pid_t id= fork();
- if(id < 0) return 1;
- else if( id == 0)
- {
- printf("child is running..., pid is: %d\n",getpid());
- sleep(5);
- exit(10);
- }
- else
- {
- int status = 0;
- pid_t rid = 0;
- do
- {
- rid = waitpid(-1, &status, WNOHANG);//非阻塞等待
- if(rid == 0)
- {
- printf("child is running...\n");
- }
- sleep(1);
- }while(rid == 0);
- if(WIFEXITED(status) && rid == id)
- {
- printf("wait child 5s success, child return code is: %d\n",WEXITSTATUS(status));
- }
- else{
- printf("wait child failed\n");
- return 1;
- }
- }
复制代码
三、进程程序更换
1、更换原理
用fork创建子进程后实行的是和父进程雷同的程序(但有可能实行不同的代码分支),子进程往往要调用一种exec函数以实行另外一个程序。当进程调用一种exec函数时,该进程的用户空间和数据被新程序更换,重新程序的启动例程开始实行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。
下面我们来用一个最简单的exec的接口来表示一下更换原理:
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
-
-
- int main()
- {
- printf("I am a process, pid: %d\n",getpid());
- printf("exec begin...\n");
-
- execl("/usr/bin/ls", "ls", "-a", "-l", NULL);//注意这里是NULL,不是"NULL"
-
- printf("exec end ...\n");
- return 0;
- }
复制代码
这里代码末了的printf并没有被实行?为什么?
上面更换原理说了:当进程调用一种exec函数时,该进程的用户空间和数据被新程序更换,重新程序的启动例程开始实行。
2、更换函数
这些函数假如调用乐成则加载新的程序从启动代码开始实行,不再返回。假如调用出错则返回-1。
所以exec函数只有出错的返回值,而没有乐成的返回值。
那这么多我们该如何记住它们呢?---看命名
带p:PATH,你不用告诉体系,程序在哪里,只要告诉我名字是什么,体系更换的时间,会主动去PATH环境变量中查找。
execlp:
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
-
- int main()
- {
- printf("I am a process, pid: %d\n",getpid());
-
- pid_t id=fork();
- if(id == 0)
- {
- sleep(3);
- printf("exec begin...\n");
-
- // execl("/usr/bin/ls", "ls", "-a", "-l", NULL);//注意这里是NULL,不是"NULL"
- execlp("ls", "ls","-a","-l",NULL);
- printf("exec end...\n");
- exit(1);
- }
-
- pid_t rid= waitpid(id,NULL,0);
- if(rid>0)
- {
- printf("wait sucess\n");
- }
-
- exit(1);
- }
复制代码
带v:vector 数组 带l:list,列表,参数列表
execv:
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
-
- int main()
- {
- printf("I am a process, pid: %d\n",getpid());
-
- pid_t id=fork();
- if(id == 0)
- {
- char *const argv[] = {
- (char*)"ls",
- (char*)"-a",
- (char*)"-l",
- NULL
- };
- sleep(3);
- printf("exec begin...\n");
-
- execv("/usr/bin/ls", argv);
- printf("exec end...\n");
- exit(1);
- }
-
- pid_t rid= waitpid(id,NULL,0);
- if(rid>0)
- {
- printf("wait sucess\n");
- }
- exit(1);
- }
复制代码
execvp:
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
-
- int main()
- {
- printf("I am a process, pid: %d\n",getpid());
-
- pid_t id=fork();
- if(id == 0)
- {
- char *const argv[] = {
- (char*)"ls",
- (char*)"-a",
- (char*)"-l",
- NULL
- };
- sleep(3);
- printf("exec begin...\n");
- execvp("ls", argv);
- printf("exec end...\n");
- exit(1);
- }
-
- pid_t rid= waitpid(id,NULL,0);
- if(rid>0)
- {
- printf("wait sucess\n");
- }
- exit(1);
- }
复制代码
当然对于所有的更换函数,我们都不仅可以和上面一样实行体系的指令,也可以实行本身的程序。
- //test.c
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
-
- int main()
- {
- printf("I am a process, pid: %d\n",getpid());
-
- pid_t id=fork();
- if(id == 0)
- {
- char *const argv[] = {
- (char*)"ls",
- (char*)"-a",
- (char*)"-l",
- NULL
- };
- sleep(3);
- printf("exec begin...\n");
- execl("./mytest", "mytest", "-a", "-b", NULL);//注意这里是NULL,不是"NULL"
- printf("exec end...\n");
-
- exit(1);
- }
-
- pid_t rid= waitpid(id,NULL,0);
- if(rid>0)
- {
- printf("wait sucess\n");
- }
- exit(1);
- }
- //mytest.cc
- #include <iotream>
- using namespace std;
- int main()
- {
- cout<<"hello world"<<endl;
- cout<<"hello world"<<endl;
- cout<<"hello world"<<endl;
- return 0;
- }
复制代码 execle,execve,execvpe:
带e:本身维护环境变量 execle,execve,execvpe
先观察下面代码:
- //test.c
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- int main
- {
- printf("I am a process, pid: %d\n", getpid());
- pid_t id=fork();
- if(id == 0)
- {
- sleep(1);
- execl("./mytest","mytest",NULL);
- printf("exec end...\n");
- exit(1);
- }
- pid_t rid = waitpid(id, NULL, 0);
- if(rid > 0)
- {
- printf("wait sucess\n");
- }
- exit(1);
- }
- //mytest.cc
- #include <iostream>
- #include <unistd.h>
- using namespace std;
-
- int main()
- {
- for(int i = 0; environ[i]; i++)
- {
- printf("env[%d]: %s\n", i, environ[i]);
- }
- cout<<"hello world"<<endl;
-
- return 0;
- }
复制代码
这里我们传递环境变量表了吗?? ---没有,子进程默认就拿到了,它是怎么做到的?
默认可以通过地址空间继承的方式,让所有子进程拿到环境变量,进程更换不会更换环境变量数据
1、假如我们想让子进程继承全部的环境变量,直接能拿到
2、假如单纯的新增我们可以利用putenv
3、那么我想设置全新的环境变量给子进程呢?
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- int main()
- {
- char* const env[]={
- (char*)"one=1111111111111",
- (char*)"two=2222222222222",
- NULL
- };
-
-
-
- printf("I am a process, pid: %d\n",getpid());
-
- pid_t id=fork();
- if(id == 0)
- {
- sleep(1);
- execle("./mytest","mytest",NULL,env);
- printf("exec end...\n");
- exit(1);
- }
- pid_t rid= waitpid(id,NULL,0);
- if(rid>0)
- {
- printf("wait sucess\n");
- }
- exit(1);
- }
复制代码
这样我就设置了全新的环境变量给子进程!
总结:
究竟上,只有execve是真正的体系调用,其它五个函数最终都调用 execve,都是体系调用 execve的封装,所以execve在man手册的第2节,其它函数在man手册的第3节。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |