fork函数
1.进程概念
多个进程可共享同一个可执行文件(程序),进程一般定义为程序执行的一个实例。fork用于进程中创建子进程,当该子进程创建时,它从fork函数的下一条语句(或者说从fork的返回处)开始执行与父进程相同的代码。fork后父子进程执行的先后顺序并不确定(与内核调度算法有关)。
2.简单范例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
//信号处理函数
void sig_usr(int signo) {
printf("Signal=%d, PID=%d\n", signo, getpid());
}
int main(int argc, char *const *argv) {
pid_t pid;
printf("Start\n");
if(signal(SIGUSR1, sig_usr) == SIG_ERR) {
printf("Catch SIGUSR1 Error\n");
exit(1);
}
pid = fork();
if(pid<0) {
printf("Create Process Error\n");
exit(1);
}
//父、子进程同时运行
while(1) {
sleep(1);
printf("Sleep 1s, PID=%d\n", getpid());
}
printf("Over, PID=%d\n", getpid());
return 0;
}
子进程被杀死(SIGKILL)后,父进程收到了SIGCHILD信号,子进程变为僵尸进程(COMMAND显示defunct(失效),STAT显示Z)。
3.僵尸进程的产生和解决
子进程终止了,父进程还活着,但该父进程未调用函数(wait/waitpid)来进行额外处置(处置子进程终止这件事),子进程将变成僵尸进程(未释放资源,比如PID)。
杀掉父进程,僵尸进程会自动消失;
子进程终止或停止时,父进程会收到SIGCHILD信号,可以在信号处理函数中调用wait/waitpid函数,释放僵尸进程。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
//信号处理函数
void sig_usr(int signo) {
int status;
switch(signo) {
case SIGUSR1:
printf("SIGUSR1, PID=%d\n", getpid());
break;
case SIGCHILD://杀掉子进程信号
//新函数waitpid,有人也用wait,但是掌握和使用waitpid即可
//这个函数说白了获取子进程终止状态,这样子进程就不会成为僵尸进程了
printf("SIGCHILD, PID=%d\n", getpid());
//第一个参数为-1,表示等待任何子进程
//第二个参数status,保存子进程的状态信息
//第三个参数提供了额外选项,WNOHANG表示不要阻塞,让这个waitpid立即返回
pid_t pid = waitpid(-1, &status, WNOHANG);
if (pid == 0)//子进程没有结束会立即返回这个数字,但这里应该不是这个这个数字
return;
if (pid == -1)//waitpid调用有错误,有错误也返回出去我们管不了这么多
return;
//走到这里,表示成功了那也retrun吧
break;
}
}
int main(int argc, char *const *argv) {
pid_t pid;
if(signal(SIGUSR1, sig_usr) == SIG_ERR) {
printf("Catch SIGUSR1 Error\n");
exit(1);
}
if(signal(SIGCHILD, sig_usr) == SIG_ERR) {
printf("Catch SIGCHILD Error\n");
exit(1);
}
pid = fork();
if(pid<0) {
printf("Create Process Error\n");
exit(1);
}
//父子进程同时运行
while(1) {
sleep(1);
printf("Sleep 1s, PID=%d\n", getpid());
}
printf("Over, PID=%d\n", getpid());
return 0;
}
4.进一步认识
fork产生的子进程和父进程共享内存空间,采用的是写时复制(修改内存后,复制一份内存给进程单独使用,避免相互影响,内核给它们单独分配),提高执行效率。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
//4个进程
int main(int argc, char *const *argv) {
fork();
fork();
while(1) {
sleep(1);
printf("Sleep 1s, PID=%d\n", getpid());
}
printf("Over, PID=%d\n", getpid());
return 0;
}
5.完善一下fork代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
int g_mygbltest = 0;
int main(int argc, char *const *argv) {
pid_t pid;
printf("Start\n");
pid = fork();//创建子进程
if(pid<0) {
printf("Create Process Error\n");
exit(1);
}
//父、子进程都会执行下面代码 pid的值可以区别父子进程
if(pid==0) {//子进程
while(1) {
g_mygbltest++;//修写了内存 修改内存后,复制一份内存给进程单独使用,避免相互影响
sleep(1);
printf("Child Process, PID=%d, g_mygbltest=%d\n", getpid(), g_mygbltest);
}
} else {//父进程
while(1) {
g_mygbltest++;//修写了内存 修改内存后,复制一份内存给进程单独使用,避免相互影响
sleep(1);
printf("Parent Process, PID=%d, g_mygbltest=%d\n", getpid(), g_mygbltest);
}
}
printf("Over, PID=%d\n", getpid());
return 0;
}
6.fork执行相关的逻辑判断
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
//7个进程
int main(int argc, char *const *argv) {
(fork() && fork()) || (fork() && fork());
while(1) {
sleep(1);
printf("Sleep 1s, PID=%d\n", getpid());
}
printf("Over, PID=%d\n", getpid());
return 0;
}
7.fork失败的可能原因
(1)系统中进程太多,系统PID有限,默认最大PID为32767,僵尸进程占据PID。
(2)创建进程数超过当前用户允许创建的最大进程数
printf("每个用户允许创建的最大进程数=%ld\n", sysconf(_SC_CHILD_MAX));