进程创建:
首先认识一下fork()函数:
pid_t fork();
函数作用:
创建一个子进程;
返回值:
1.调用成功,子进程返回0;父进程返回子进程的PID;
2.调用失败,返回-1;
子进程创建成功,子进程会采用写时拷贝的方式拷贝父进程的数据;
进程等待:
先认识两个函数:
pid_t wait(int* status);
pid_t waitpid(pid_t pid,int* status,int option);
先来说wait函数:
父进程调用wait函数可以回收子进程终止信息;该函数有以下功能:
1.阻塞等待子进程退出;
2.回收子进程资源;
3.获取子进程结束状态(退出原因);
wait函数调用成功 则返回等待的子进程的PID,失败:则返回-1(说明没有子进程),status,传出参数,用来获取子进程退出的状态,不关心子进程退出状态,则可以设置为NULL, 它占4个字节,低8位,退出信号; 次低8位,退出码;
用一些特定的宏和传出参数status搭配来判断进程终止状态:
- WIFEXITED(status) 为非0 → 进程正常结束
WEXITSTATUS(status) 如上宏为真,使用此宏 → 获取进程退出状态 (exit的参数) - WIFSIGNALED(status) 为非0 → 进程异常终止
WTERMSIG(status) 如上宏为真,使用此宏 → 取得使进程终止的那个信号的编号。
接着说waitpid函数:
参数:
pid>0: 只等待子进程ID等于pid的子进程;
pid=-1:等待任何一个子进程,和wait一样;
pid==0等待其组ID等于调⽤用进程组ID的任一个子进程。
pid<-1等待其组ID等于pid绝对值的任一子进程。
status参数上面已经介绍过了;
option参数有以下两种:
1.如果使用了WNOHANG参数,即使没有子进程退出,它也会立即返回,返回一个0值,不会像wait那样永远等下去.
2.如果使用了WUNTRACED参数,则子进程进入暂停则马上返回,但结束状态不予以理会.
如果我们不想使用它们,也可以把options设为0,如:ret=waitpid(-1,NULL,0);
如果使用上面两个宏,waitpid就是非阻塞等待; 如果不使用,用0代替,则就会和wait一样 变为阻塞式等待;
程序替换:
先来说一下exec函数族:
fork创建子进程成功后,子进程执行的是和父进程相同的代码(也有可能执行不同的代码分支),子进程往往会调用exec函数,以执行不同的程序,当进程调用一种exec函数时,该进程用户空间代码和数据会完全被新程序代替(包括一些堆、栈数据),调用exec函数并不会创建新进程,原有的进程ID不会变;
exec族函数有以下6种:
#include<unistd.h>
int execl(const char* path,const char* argc, ...); //库函数
// 参数: 路径、具体操作,以NULL结尾
int execlp(const cahr* file,const char* argc,...); //库函数
//参数: 文件名,具体操作,以NULL结尾
int execle(const char* path,const char* argc,...,const char* envp[]) //库函数
//参数: 路径、具体操作,以NULL结尾、环境变量(NULL结尾)
int execv(const char* path,char* const argv[]) //库函数
//参数: 路径、指令数组(存放需要执行的指令)以NULL结尾
int execvp(const char* file,char* const argv[]) //库函数
//参数: 文件名、 同上
int execve(const char* path,char* const argv[],char* const envp[])
//系统调用
//参数: 路径、指令数组(以NULL结尾)、环境变量(以NULL结尾)
//exec函数族的具体用法:
#include<unistd.h>
int main()
{
char* const argv={"ls","-a",NULL};
char* const envp={"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-ef", NULL);
execlp("ps", "ps", "-ef", NULL);
execle("ps", "ps", "-ef", NULL, envp);
execv("/bin/ps", argv);
execvp("ps", argv);
execve("/bin/ps", argv, envp);
}
//总结一下:
//1.带l的 就是要把指令列举出来
//2.带v的 参数直接用数组(数组里面放的是指令)
//3.带p的 第一个参数路径可以不写全,只写一个文件名即可;
//4.带e的 多一个参数,envp[], 里边放的环境变量值,意思是构建一份
//新的环境变量表给它,构造的环境变量不会继承原有的;