• LinuxC创建回收进程fork、exec、wait、waitpid函数的理解


    1. fork 

      int pid = fork();
      if (pid == -1 ) {//返回-1,说明fork失败
        perror("fork");
        exit(1);
      } else if (pid > 0) {//返回子进程pid,说明是父进程

      } else if (pid == 0) {//返回0,说明是子进程
      }

      fork出来的子进程和父进程相同的是:全局变量、.data、.text、堆、栈、环境变量、工作目录、宿主目录、信号处理方式等

      不同的是:进程id,父进程id,定时器,未决信号集

      1-1)父子进程共享文件描述符:

        int test_share_fd() {
          int fd = open("test_share_fd.txt", O_CREAT | O_RDWR, 0777);//在fork之前open的文件,共享fd以及文件指针offset
          if (fd == -1)
          {
            perror("open file error");
            exit(EXIT_FAILURE);
          }
          int pid = fork();//在fork之后open的文件,共享fd,但不共享文件指针offset
          if (pid == -1)
          {
            close(fd);
            perror("fork error");
            exit(EXIT_FAILURE);
          }
          if (pid == 0)
          {//child
            char* p = "hello,share fd from child";
            write(fd, p, strlen(p));
            close(fd);
          }
          else {//parent
            wait(NULL);
            char buf[512];
            lseek(fd, 0, SEEK_SET);
            int size = read(fd, buf, sizeof(buf));
            buf[size] = 0;
            printf("fd from parent:%s ", buf);
            close(fd);
          }
          return 0;
        }

      1-2)父子进程共享内存mmap

        int test_share_mmap() {
          int len = 100;
          char* filename = "test_share_mmap_temp";
          int fd = open(filename, O_CREAT | O_RDWR, 0777);
          if (fd == -1)
          {
            perror("open file error");
            exit(EXIT_SUCCESS);
          }
          ftruncate(fd, len);
          char* p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
          if (p == MAP_FAILED)
          {
            perror("mmap error");
            exit(EXIT_FAILURE);
          }
          int pid = fork();
          if (pid == -1)
          {
            munmap(p, len);
            perror("fork error");
            exit(EXIT_FAILURE);
          }
          if (pid == 0)
          {
            strcpy(p, "hello,share mmap from child");
          }
          else {
            wait(NULL);
            printf("mmap from parent:%s ", p);
          }
          munmap(p, len);
          unlink(filename);
          return 0;
        }

    2. exec

        execl,execle用于执行一个可执行文件的,比如test.exe文件
        execlp用于执行一个命令行指令的,比如ls -l

        int execl(const char *path, const char *arg, ... , NULL);//按路径查找,执行一个文件
          //execl("/bin/ls", "ls", "-l", "-a", NULL);
          //char* path = "/home/ubuntu/app/test.exe"; execl(path, path, "argv1", "argv2", NULL);

        int execle(const char *path, const char *arg, ... , NULL, char * const envp[] */);//同execle
          //char* _path = "/home/ubuntu/app/test.exe";
          //char* _env[] = { "k1=v1","k2=v2",NULL };使用自定义env,不使用默认的extern char** environ;
          //execle(_path, _path, "-l", "-a", NULL, _env);

        int execlp(const char *file, const char *arg, ... , NULL);//按PATH查找,执行一个命令
        //execlp("ls", "ls", "-l", "-a", NULL);

        以下三个exec函数和上面三个类似,上面三个是可变参数传参,以下三个使用数组传参。但是,不管哪种都是需要NULL作为结束符的。

        int execv(const char *path, char *const argv[]);
        int execvp(const char *file, char *const argv[]);
        int execvpe(const char *file, char *const argv[],char *const envp[]);

       上面六个exec都是linux c函数库的标准函数,在man手册第三章,man 3 execle 即可查看。它们最终都会调用execve这个系统函数,execve函数在man手册第二章。

    3
    . wait,waitpid
      孤儿进程:父进程已死,子进程还在,此时子进程就是孤儿进程。子进程的父进程变为init进程,俗称进孤儿院。
      僵死进程:父进程还在,子进程已死而且没被回收,这个已死的子进程就是僵死进程。(注意:前提是父进程还在,如果父进程都死了,父子进程都会被回收,就不存在僵死进程了)
      僵死进程如何回收?父进程通过wait,waitpid函数回收呀。
      pid_t wait(int &status);//阻塞等待子进程退出,回收子进程pcb等残留资源,获取子进程退出状态码
        status为传出参数,子进程的退出状态码,该方法堵塞等待。   pid_t
    waitpid(pid_t pid, int &status, int options);
        pid>0:回收指定的子进程,比如pid=1000,表示回收进程id为1000的那个子进程(进程id)
        pid=0:回收和调用waitpid同一进程组的任一子进程
        pid=-1:回收任一进程组的任一子进程,等价于wait函数
        pid<-1:回收指定进程组的任一子进程,比如pid=-1000,表示回收进程组id为1000的任一子进程(进程组id)

      status状态码说明:
        if (WIFEXITED(status))//正常退出     {       printf("child exit normal,exit code:%d ", WEXITSTATUS(status));//正常退出状态码     }     else if (WIFSIGNALED(status))//异常退出     {       printf("child term error,term code:%d ", WTERMSIG(status));//导致异常退出的状态码     }     else if (WIFCONTINUED(status))//从挂起状态继续运行     {       printf("child continue running");     }     else if (WIFSTOPPED(status))//从运行状态挂起暂停     {       printf("child stop running,stop code:%d ", WSTOPSIG(status));//导致暂停的状态码     }
      
      options有三个可选值:WCONTINUED,WNOHANG,WUNTRACED
      wait是堵塞方法,waitpid可以设置为非堵塞。当options为WNOHANG时为非堵塞,此时waipid返回0表示还没有可回收的子进程,大于0表示回收的子进程。
      一次wait或者waitpid调用只能回收一个子进程,所以一般使用do{}while()循环调用。

  • 相关阅读:
    Java三大特殊类
    静态顺序表and动态顺序表(一)_插入操作
    模拟实现memcpy、memmove函数
    模拟实现strcpy函数
    模拟实现Strlen函数
    数组相关知识总结(一)
    C语言学习总结(二)__操作符
    受控组件 & 非受控组件
    SyntheticEvent
    ReactDOM & DOM Elements
  • 原文地址:https://www.cnblogs.com/yongfengnice/p/11874335.html
Copyright © 2020-2023  润新知