fork()函数

java主进程创建子进程 主进程会等子进程结束吗 创建一个子进程_#include


 如上图所示,调用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 \t 子进程的PID是:%d!!!\n", getpid(), id);
    }
    else if (0 == id)//执行子进程的时候调用这个,子进程的ID号码为0
    {
        printf("我是子进程,我的PID是:%d \t 父进程的PID是:%d!!!\n", getpid(), getppid());
    }
    else if (id < 0)
    {
        printf("失败了!!\n");
    }
    
    return 0;
}

输出结果:

java主进程创建子进程 主进程会等子进程结束吗 创建一个子进程_父进程_02

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("子进程正常退出\n");
       }
       if (WIFSIGNALED(status) )
       {
           printf("子进程被信号杀死了\n");
       }
    }
    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\n");

#ifdef ABORT
    abort();
#else
    exit(100);
#endif
    return 0;
}

输出结果:

java主进程创建子进程 主进程会等子进程结束吗 创建一个子进程_#include_03