zoukankan      html  css  js  c++  java
  • waitpid 中 的最后一个参数使用 WNOHANG && read和recv函数区别

    一、wait()和waitpid()的作用

    首先内核会释放终止进程(调用了exit系统调用)所使用的所有存储区,关闭所有打开的文件等,但内核为每一个终止子进程保存了一定量的信息。这些信息至少包括进程ID,进程的终止状态,以及该进程使用的CPU时间,所以当终止子进程的父进程调用wait或waitpid时就可以得到这些信息。

    二、僵尸进程的作用

    一个进程执行了exit系统调用退出,而其父进程并没有为它收尸(调用wait或waitpid来获得它的结束状态)的进程。
    任何一个子进程(init除外)在exit后并非马上就消失,而是留下一个称外僵尸进程的数据结构,等待父进程处理。这是每个子进程都必需经历的阶段。另外子进程退出的时候会向其父进程发送一个SIGCHLD信号。

    三、使用wait()和waitpid()处理僵尸进程三种方式

    1、通过signal(SIGCHLD, SIG_IGN)通知内核对子进程的结束不关心,由内核回收。
    2、父进程调用wait/waitpid等函数等待子进程结束,如果尚无子进程退出wait会导致父进程阻塞。waitpid可以通过传递WNOHANG使父进程不阻塞立即返回。

    3、如果父进程很忙可以用signal注册信号处理函数,在信号处理函数调用wait/waitpid等待子进程退出。
    通过两次调用fork。父进程首先调用fork创建一个子进程然后waitpid等待子进程退出,子进程再fork一个孙进程后退出。这样子进程退出后会被父进程等待回收,而对于孙子进程其父进程已经退出所以孙进程成为一个孤儿进程,孤儿进程由init进程接管,孙进程结束后,init会等待回收。

    #include <stdio.h>
    #include <unistd.h>
    #include <signal.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    void wait4children(int signo) {
      int status;
      wait(&status);
    }
    int main() {
      int i;
      pid_t pid;
      signal(SIGCHLD, wait4children);
      for(i=0; i<100; i++) {
        pid = fork();
        if(pid == 0)
          break;
      }
      if(pid>0) {
        printf("press Enter to exit...");
        getchar();
      }
      return 0;
    }

    但是通过运行程序发现还是会有僵尸进程,而且每次僵尸进程的数量都不定。这是为什么呢?其实主要是因为Linux的信号机制是不排队的,假如在某一时间段多个子进程退出后都会发出SIGCHLD信号,但父进程来不及一个一个地响应,所以最后父进程实际上只执行了一次信号处理函数。但执行一次信号处理函数只等待一个子进程退出,所以最后会有一些子进程依然是僵尸进程。
    虽然这样但是有一点是明了的,就是收到SIGCHLD必然有子进程退出,而我们可以在信号处理函数里循环调用waitpid函数来等待所有的退出的子进程。至于为什么不用wait,主要原因是在wait在清理完所有僵尸进程后再次等待会阻塞。

    #include <stdio.h>
    #include <unistd.h>
    #include <signal.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    void wait4children(int signo) {
      int status;
      while(waitpid(-1, &status, WNOHANG) > 0);
    }
    int main() {
      int i;
      pid_t pid;
      signal(SIGCHLD, wait4children);
      for(i=0; i<100; i++) {
        pid = fork();
        if(pid == 0)
          break;
      }
      if(pid>0) {
        printf("press Enter to exit...");
        getchar();
      }
      return 0;
    }

    四、waitpid使用WNOHANG的作用

    在父进程不打算阻塞等待子进程返回时,可以这样使用。父进程可以定期轮询子进程的状态。

    具体实现:

    while(waitpid(-1, &status, WNOHANG) > 0);

    waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结
    束。
    WNOHANG 如果没有任何已经结束的子进程则马上返回,不予以等
    待。
    使用while来轮询

    五、信号处理函数

    信号处理函数必须是可重入函数。

    不可重入函数有以下特点:
    ①使用了静态数据结构,那么不可重入,因此继续执行时,结构可能被修改

    ②调用了malloc函数。free函数,因为会访问共享数据结构

    ③使用了标准IO函数,标准IO函数会维护共享的缓冲区

    malloc和free函数是一个线程安全函数,调用时将陷入内核,内部维护一个空闲区链表来满足堆内存的申请获取和释放回收。因此malloc函数是不可重入的,因为其在执行过程中如果被信号打断,之后继续执行时,可能内部的空闲区链表已经发生变化。

    六、read和recv函数区别

    recv和send函数提供了和read和write差不多的功能.但是他们提供了第四个参数来控制读写操作。
    int recv(int sockfd,void *buf,int len,int flags)
    int send(int sockfd,void *buf,int len,int flags)(不太常使用)

    recv函数主要用的flags选项就是MSG_PEEK

    MSG_PEEK:是recv函数的使用标志,表示只是从系统缓冲区中读取内容,而不清除系统缓冲区的内容。这样下次读的时候,仍然是相同的内容。一般在有多个进程读写数据时能够使用这个标志。

    参考链接:https://www.cnblogs.com/lancidie/archive/2013/04/12/3016413.html

    参考链接:https://blog.csdn.net/qq_33369979/article/details/110674929

    参考链接:https://blog.csdn.net/ChenAiZiTeng/article/details/25611941

  • 相关阅读:
    Qt 数据库篇
    js字符串函数(转)
    如何解决IE无法识别html5中的新标签(article、abbr、header等)
    web多页打印问题
    诡异的Spinner级联样式
    discuz x2用户删除了,帖子不能用了,恢复帖子的办法
    创业公司如何招聘优秀工程师
    清除目录下的SVN信息
    .NET 项目SVN 全局排除设置
    编程技术面试的五大要点
  • 原文地址:https://www.cnblogs.com/kongbursi-2292702937/p/14868135.html
Copyright © 2011-2022 走看看