1、进程创建-fork
#include <unistd.h>
pid_t fork(void);
- 创建新的进程,失败时返回-1
- 成功时父进程返回子进程的进程号,子进程返回0
- 通过fork的返回值区分父进程和子进程
pid_t pid;
//fork子进程之后,子进程和父进程会同时继续往下执行,因此子进程执行的以下代码,打印pid只会是0,而父/进程则会打印子进程的正整数的pid
if((pid = fork())<0)
{perror("fork");return -1;
}
else if(pid == 0){printf("child process:my pid is %d\n",getpid());
}else{printf("parent process: my pid is %d\n",getpid());
}
(1)子进程继承父进程的内容;
(2)父子进程有独立的地址空间,互相不影响;
(3)若父进程先结束,
- 子进程成为孤儿进程,被init进程收养,linux 内核启动之后创建的第一个用户态进程,进程号1。系统规定每个进程结束的时候,必须由父进程回收。一个进程进程号pid不会变的,但是父进程号有可能会改变的。
- 子进程变成后台进程
(4)若子进程先结束
- 父进程如果没有及时回收,子进程变成僵尸进程(当进程结束,PCB没有被释放返回值和结束方式,必须父进程回收)
请参考《嵌入式开发之进程回收》https://blog.csdn.net/qq_22111417/article/details/143466320?sharetype=blogdetail&sharerId=143466320&sharerefer=PC&sharesource=qq_22111417&sharefrom=mp_from_link
1.1、思考
1、问:子进程从何处开始运行?
答:子进程从fork的下一条指令开始执行。PC的程序寄存器也被继承过来了。子进程不执行fork。
2、问:父子进程谁先执行?
答:linux没有规定哪个先执行,依赖内核的调度。父进程的时间片没有用完,通常父进程先执行。不确定。
3、问:父进程能否多次调用fork?子进程呢?
答:可以,父进程没有限制多次创建子进程。子进程可以创建fork。
2、进程结束- exit/_exit
#include <stdlib.h>
void exit(int status);
#include <unistd.h>
void _exit(int status);
- status:整型的数值,只有低8位有效的
- 结束当前的进程并将status返回
- exit结束进程时会刷新(流)缓冲区,_exit不会刷新缓冲区,会被丢弃
/**
除了遇到换行符\n外,缓冲区刷新的其他条件还包括:(1)进程结束:当程序正常结束时,缓冲区的内容会被刷新并显示。
(2)缓冲区满:当缓冲区满时,内容也会被刷新并显示。
(3)手动刷新:可以使用fflush(stdout)函数手动刷新缓冲区。
(4)调用exit(0):使用exit(0)函数时,缓冲区也会被刷新
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main(void){printf("using exit...\n");//遇到换行符会立即刷新缓冲区printf("this process will eixt"); exit(0); //遇到exit结束程序会刷新缓冲区//_exit(); //直接结束进程,不会刷新缓冲区printf("never be displayed");
}
补充
当父进程没有及时回收子进程时,可以通过以下方法保证子进程不成为僵尸进程:
-
使用信号处理机制:父进程可以使用
SIGCHLD
信号来捕获子进程的退出事件,并在信号处理函数中调用wait()
或waitpid()
来回收子进程资源。 -
设置
SA_NOCLDWAIT
标志:父进程可以通过设置SA_NOCLDWAIT
标志来告诉操作系统不要将已终止子进程保留为僵尸进程。可以使用sigaction()
函数来设置信号处理动作。 -
创建孤儿进程:父进程在创建子进程后,可以通过调用
setsid()
函数创建一个新的会话,并使子进程成为该会话的领导者。这样即使父进程退出,子进程也会被init进程接管,不会成为僵尸进程。 -
使用
waitpid()
函数的WNOHANG
选项:父进程可以通过调用waitpid()
函数并指定WNOHANG
选项来非阻塞地回收子进程资源,即使子进程还未退出也可以得到子进程的状态。 -
终止或重启父进程:当父进程终止或重启时,操作系统会将父进程ID为1的init进程接管子进程,从而避免子进程成为僵尸进程。
综上所述,通过合理使用信号处理机制、设置标志、创建孤儿进程、调用waitpid()
函数和终止或重启父进程等方法,可以保证子进程不会成为僵尸进程。