一、进程创建
(1)
#include <unistd.h>
pid_t fork(void);
父进程这返回子进程的pid号,子进程中返回值为0。
fork执行流程:
用户态调用fork函数 -> 触发80中断,切换到内核态执行系统函数(sys_fork)-> 复制当前进程信息(堆栈寄存器内容等)生成子进程 -> 设置当前进程,子进程的eax寄存器(两个进程设置的eax值不同) -> 当前进程返回(return值为eax中的内容) -> fork执行完毕 -> 操作系统切换进程到子进程 -> 子进程获取到返回值(eax)
子进程创建完成后的状态,执行位置处于sys_fork函数即将return的位置
本质上fork函数确实只调用了一次,只不过系统在fork函数中完成对子进程的相关设置后,给父进程的eax寄存器和子进程的eax寄存器设置的值是不同的,从而导致在两个进程中fork函数返回的值不同。
(2)子进程存在写时拷贝机制,只有在实际修改子进程中值的时候,才会给子进程真正分配空间
(3)_exit方式退出进程,将不会执行用户的清除函数 刷新缓冲区 关闭流等。另外,在主函数中的return num 就相当于调用了 exit(num),而exit(num)在执行完用户定义的清理函数 清空缓冲区关闭流之后还是会调用_exit函数
二、进程等待
wait
我们可以使用wait函数来让父进程等待子进程运行结束
它的函数使用方法如下
pid_t wait(int* status);
它的返回值是一个pid_t类型的数据 如果等待成功会返回这个进程的pid 等待失败则会返回-1
status指针是一个输出型参数,用来返回进程运行的结果:
一般用到这个指针指向的int变量的低十六位:
- 高八位表示的是退出状态 即我们的退出码
- 低七位表示的是终止信号 如果我们的进程被信号所杀则此处会有终止信号
- 第八位表示的是core dump标志,表示子进程是否因为出错生成了核心存储文件。有关于core dump的详细信息见Linux Core Dump - 知乎 (zhihu.com)
waitpid
waitpid和wait函数的作用差不多 都是等待一个子进程结束
它的函数使用方法如下
pid_t waitpid(pid_t pid, int* status, int options);
如果等待成功会返回等待进程的pid 如果等待失败则返回-1
这个函数具有三个参数:
pid_t pid
此项参数需要我们填入一个子进程的pid来指定等待该子进程结束
如果我们想等待任意进程都可以 这里可以填-1
int* status
此项参数需要我们填入一个地址
它是一个输出型参数 当我们填入一个地址后该函数会在该地址处写入子进程的退出状态
如果我们不关心这个退出状态 这里可以填写null
int options
此项参数需要我们填入选项
如果我们想要父进程一直等待子进程结束 则可以填入0 (阻塞等待)
如果我们只想父进程问一次子进程有没有结束 则可以填入WNOHANG (非阻塞等待)
宏方法
linux中提供了两个宏来让我们获取退出码和退出信号
它们分别是
- WIFEXITED(status):用于查看进程是否是正常退出 本质是检查是否收到信号
- WEXITSTATUS(status):用于获取进程的退出码
三、进程替换
Linux C中进程替换通过六大exec函数完成,他们的区别仅仅在于执行文件的路径、传递参数的方法与环境变量的设置方法不同,其中只有execve才是真正的系统调用。
例如:
execve
int execve(const char *path, char *const argv[], char *const envp[]);
这个v我们可以将它理解为vector 数组的意思 我们传递的参数要以数组的形式传递
这个e意味着它可以自己配置一个环境变量 我们在自己的函数内部就可以使用我们配置的这个环境变量
例如 我们可以自己设置一个env环境变量 并在我们的程序中使用它:
char* myargv[] = { "mycmd", NULL };
char* env[] = { "val", NULL };
execve("./mycmd", myargv, env);
execle
int execle(const char *path, const char *arg, ..., char *const envp[]);
我们首先看这个函数的名字 相比我们的exec多了一个l 多了一个e
多了的这个e意味着它可以自己配置一个环境变量 我们在自己的函数内部就可以使用我们配置的这个环境变量
它的第一个参数是const char *path
它代表着要执行程序的路径
它的第二个参数是 const char *arg, ...
它代表着可变参数列表 是使用NULL结尾的
它的第三个参数是 *const envp[]
它代表着一个数组 数组里面是我们自己配置的环境变量
例如 我们可以自己设置一个env环境变量 并在我们的程序中使用它
char* envp[] = { "val", NULL };
execle("./mycmd", "mycmd", NULL, envp);