缠丝猫 发表于 2024-6-14 23:09:46

【Linux】详解进程终止&&进程等候

一、页表&&写时拷贝的进一步明白

        页表中不仅仅只有虚拟所在到物理所在的映射,还包括了很多选项,此中就包括了映射条目的权限。当我们进程的代码和数据加载到内存并和进程所在空间建立映射关系时,如果数据的内容不答应被修改(好比说常量字符串),对应数据在页表中的映射条目的权限就会被设置为'r',表示该数据是只读的,不能被修改。这就是为什么当我们要对常量字符串的内容做修改程序运行阶段会报错的底层缘故起因。
        通过页表的权限位,我们也可以很好地解释写时拷贝是如何做到的。当父进程创建子进程时会发生写时拷贝,写时拷贝会把大部分映射条目的权限都由‘rw’设置成‘r’。当子进程或者是父进程要对数据举行修改时,发现要修改数据对应的映射条目的权限位‘r’,无法举行修改,这时操作体系就会介入。操作体系发现子进程(假设是子进程要对数据举行修改)要对数据举行修改,且操作时正当的,这是就会在内存中申请一块空间重新建立映射关系,再将父子进程该对应的映射条目都改成‘rw’,这才算完成了一次写时拷贝。通过以上过程操作体系就可以按需举行写时拷贝。
二、进程终止

        main函数的返回值我们叫做进程的退出码。一般0表示进程执行成功,非0表示进程执行失败。可以用非0的数字表示进程失败的缘故起因。错误码可以转换成错误描述,可以使用语言和体系自带的方法举行转化,也可以自界说。其他函数错误码仅仅表示函数调用结束。
2.1、echo $?

        bash进程会记录最近一个进程退出的退出码,可以查看echo $?可以查看最近一个进程退出的退出码。
        调用函数我们通常想看到两种结果,一是函数的执行结果(好比说fopen打开文件,打开成功就返回文件指针,打开失败返回NULL,这叫函数的执行结果),二是函数的执行情况(好比说同样是fopen打开文件,我们函数的执行情况对应的数字会被生存在errno(错误码)这个变量中)。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main()
{
    FILE* fp = fopen("./log.txt", "r");
    printf("%d %s\n", errno, strerror(errno));
    return 0;
}
https://img-blog.csdnimg.cn/direct/c91d7e46fcd24beeb456c7889c2b88ba.png
 错误码和main函数的退出码本质上是一样的。 错误码只会记录体系提供的函数的执行情况。
2.2、进程退出的场景

进程提出的场景与三种:
   1、进程代码执行完了,结果是正确的。
2、进程代码执行完了,结果不正确。
3、进程代码没有执行完,进程出异常了。进程出异常时,进程的退出码是没故意义的。
         第三种情况进程出异常是进程收到了操作体系发出的异常信号,每个信号都有差别的编号,差别的信号编号表示异常的缘故起因。
https://img-blog.csdnimg.cn/direct/ad75de667384471b82d40b941f932853.png
        任何进程最终的执行情况我们都可以用两个数字来表示。一个是进程的退出信号(exit_signal)(退出信号为0表示进程没有出异常),一个是进程的退出码(exit_code)。进程的退出信号是由操作体系发送给进程,以指示进程应该终止或举行某种操作的信号,是上对下的操作。而进程的退出码则是当进程结束运行时返回给操作体系的一个整数值,用于表示进程的执行状态或结果,是下对上返回的结果。
2.3、exit

        exit就是用来终止进程的,exit括号中的内容就是进程的退出码。在我们的代码进程中,在任意地方调用exit都表示进程退出。
三、进程等候

3.1、进程等候的须要性

   1、之前讲过,子进程退出,父进程如果不管掉臂,就可能造成‘僵尸进程’的题目,进而造成内存走漏。

2、另外,进程一旦变成僵尸状态,那就刀枪不入,kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程。

3、最后,父进程派给子进程的使命完成的如何,我们须要知道。如子进程运行完成,结果对还是不对,或者是否正常退出。

4、父进程通过进程等候(wait)的方式,回收子进程资源,获取子进程退出信息
 3.2、进程等候的方法

3.2.1、wait方法

https://img-blog.csdnimg.cn/direct/42623b3f811a44658edc149a1e5de404.png
        wait方法里的参数为输出型参数,可以设置为NULL。调用wait函数父进程默认举行阻塞等候,会等候任意一个子进程退出。等候成功,wait会返回子进程的pid,等候失败返回小于0的值。 
        fork之后父子进程谁先运行不确定,但fork之后肯定是父进程后退出,因为父进程要回收子进程。
3.2.2、waitpid方法

https://img-blog.csdnimg.cn/direct/873ca3176a40453bb6bd3fb22252de1a.png
        以上pid参数为要回收子进程的pid(pid如果为-1,表示等候任意一个子进程,与wait等效),wstatus参数同样为输出型参数,可以设置为NULL(也可以设置为一个int变量的所在,可以查看子进程的退出码), options参数设置为0表示阻塞等候,设置为宏 WNOHANG表示非阻塞等候。接纳非阻塞的方法等候,子进程退出成功返回子进程的pid,子进程还在继续自己的工作返回0,子进程出错返回小于0的数。阻塞等候时父进程会阻塞在waitpid这里不停等候子进程返回,非阻塞等候接纳轮询的方法查看子进程的退出信息,在轮询的间隙父进程可以继续做别的工作。
3.2.3、wstatus参数详解

        *wstatus表示一个int整形变量,由三十二个比特位组成,此中前16个比特位我们不用,第17到第24个比特位用来表示进程退出时的退出码,第26到第32个比特位用来表示进程退出时收到的退出信号。
https://img-blog.csdnimg.cn/direct/709c4e25ee1b4d75a8ea241c9c44cb01.png
https://img-blog.csdnimg.cn/direct/8b43279cc2784b78887d6efcf4f846f4.png
#include <stdio.h>
#include <stdlib.h>
#include <unistd.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 success, rid: %d, status: %d\n", rid, status);
    }

    return 0;
}  https://img-blog.csdnimg.cn/direct/cbcc3d34395840b69fb882f222231860.png
        这就可以解释为什么上面的status变量为256了。因为子进程的退出码为1,status的第24个比特位被设置为1,没有收到退出信号,以是status后8个比特位都为0,以是status便是2的8次方便是256。 
3.2.4、使用位操作从status变量中提取出进程的退出信号和退出码。

int main()
{
    pid_t id = fork();
    if(id == 0)
    {

      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 success, rid: %d, status: %d, exit_signal: %d, exit_code: %d\n", rid, status, status&0x7f, (status>>8)&0xff);
    }

    return 0;
}
https://img-blog.csdnimg.cn/direct/a3c70934cdae43f1a910d5c729a3afcb.png
 3.2.5、使用宏获取进程的退出码

        一般用户不是很关心进程的退出状态只想获取子进程的退出码就可以使用这种方法。
   WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)。
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)。
int main()
{
    pid_t id = fork();
    if(id == 0)
    {

      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(WIFEXITED(status))
    {

       printf("wait success, rid: %d, status: %d, exit_code: %d\n", rid, status,WEXITSTATUS(status));
    }


    return 0;
} https://img-blog.csdnimg.cn/direct/ed49d5bf995c4a729d7872ebe241f7f9.png


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【Linux】详解进程终止&&进程等候