fork 函数
fork 函数用于创建一个进程(创建当前调用此函数的子进程)
- 父进程的fork返回 子进程ID ;而子进程的fork返回 0,表示进程创建成功
- getpid函数返回调用此函数的进程ID;getppid函数返回调用此函数的父进程ID
- getuid函数返回当前进程的实际用户ID;geteuid函数返回当前进程的有效用户ID
- 在fork之后,父子进程有相同的权利去抢占CPU,并不意味着父进程的优先级一定会比子进程的高,取决于内核所使用的调度算法
- fork函数遵循 “
读时共享,写时复制
” 的原则:如果fork后的代码只有读操作,那么共享同一块物理地址
;否则复制一份
循环创建多个进程示例:
- 用
for() { fork(); }
循环调用fork函数会创建 2n - 1 个子进程。正确的方法应该如下:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid;
int i;
for (i = 0; i < 5; ++i) {
pid = fork();
if (pid == -1) {
perror("fork error");
exit(1);
} else if (pid == 0) {
break;
}
}
if (i < 5) {
sleep(i); // 按创建子进程的顺序打印
printf("I'm %d child, pid = %u, ppid = %u\n", i+1, getpid(), getppid());
} else {
sleep(i);
printf("I'm parent, pid = %u, ppid = %u\n", getpid(), getppid()); // 这里的ppid是bash/shell
}
return 0;
}
在fork以后,父子进程有哪些相同,哪些不同呢?各自独立和共享的又有什么呢?
- 相同:代码段、堆、栈、用户ID、进程工作目录、信号处理方式等
- 不同:进程ID、父进程ID、定时器、未决信号集等
- 全局变量位于data段,父子进程是各自独立的(在做写操作的情况下,否则是共享的)
- 共享的是
文件描述符
和mmap建立的映射区
gdb调试多进程程序
gdb在调试时默认跟踪父进程,在gdb中设置fork之后的跟踪进程的方法(要在fork函数调用前设置)是
set follow-fork-mode child
set follow-fork-mode parent
子进程状态(孤儿与僵尸)
孤儿进程:父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程(1号)
僵尸进程:进程终止,父进程尚未回收,子进程0-4G进程地址空间都释放了,但唯独PCB残留在内核中,成为僵尸进程。
僵尸进程不能被kill命令清除,因为kill命令是用来终止进程的,而僵尸进程已经终止
。- PCB存在的意义是为了父进程给子进程“收尸”,例如获取
子进程的退出状态
和导致终止的信号信息
子进程回收
对于已经终止的子进程,它的父进程可以调用 wait 或 waitpid 来获取子进程终止时在内核中残留的PCB信息,然后彻底清除掉这个进程。
1、wait 函数
- 父进程阻塞,等待子进程退出
- 回收子进程残留资源
- 获取子进程结束状态或退出原因
1)进程正常结束:
WIFEXITED(status)
为真
WEXITSTATUS(status)
,程序正常结束,用该宏函数获取进程的退出状态2)进程异常终止:
WIFSIGNALED(status)
为真
WTERMSIG(status)
,程序异常终止,用该宏函数获取导致终止的信号编号
2、waitpid 函数
exec 函数族
作用是在程序中运行一个程序。当一个进程调用其中一个exec函数时,该进程的用户空间代码和数据完全被新程序替换(.text,.data)
,从新程序的启动例程(.text第一条指令
)开始执行。
调用exec并不创建新进程
,所以调用exec前后该进程的ID并没有改变
示例:int execlp(const char* file, const char *arg, ...);
,命令行参数以NULL
结尾
execlp("ls", "ls", "-l", NULL);
,第二个参数是argv[0]
,第二个以后的参数是传递给可执行程序 ls;使用程序名在PATH环境变量中搜索,有环境变量参与execl("/bin/ls", "ls", "-l", NULL);
,使用参数1给出的绝对路径搜索,没有环境变量参与
使用实例:利用exec函数,将当前系统中的进程信息打印到文件中
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int main () {
int fd = open("ps.out", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
perror("open ps.out error");
exit(1);
}
dup2(fd, STDOUT_FILENO); // 把参1的文件描述符复制给参2,导致参2指向的文件现在指向参1指向的文件
execlp("ps", "ps", "ax", NULL);
perror("exec error"); // exec出错才会执行该语句
exit(1);
return 0;
}
来源:CSDN
作者:csdn_dzh
链接:https://blog.csdn.net/CSDN_dzh/article/details/103651974