自己做的网站搜索不到,wordpress编辑器位置,江西龙峰建设集团的网站,图片素材网站免费大推荐代码运行完毕#xff0c;结果正确代码运行完毕#xff0c;结果不正确代码异常终止#xff08;被信号终止了#xff09;而进程执行的结果状态#xff0c;可以用两个数字表示出来#xff0c;即退出码和终止信号#xff1a;int exit_code, int exit_signal#xff1a;当代…代码运行完毕结果正确代码运行完毕结果不正确代码异常终止被信号终止了而进程执行的结果状态可以用两个数字表示出来即退出码和终止信号int exit_code, int exit_signal当代码运行完毕且结果正确时终止信号为0退出码为0当代码运行完毕且结果不正确终止信号为0退出码不为0当代码异常终止时终止信号不为0退出码无意义这两个数字不用由我们维护OS会把进程退出的详细信息写到进程的task_struct中在这里插入图片描述所以进程需要僵尸状态维持自己的退出状态话说回来为什么自此开始学习编程后main函数总是要return 0这就是因为main函数的返回值就是这个进程的退出码而一般规定退出码0代表进程结果正确当一个进程正常终止后在Linux系统中我们可以用命令echo $?查看上一个进程的退出码在这里插入图片描述可以使用strerror函数获取退出码对应的描述在这里插入图片描述2. 进程退出的方法进程常见的退出方法有代码执行完毕正常终止的方法main函数return使用exit函数或_exit系统调用代码执行异常终止方法ctrl c、发送信号终止exit是一个C库函数作用是使当前进程终止参数是想要返回的退出码在这里插入图片描述_exit是一个系统调用它和exit的唯一区别是exit终止会强制刷新缓冲区但_exit不会。而本质上exit的实现也是封装了_exit。这代表刷新缓冲区的操作一定不是系统内核中的而是由C/C维护的在这里插入图片描述写一段代码验证一下代码语言javascriptAI代码解释#includestdio.h #includestdlib.h void test() { exit(1); } int main() { test(); return 0; }在这里插入图片描述在非main函数中要注意return和exit的使用区别——return仅退出当前子函数、向函数调用处返回值、程序继续执行exit 直接终止整个进程、程序彻底停止二、进程等待之前已经讲过子进程退出父进程如果不管就会导致子进程一直处于僵尸状态而造成内存泄漏除此之外父进程还可能需要知道子进程的任务完成如何也就是需要获取子进程的退出信息。所以父进程需要通过进程等待的方式回收子进程资源获取子进程退出信息1. 进程等待的方法实现进程等待主要依靠系统调用wait和waitpid在这里插入图片描述wait可以等待任意一个子进程且是阻塞等待。它的返回值是如果等待成功返回等待的子进程pid等待失败则返回-1。至于它的参数是用于获取子进程退出信息的下面讲waitpid的功能比wait更丰富返回值规则与wait类似它有三个参数参数pid_t pid表示等待特定的子进程pid。若传-1则表示等待任意一个子进程。如果调用中出错如传的pid不是自己子进程的pidwaitpid返回-1程序的error会被设置成相应的错误码。参数int options传0表示阻塞等待这是默认情况传WNOHANG一个宏表示非阻塞等待。阻塞等待时父进程不会继续执行代码直到等到了子进程退出非阻塞等待时若指定pid的子进程还没有退出waitpid返回0不予等待。可以看出如果waitpid第一个参数传-1第三个参数传0效果就和wait一样。wait的参数和waitpid第二个参数int* status是要传一个int变量的地址这个变量用于接收子进程的退出信息若不想获取子进程退出信息则可以传NULL。写一个程序验证一下代码语言javascriptAI代码解释#includestdio.h #includeunistd.h #includestdlib.h #includesys/types.h #includesys/wait.h int main() { pid_t id fork(); if(id 0) { printf(我是子进程, pid是%d, 终止信号是%d, 退出码是%d\n, getpid(), 0, 1); exit(1); } else { int status 0; pid_t retpid waitpid(id, status, 0); // 子进程退出前父进程一直阻塞在这里 printf(我是父进程, 子进程%d已回收, status是%d\n, retpid, status); } return 0; }在这里插入图片描述其他地方没有问题可是status是256怎么来的其实status不能当做简单的int看待而是一个位图status有32个比特位只用后16个比特位记录信息。在后16个比特位中低8位表示子进程的终止信号高8位表示子进程的退出码所以在上面的例子中子进程终止信号为0即00000000子进程退出码为1即00000001。那么status的32比特位为00000000000000000000前16位不用00000001退出码00000000终止信号转为十进制就是256换句话说想从status中得到子进程的具体信息还需要这样位运算终止信号 status 0x7F退出码 (status 8) 0x7F但其实并不需要我们手动运算系统中已经为我们提供了相应的宏函数WIFEXITED(status)如果是正常终止的子进程返回真WEXITSTATUS(status)如果WIFEXITED(status)是真返回子进程退出码试验一下代码语言javascriptAI代码解释#includestdio.h #includeunistd.h #includestdlib.h #includesys/types.h #includesys/wait.h int main() { pid_t id fork(); if(id 0) { printf(我是子进程, pid是%d, 终止信号是%d, 退出码是%d\n, getpid(), 0, 10); exit(10); } else { int status 0; pid_t retpid waitpid(id, status, 0); // 子进程退出前父进程一直阻塞在这里 printf(我是父进程, 子进程%d已回收, status是%d\n, retpid, status); printf(子进程是否正常终止:%d\n, WIFEXITED(status)); printf(子进程退出码%d\n, WEXITSTATUS(status)); } return 0; }在这里插入图片描述没有问题三、进程替换fork之后父子进程各自执行当前程序的代码的一部分如果我们想让子进程执行一个全新的程序怎么办呢进程的程序替换来满足这个需求进程替换是指通过特定的接口加载磁盘上的一个全新程序加载到调用进程的地址空间中1. 程序替换的方法实现进程替换主要依靠exec系列库函数在这里插入图片描述这六个函数的功能都是替换一个程序但是参数上有所区别观察发现它们的参数总共是这几种const char* path代表要传一个路径名字符串可以是相对路径或绝对路径如/usr/bin/lsconst char* arg, ...代表要传若干个字符串最后一个必须是NULL。你想替换的程序在命令行中怎么执行这里就怎么传如ls, -a, -l, NULLconst char* file代表要传一个文件名字符串不用写路径程序会从环境变量PATH中的寻找这个文件。如lschar* const envp[]代表要传一张环境变量表这套环境变量会被新程序继承使用覆盖原来的环境变量数组元素也需要以NULL结尾如果不想覆盖原环境变量表则这个参数可以传environchar* const argv[]代表要传一张命令行参数表其实就是将上面的const char* arg, ...内容写进数组再传递数组元素也需要以NULL结尾。本质上程序的命令行参数都是通过父进程使用程序替换函数传递给子进程的不难想到exec系列函数通过上面不同的参数组合有了六种函数以应对不同的使用场景比如想使用int execlp(const char* file, const char* arg, ...)函数想把当前进程替换为执行ls。在当前进程中调用代码语言javascriptAI代码解释execlp(ls, ls, -a, -l, NULL); // 可以省略一个ls但是不建议因为还要额外记忆比如想使用int execv(const char* file, char* const argv[])函数想把当前进程替换为执行touch test.c。在当前进程中调用代码语言javascriptAI代码解释char* argv[] {touch, test.c, NULL}; execv(/usr/bin/touch, argv); // 或execv(argv[0], argv);这几个函数如果程序替换成功则没有返回值如果调用出错则返回-1。所以exec函数只有出错的返回值而没有成功的返回值。在之前的学习中我们fork创建子进程后子进程和父进程执行的还是同一个程序只是进入了不同的代码分支。程序替换没有创建新的进程而是直接覆盖原有程序继续执行。所以通常是父进程创建子进程后让子进程替换成别的程序。程序替换后原程序后面的代码就不再执行了。替换的本质是代码和数据拷贝到内存中。而只有OS有权做IO过程所以进程替换要依靠系统调用真正的系统调用函数是在这里插入图片描述上面说的exec系列函数底层都是调用它。我们来举个栗子完成一次程序替换代码语言javascriptAI代码解释#includestdio.h #includeunistd.h #includestdlib.h #includesys/types.h #includesys/wait.h int main() { pid_t id fork(); if(id 0) { // child execlp(ls, ls, -a, -l, NULL); //一般而言替换的新程序中正常执行完就会在它内部退出终止子进程不会执行这一句 //而如果代码执行到了这里就说明程序替换失败了 exit(1); } else { waitpid(id, NULL, 0); } return 0; }在这里插入图片描述结果符合预期子进程替换成了ls -a -l命令