文章目录
fork入门知识
一个进程,包括代码、数据和分配给进程的资源。fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
note:fork只拷贝下一个要执行的代码到新的进程。
调用fork的同时,我的理解是,已经在内存中创建了“副本”进程,同时返回pid,所以在返回值之前,已经是主进程和子进程同时在运行了(如果fork成功的话),这样,在程序的运行过程中,一次fork返回了两次值,在父进程中,fork返回新创建子进程的进程ID,在子进程中,fork返回0,这时候就能够同时跑两个进程了。
进程复制图
fork系统调用说明
通过man手册我们可以轻松知道fork()包含的头文件<sys/types.h>和<unistd.h>,功能就是创建一个子进程.函数原型:pid_t fork(void),pid_t是带一个代表经常号pid的数据结构.如果创建成功一个子进程,对于父进程来说是返回子进程的ID.而对于子进程来说就是返回0.而返回-1代表创建子进程失败.
调用示例
#include <sys/types.h>
#include <unistd.h> //fork()头文件
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <signal.h>
int main(void)
{
pid_t pid ;
signal(SIGCHLD,SIG_IGN);//处理僵尸进程,下面有讲解
printf("before fork pid:%d\n",getpid());
int abc = 10;
pid = fork(); //创建子进程分配资源并复制父进程的数据,返回两个值
if(pid == -1) //错误返回
{
perror("tile");
return -1;
}
if(pid > 0) //父进程空间
{
abc++;
printf("parent:pid:%d \n",getpid());
printf("abc:%d \n",abc);
sleep(20);
}
else if(pid == 0)
{ //子进程空间:其中abc初值为10,从父进程复制而来
abc++;
printf("child:%d,parent: %d\n",getpid(),getppid());
printf("abc:%d",abc);
}
printf("fork after...\n");
}
运行结果:
before fork pid:27319
parent:pid:27319
abc:11
before fork pid:27319
child:27320,parent: 27319
abc:11
fork after...
fork()系统调用注意点
通过以上程序我们可以知道:
- fork系统调用之后,父进程和子进程交替执行(顺序随机),并且它们处于不同空间中。
- fork()函数的一次调用返回2次返回,这个有点抽象难理解,此时二个进程处于独立的空间,它们各自执行者自己的东西,不产生冲突,所以返回2次一次pid ==0,一次pid大于0.而至于是先子进程还是父进程先执行,这没有确切的规定,是随机的.
- 将fork()返回值大于零设置为父进程
- 执行过程在fork()之后,并不是从头开始,因为在fork()之前,父进程已经为子进程搭建好了运行环境了…理解了上面四点,相信我们应该对此系统调用一定有较深的了解.
关于signal(SIGCHLD,SIG_IGN)
signal(参数一,参数二)
参数一:我们要进行处理的信号。系统的信号我们可以再终端键入 kill -l查看(共64个)。其实这些信号时系统定义的宏。
参数二:我们处理的方式(是系统默认还是忽略还是捕获)。可以写一个handdle函数来处理我们捕获的信号
SIGCHLD信号
子进程结束时, 父进程会收到这个信号。
如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。这种情 况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程 来接管)。
SIG_ING
忽略的意思
使用signal(SIGCHLD, SIG_IGN)处理僵尸进程
通过signal(SIGCHLD, SIG_IGN)
通知内核对子进程的结束不关心(忽略),由内核回收:
如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号该信号是子进程退出的时候向父进程发送的,此时的效果类似于父进程先退出,子进程由init
进程接管。
vfork()系统调用
vfork()在某些情况下,我们知道vfork()与fork()执行结果是一样的,
除了子进程会执行一次exec系统调用或者调用_exit(0)退出.
函数原型:pid_t vfork vfork(void)
具体返回值与其中fork()类似.
这个函数时是在没有实现写时赋值前提下,所以现在我们并不推荐使用vfork().
结论如下
- vfork() 子进程与父进程共享数据段
- vfork() 中是子进程先执行,父进程后执行.
通常我们都是与exec函数在一起,在主进程中替换进程印象.