linuxfork 创建两个子进程进程为什么fork

下次自动登录
现在的位置:
& 综合 & 正文
linux进程创建过程与原理 & linux进程创建:fork、vfork和clone联系与区别
系统允许一个进程创建新进程,新进程即为子进程,子进程还可以创建新的子进程,形成进程树结构模型。整个linux系统的所有进程也是一个树形结构。树根是系统自动构造的,即在内核态下执行的0号进程,它是所有进程的祖先。由0号进程创建1号进程(内核态),1号负责执行内核的部分初始化工作及进行系统配置,并创建若干个用于高速缓存和虚拟主存管理的内核线程。随后,1号进程调用execve()运行可执行init,并演变成用户态1号进程,即init进程。它按照配置文件/etc/initab的要求,完成系统启动工作,创建编号为1号、2号...的若干终端注册进程getty。每个getty进程设置其进程组标识号,并监视配置到系统终端的接口线路。当检测到来自终端的连接信号时,getty进程将通过函数execve()执行注册程序login,此时用户就可输入注册名和密码进入登录过程,如果成功,由login程序再通过函数execv()执行shell,该shell进程接收getty进程的pid,取代原来的getty进程。再由shell直接或间接地产生其他进程。
上述过程可描述为:0号进程-&1号内核进程-&1号内核线程-&1号用户进程(init进程)-&getty进程-&shell进程
注意,上述过程描述中提到:1号内核进程调用执行init并演变成1号用户态进程(init进程),这里前者是init是函数,后者是进程。两者容易混淆,区别如下:
1.init()函数在内核态运行,是内核
2.init进程是内核启动并运行的第一个用户进程,运行在用户态下。
3.init()函数调用execve()从文件/etc/inittab中加载可执行程序init并执行,这个过程并没有使用调用do_fork(),因此两个进程都是1号进程。
linux进程创建:fork、vfork和clone联系与区别
fork,vfork,clone都是linux的系统调用,用来创建子进程的,但是大家在使用时经常混淆,这里给出具体例子讲解三者的联系与区别。
在此之前,推荐大家先看我的:
我们知道,进程由4个要素组成:
1.进程控制块:进程标志
2.进程程序块:可与其他进程共享
3.进程数据块:进程专属空间,用于存放各种私有数据以及堆栈空间。4.独立的空间(如果没有4则认为是线程)
fork 创造的子进程复制了父亲进程的资源,包括内存的内容task_struct内容,新旧进程使用同一代码段,复制数据段和堆栈段,这里的复制采用了注明的copy_on_write技术,即一旦子进程开始运行,则新旧进程的地址空间已经分开,两者运行独立。如:
int main() {
int num = 1;
if(!(child = fork())) {
printf("This is son, his num is: %d. and his pid is: %d\n", ++num, getpid());
printf("This is father, his num is: %d, his pid is: %d\n", num, getpid());
执行结果为:This is son, his num is: 2. and his pid is: 2139
This is father, his num is: 1, his pid is: 2138
从代码里面可以看出2者的pid不同,子进程改变了num的值,而父进程中的num没有改变。
总结:优点是子进程的执行独立于父进程,具有良好的并发性。缺点是两者的通信需要专门的通信机制,如pipe、fifo和system V等。有人认为这样大批量的复制会导致执行效率过低。其实在复制过程中,子进程复制了父进程的task_struct,系统堆栈空间和页面表,在子进程运行前,两者指向同一页面。而当子进程改变了父进程的变量时候,会通过copy_on_write的手段为所涉及的页面建立一个新的副本。因此fork效率并不低。
vfork函数创建的子进程完全运行在父进程的地址空间上,子进程对虚拟地址空间任何数据的修改都为父进程所见。这与fork是完全不同的,fork进程是独立的空间。另外一点不同的是vfork创建的子进程后,父进程会被阻塞,直到子进程执行exec()和exit()。如:
int main() {
int num = 1;
if(!(child = fork())) {
printf("This is son, his num is: %d. and his pid is: %d\n", ++num, getpid());
printf("This is father, his num is: %d, his pid is: %d\n", num, getpid());
运行结果为:This is son, his num is: 2. and his pid is:4139
This is father, his num is: 2, his pid is: 4138
从运行结果可以看到vfork创建出的子进程(线程)共享了父进程的num变量,这一次是指针复制,2者的指针指向了同一个内存
总结:当创建子进程的目的仅仅是为了调用exec()执行另一个程序时,子进程不会对父进程的地址空间又任何引用。因此,此时对地址空间的复制是多余的,通过vfork可以减少不必要的开销。
函数功能强大,带了众多参数,因此由他创建的进程要比前面2种方法要复杂。clone可以让你有选择性的继承父进程的资源,你可以选择想vfork一样和父进程共享一个虚存空间,从而使创造的是线程,你也可以不和父进程共享,你甚至可以选择创造出来的进程和父进程不再是父子关系,而是兄弟关系
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
这里fn是函数指针,我们知道进程的4要素,这个就是指向程序的指针,就是所谓的“剧本", child_stack明显是为子进程分配系统堆栈空间(在linux下系统堆栈空间是2页面,就是8K的内存,其中在这块内存中,低地址上放入了值,这个值就是进程控制块task_struct的值),flags就是标志用来描述你需要从父进程继承那些资源, arg就是传给子进程的参数)。
clone, fork, vfork实现方式
系统调用服务例程sys_clone, sys_fork, sys_vfork三者最终都是调用do_fork函数完成.
do_fork的参数与clone系统调用的参数类似, 不过多了一个regs(内核栈保存的用户模式寄存器). 实际上其他的参数也都是用regs取的
clone的API外衣, 把fn, arg压入用户栈中, 然后引发系统调用. 返回用户模式后下一条指令就是fn.
sysclone: parent_tidptr, child_tidptr都传到了 do_fork的参数中
sysclone: 检查是否有新的栈, 如果没有就用父进程的栈 (开始地址就是regs.esp)
fork, vfork:
服务例程就是直接调用do_fork, 不过参数稍加修改
clone_flags:
sys_fork: SIGCHLD|0;
sys_vfork: SIGCHLD| (clone_vfork | clone_vm)
用户栈: 都是父进程的栈.
parent_tidptr, child_ctidptr都是NULL.
&&&&推荐文章:
【上篇】【下篇】Linux fork函数创建一个新进程_Linux编程_Linux公社-Linux系统门户网站
你好,游客
Linux fork函数创建一个新进程
来源:Linux社区&
作者:blackcastle7
在Linux中创建一个新进程的唯一方法是使用fork函数,fork()执行一次但有两个返回值。
在父进程中,返回值是子进程的进程号;在子进程中,返回值为0。因此可通过返回值来判断当前进程是父进程还是子进程。
使用fork函数得到的子进程是父进程的一个复制品,它从父进程处复制了整个进程的地址空间,包括进程上下文,进程堆栈,内存信息,打开的文件描述符,信号控制设定,进程优先级,进程组号,当前工作目录,根目录,资源限制,控制终端等。而子进程所独有的只是它的进程号,资源使用和计时器等。可以看出,使用fork函数的代价是很大的,它复制了父进程中的代码段,数据段和堆栈段里的大部分内容,使得fork函数的执行速度并不快。
#include &unistd.h&
函数定义:
int fork( void );
子进程中返回0,父进程中返回子进程ID,出错返回-1
一个简单的fork程序:
#include &stdio.h&
#include &stdlib.h&
#include &unistd.h&
int main()
pid_ //定义一个进程号变量
printf("start fork\n");
id = fork(); //调用fork函数新建一个进程
printf("end fork\n");
//判断当前进程
if(id & 0){ //出错
perror("fork failed\n");
else if(id == 0){ //子进程
printf("In child\n");
printf("i = %d\n", i++);
else{ //父进程
printf("In father\n");
printf("i = %d\n", i++);
运行结果:[root@localhost Process]# ./forkstart forkend forkIn childi = 1end forkIn fatheri = 1可知:1.子进程是从调用fork函数处的下一条语句开始执行的。2.子进程中的局部变量i不同于父进程中的i,是父进程的复制。下面写一个小实验(包括了fork, exec, waitpid等函数的使用):实验要求:该实验有3个进程,其中一个为父进程,其余两个是该父进程创建的子进程,其中一个子进程运行“ls -l”指令,另一个子进程在暂停5s之后异常退出,父进程并不阻塞自己,并等待子进程的退出信息,带收集到该信息,父进程就返回。程序如下:#include &stdio.h&#include &stdlib.h&#include &unistd.h&#include &fcntl.h&#include &sys/wait.h&int main(){pid_t child1, child2,child1 = fork(); //创建子进程1child2 = fork(); //创建子进程2if(child1 & 0){perror("fork child1 failed\n");exit(1);}else if(child1 == 0){printf("In child1: execute 'ls -l'\n"); //子进程1执行"ls -l"if(execlp("ls", "ls", "-l", NULL) & 0){perror("execlp failed\n");}}if(child2 & 0){perror("fork child2 failed\n");exit(1);}else if(child2 == 0){printf("In child2: sleep for 5 sec and exit\n");sleep(5); //子进程2睡眠5sexit(1);}& else{printf("In father process:\n");do{ //父进程每隔1s接受一次子进程2的信号child = waitpid(child2, NULL, WNOHANG);if(child == 0){printf("The child2 process has not exited!\n");sleep(1);}}while( child == 0);if(child == child2){ //接受到信号printf("Get child2\n");}else{perror("Error\n");}}return 0;}运行结果:[root@localhost Process]# ./exp1In child1: execute 'ls -l'In child1: execute 'ls -l'In child2: sleep for 5 sec and exitIn father process:The child2 process has not exited!总计 15-rwxrwxrwx 1 root root
19:44 dameon-rwxrwxrwx 1 root root& 812 02-12 19:43 dameon.c-rwxrwxrwx 1 root root -12 exp1-rwxrwxrwx 1 root root -12 exp1.c-rwxrwxrwx 1 root root
22:19 fork-rwxrwxrwx 1 root root& 508 02-12 22:19 fork.c-rwxrwxrwx 1 root root& 510 02-12 22:18 fork.c.bak-rwxrwxrwx 1 root root
19:57 sys_dameon-rwxrwxrwx 1 root root& 919 02-12 19:57 sys_dameon.c总计 15-rwxrwxrwx 1 root root
19:44 dameon-rwxrwxrwx 1 root root& 812 02-12 19:43 dameon.c-rwxrwxrwx 1 root root -12 exp1-rwxrwxrwx 1 root root -12 exp1.c-rwxrwxrwx 1 root root
22:19 fork-rwxrwxrwx 1 root root& 508 02-12 22:19 fork.c-rwxrwxrwx 1 root root& 510 02-12 22:18 fork.c.bak-rwxrwxrwx 1 root root
19:57 sys_dameon-rwxrwxrwx 1 root root& 919 02-12 19:57 sys_dameon.cThe child2 process has not exited!The child2 process has not exited!The child2 process has not exited!The child2 process has not exited!Get child2
相关资讯 & & &
& (04月11日)
& (04/16/:35)
& (11/08/:38)
& (03月03日)
& (01/19/:21)
& (10/28/:36)
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款为什么操作系统(linux)是以fork-exec的形式建进程?
/*接上*/为什么一定要先复制父进程的内存堆栈而不是给新进程重新分配内存?
除了创建新进程用来运行别的程序之外,还有一种场景是程序只需要fork成多个同样的,不需要运行别的程序。Windows下CreateProcess直接支持前一种场景,但就没办法支持后一种场景了;Unix的fork只支持后一种场景,但可以通过和exec配合来支持前一种场景,这只是一种实现策略而已。
已有帐号?
无法登录?
社交帐号登录

我要回帖

更多关于 fork创建子进程 的文章

 

随机推荐