Unix 中 fork 函数的理解
fork是创建子进程的关键函数,常和exec系列函数一起使用。
对于代码
1 | #include <stdio.h> |
执行结果如下:
fork() 调用之后,系统中同时存在了两个进程,调用一次,返回两次。
子进程得到的返回值为0,而父进程得到的返回值是子进程的PID(大于0),子进程获得了父进程数据空间、堆和栈的副本。注意是“副本”,父子进程不共享内存变量(除非使用特殊的共享内存手段)。
当子进程执行 execl 时,它会把当前进程的整个内存(代码段、数据段)全部清空,然后加载 /bin/ls 的程序代码,子进程后面的代码就不执行了。
注意执行 exec 系列函数后,原进程打开的文件描述符(如 open("/dev/scull", ...) 得到的 fd)默认保持打开状态。这意味着新程序可以直接通过原来的数字 fd 继续读写文件。除非有**D_CLOEXEC 标志**,如果在 open 驱动时设置了这个标志(Close-on-exec),那么在执行 exec 的瞬间,内核会自动关闭这个 fd,防止子进程意外操作父进程打开的敏感硬件设备。
execl参数含义:
"/bin/ls":可执行文件的绝对路径。"ls":传给程序的argv[0],通常是程序名。"-l":传给程序的参数。NULL:必须以空指针结尾,告诉内核参数列表结束了。
wait则是父进程执行到这一行会挂起(休眠),等待任何一个子进程结束后继续执行
补充:
exec系列函数:
| 函数名 | 字母含义 | 特点 |
|---|---|---|
execl |
l(list) | 参数以列表形式列出,最后必须以 NULL 结尾。 |
execv |
v(vector) | 参数放入一个字符串数组(指针数组)中传递。 |
execlp |
p (path) | 会自动在环境变量 PATH 中寻找可执行文件,不需要写全路径。 |
exece |
e (env) | 可以自定义环境变量,而不使用当前进程的环境变量。 |
如果知道所有参数的话就使用**execl**
1 | // 路径, argv[0], argv[1], ..., NULL |
如果参数是动态生成的,用 execv
1 | char *args[] = {"ls", "-l", "-a", NULL}; |
根据后缀是v还是l就可以判断传入的参数,也就是说exec函数可以有很多组合,如execvp,execve,execlp,execle。
如果不想写 /bin/ls 这么长的绝对路径,只想写 ls,就使用p(path)
1 | execvp("ls", args); |
内核会去 /usr/bin, /bin 等目录下帮你找。
execve是 Linux 内核中真正的系统调用。其他的 execl, execvp 等底层最终都会调用 execve。不使用e的话就直接继承父进程的环境变量。函数原型:
1 | int execve(const char *pathname, char *const argv[], char *const envp[]); |
