/********************************************************************/
fork 函数
/********************************************************************/
#include<sys/types.h> //提供类型 pid_t 的定义 #include<unistd.h> pid_t fork(void); /* 调用 fork 函数,其返回值赋给 result */ int result = fork(); if (result == -1) { perror("fork"); exit(-1); } else if (fork == 0) { //子进程相关语句 } else { //父进程相关语句 }
/********************************************************************/
exec 函数
/********************************************************************/
#include<unistd.h> int execl(const char *path, const char *arg,....); int execv(const char *path, char *const argv[]); int execle(const char *path, const char *arg,....,char *const envp[]); int execve(const char *path, char *const argv[], char *const envp[]); int execlp(const char *file, const char *arg,....); int execvp(const char *file, char *const argv[]);
例子:
1. int execlp(const char *file, const char *arg,....)
/* 在当前系统默认的环境变量 PATH 中查找可执行文件 */ if (fork() == 0) { //调用 execlp 函数,这里相当于调用了 “ls -l” 命令 if (execlp ("ls", "ls", "-l", NULL)<0) { perror("execlp error!"); } }
2. int execl(const char *path, const char *agr,...)
/* 使用 execl 函数时,需要给出完整的文件路径来查找对应的可执行文件 */ if (fork() == 0) { if (execl ("/bin/ls", "ls", "-l", NULL)<0) { perror("execl error!"); } }
3. int execle(const char *path, const char *arg,....,char *const envp[])
/* 使用 execle 时可以将环境变量添加到新建的子进程中去,这里先把环境变量构造成指针数组的方式来传递 */ // 命令参数列表必须以 NULL 结尾 char *envp[]={"PATH=/tmp","USER=sunq", NULL}; if (fork() == 0) { //调用 execle 函数,注意这里也要指出 env 的完整路径 if (execle("/bin/env", "env", NULL, envp)<0) { perror("execle error!"); } }
4. int execve(const char *path, char *const argv[], char *const envp[])
/* 使用 execve 函数时,通过构造指针数组的方式来传递参数,注意参数列表一定要以 NULL 作为结尾标识符 */ char *arg[]={"env", NULL}; char *envp[]={"PATH=/tmp","USER=sunq",NULL}; if (fork() == 0) { if (execve("/bin/env", arg, envp)<0) { perror("execve error!"); } }
/********************************************************************/
exit 和 _exit
/********************************************************************/
exit :#include<stdlib.h> _exit: #include<unistd.h> void exit(int status); void _exit(int status); /* 利用该参数 status 传递进程结束时的状态。一般来说,0表示正常结束;其他的数值表示出现错误,进程非正常结束 */
这两个函数调用很简单,输入状态参数即可,如下:
exit(0); _exit(-1);
/********************************************************************/
wait 和 waitpid
/********************************************************************/
wait 函数可以使父进程(也就是调用 wait 的进程)阻塞,直到任意一个子进程结束或者父进程接到了一个指定的信号为止。如果该父进程没有子进程或者他的子进程已经结束,则 wait 函数会立即返回。
waitpid 的作用和 wait 一样,但它并不一定要等待第一个终止的子进程。它还有若干选项,如可提供一个非阻塞版本的 wait 功能,也能支持作业控制。实际上 wait 函数只是 waitpid 函数的一个特例,在 Linux 内部实现 wait 函数时直接调用的就是 waitpid 函数。
#include<sys/types.h> #include<sys/wait.h> pid_t wait(int *status) //等待子进程结束,同时接受子进程退出时的状态。 pid_t waitpid(pid_t pid,//等待结束的进程类型 int *status//同 wait int options)//选项
若是 status 位空,则不保存子进程的退出状态。
成功时返回子进程的子进程号或0(调用成功但子进程还未退出);
失败时返回-1;
调用 waitpid ,且父进程不阻塞:
pr=waitpid(pid,NULL,WNOHANG);
/********************************************************************/
避免僵尸进程实例
/********************************************************************/
方法1:父进程调用 wait/waitpid 等待子进程结束,但这样做有一个弊端就是在子进程结束前父进程会一直阻塞,不能做任何事情。
方法2:调用两次 fork 函数(这个方法好!);
#include<sys/types.h> #include<unistd.h> #include<sys/wait.h> #include<stdlib.h> #include<stdio.h> int main() { pid_t pid; if((pid = fork()) < 0) { perror("fork"); } else if(pid == 0) { if((pid = fork()) < 0) { perror("fork"); } else if(pid > 0) { exit(0); } else { sleep(2); printf("second child, parient pid = %d\n", getpid()); exit(0); } } else { //其他进程 } }
因为子进程 1 创建完子进程 2 后退出,所以子进程 2 变成了孤儿进程,自动被 init 进程收养。当子进程 2 结束时,init 进程对子进程 2 进程回收,避免了僵尸进程的出现。