zoukankan      html  css  js  c++  java
  • 创建子进程

    fork()函数


     如上图所示,调用fork()函数将产生一个子进程(程序与父进程完全一致),其中父进程继续执行,子进程将在fork()的下一行开始执行,当然fork()函数的返回值会被接收,需要用于区分子进程和父进程(两者返回值不同,一个大于0,一个等于0)。fork()函数说明如下所示:

    fork()
         函数功能:
                  产生一个子进程
         头 文 件:
                  #include <sys/types.h>
                  #include <unistd.h>
         函数原型:
                  pid_t fork(void);
         参数分析:
                  无
         返 回 值:
                  成功:返回 0 或者大于0的子进程号
                  失败: -1 错误号码会被设置
    

    注意

     虽然子进程是父进程的一份拷贝,但是有些属性并不相同,以下是相同的地方:
      1.UID和GID;
      2.所有环境变量;
      3.进程组ID和会话ID;
      4.当前工作路径,除非用chdir()修改过了;
      5.打开的文件;
      6.信号响应函数;
      7.整个内存空间,包括栈、堆、数据段、代码段、标准IO的缓冲区等等。
     以下属性是不同的:
      1.进程号PID;
      2.记录锁。父进程对某文件加了把锁,子进程不会继承这把锁;
      3.挂起的信号。这些信号是所谓的“悬而未决”的信号,等待着进程的响应,子进程也不会继承这些信号。

    fork函数实验程序

    #include <stdio.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int main(int argc, char const *argv[])
    {
        pid_t id = fork();//执行该语句后,会创建一个进程,相当于这个程序有两个进程了
    
        if (id > 0)//执行父进程的时候调用这个,ID为进程的ID号码
        {
            printf("我是父进程,我的PID是:%d 	 子进程的PID是:%d!!!
    ", getpid(), id);
        }
        else if (0 == id)//执行子进程的时候调用这个,子进程的ID号码为0
        {
            printf("我是子进程,我的PID是:%d 	 父进程的PID是:%d!!!
    ", getpid(), getppid());
        }
        else if (id < 0)
        {
            printf("失败了!!
    ");
        }
        
        return 0;
    }
    

    输出结果:

    exec函数簇

     在程序中调用了exec函数簇函数后,运行程序时调用此函数的代码会被替换,exec函数簇有六种函数,以不同的方式指定二进制文件来代替其中的代码,都是以exec开头的。

    int execl(const char *path, const char *arg, ...);
    int execlp(const char *file, const char *arg, ...);
    int execle(const char *path, const char *arg, ..., char * const envp[]);
    int execv(const char *path, char *const argv[]);
    int execvp(const char *file, char *const argv[]);
    int execve(const char *path, char *const argv[], char *const envp[]);
    

    注意

      1.函数名后缀为l的,意味着其参数以列表(list)的方式提供;
      2.函数名后缀为v的,意味着其参数接受二维数组,最后一个参数以NULL作为结束;
      3.函数名后缀为p的,意味着以环境变量来查找指定的可执行二进制文件;
      4.函数名后缀为e的,意味着可以增加环境变量。

    exec函数簇实验程序

    execl("/bin/ls", "ls", "-al", "/dev", NULL); //指定二进制文件路径执行ls -al /dev,注意最后一个参数应该以NULL结尾
    
    execlp("ls", "ls", "-al", "/dev", NULL); //默认PATH环境变量执行ls -al /dev
    execle("/home/test","test","hello","world",NULL,"PATH=/usr/bin",NULL);//指定二进制文件路径执行test hello world 并且增加了环境变量
    
    char *buf[] = {"hello world", "元旦快乐", NULL};
    execv("/home/arno/test", buf);
    

    退出进程

    函数exit(status)和_exit(status)

     当某个函数直接调用了exit(status)或_exit(status)时,将直接结束此进程,status用于记录进程死亡的原因。如果当主函数执行完或者是调用了return时,后续还会调用了一个exit函数,如下所示,该函数是一个系统调用,他主要用于回收僵尸进程的一些数据,最后该函数将一去不复返。

    xxxx
    {
          ....
          ret_val = main(argc, argv);
          exit(result);
    }
    

    注意:exit()和_exit()存在些许差别,如下:

      1.如果子进程正常退出,则status一般为 0。
      2.如果子进程异常退出,则statuc一般为非 0。
      3.exit()退出时,会自动冲洗(flush)标准 IO 总残留的数据到内核,如果进程注册了“退出处理函数”还会自动执行这些函数。而_exit()会直接退出,什么都不处理。

    函数wait和waitpid()

    wait()
         函数功能:
                  父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出
         头 文 件:
                  #include <sys/wait.h>
         函数原型:
                  pid_t wait(int *stat_loc);
         参数分析:
                  stat_loc:分析子进程退出状态,也就是子进程执行exit(status),中status的值。
         返 回 值:
                  成功: 退出的子进程PID
                  失败: -1 
    
    waitpid()
         函数功能:
                  父进程一旦调用了waitpid就立即阻塞自己,等待进程组为pid的子进程
         头 文 件:
                  #include <sys/wait.h>
         函数原型:
                  pid_t waitpid(pid_t pid, int *stat_loc, int options);
         参数分析:
                  pid:小于-1:等待组 ID 的绝对值为 pid 的进程组中的任一子进程;
                       -1:等待任一子进程;
                       0:等待调用者所在进程组中的任一子进程;
                       大于 0:等待进程组 ID 为 pid 的子进程。
               option:WCONTINUED:报告任一从暂停态出来且从未报告过的子进程的状态;
                       WNOHANG:非阻塞等待;
                       WUNTRACED:报告任一当前处于暂停态且从未报告过的子进程的状态
         返 回 值:
                  成功: 状态发生改变的子进程PID,当选项options为WNOHANG且执行成功时将返回0
                  失败: -1 
        
    

    常用处理子进程退出状态的宏

    含义
    WIFEXITED(status) 如果子进程正常退出,则该宏为真
    WEXITSTATUS(status) 如果子进程正常退出,则该宏将获取子进程的退出值
    WIFSIGNALED(status) 如果子进程被信号杀死,则该宏为真
    WTERMSIG(status) 如果子进程被信号杀死,则该宏将获取导致他死亡的信号值
    WIFSTOPPED(status) 如果子进程的被信号暂停,且option中WUNTRACED已经被设置时,则该宏为真
    WSTOPSIG(status) 如果WIFSTOPPED为真,则该宏将获取导致子进程暂停的信号值
    WIFCONTINUED(status) 如果子进程被信号SIGCONT重新置为就绪态,该宏为真

    测试程序

    wait.c

    #include <sys/types.h>
    #include <unistd.h>
    #include <sys/wait.h>    
    #include <stdio.h>
    
    int main(int argc, char const *argv[])
    {
        int ret = fork();
        if (ret > 0)
        {
           int status;
           wait(&status);
           if (WIFEXITED(status))
           {
               printf("子进程正常退出
    ");
           }
           if (WIFSIGNALED(status) )
           {
               printf("子进程被信号杀死了
    ");
           }
        }
        else if (ret == 0)
        {
             execl("./exit", "exit", "NULL");
        }
        return 0;
    }
    

    exit.c,被wait.c调用的程序

    #include <stdio.h>
    #include <sys/wait.h>
    #include <stdlib.h>
    
    int main(int argc, char const *argv[])
    {
        printf("Hello, this is child
    ");
    
    #ifdef ABORT
        abort();
    #else
        exit(100);
    #endif
        return 0;
    }
    

    输出结果:

  • 相关阅读:
    git add . 的时候遇到warning: LF will be replaced by CRLF inXXX 解决办法
    用Merge存储引擎中间件实现MySQL分表
    隐型马尔科夫模型(HMM) 简介
    隐型马尔科夫模型(HMM)向前算法实例讲解(暴力求解+代码实现)---盒子模型
    数据输入——生成你需要的echart图(世界地图,气泡图)
    数据输入——生成你需要的echart图(堆积柱状图、扇形图、嵌套环形图)
    jython实现java运行python代码
    django第四课 标签的用法(if/else、for、ifequal、过滤器、注释等)
    django第三课 模版
    paddlepaddle使用(一)
  • 原文地址:https://www.cnblogs.com/ding-ding-light/p/14238578.html
Copyright © 2011-2022 走看看