linux系统编程之进程(三):进程复制fork,孤儿进程,僵尸进程
本节目标:
- 复制进程映像
- fork系统调用
- 孤儿进程、僵尸进程
- 写时复制
一,进程复制(或产生)
使用fork函数得到的子进程从父进程的继承了整个进程的地址空间,包括:进程上下文、进程堆栈、内存信息、打开的文件描述符、信号控制设置、进程优先级、进程组号、当前工作目录、根目录、资源限制、控制终端等。
子进程与父进程的区别在于:
1、父进程设置的锁,子进程不继承(因为如果是排它锁,被继承的话,矛盾了)
2、各自的进程ID和父进程ID不同
3、子进程的未决告警被清除;
4、子进程的未决信号集设置为空集。
二,fork系统调用
包含头文件 <sys/types.h> 和 <unistd.h>
函数功能:创建一个子进程
函数原型
pid_t fork(void); //一次调用两次返回值,是在各自的地址空间返回,意味着现在有两个基本一样的进程在执行
参数:无参数。
返回值:
- 如果成功创建一个子进程,对于父进程来说返回子进程ID
- 如果成功创建一个子进程,对于子进程来说返回值为0
- 如果为-1表示创建失败
流程图:
父进程调用fork()系统调用,然后陷入内核,进行进程复制,如果成功:
1,则对调用进程即父进程来说返回值为刚产生的子进程pid,因为进程PCB没有子进程信息,父进程只能通过这样获得。
2,对子进程(刚产生的新进程),则返回0,
这时就有两个进程在接着向下执行
如果失败,则返回0,调用进程继续向下执行
注:fork英文意思:分支,fork系统调用复制产生的子进程与父进程(调用进程)基本一样:代码段+数据段+堆栈段+PCB,当前的运行环境基本一样,所以子进程在fork之后开始向下执行,而不会从头开始执行。
示例程序:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h> #define ERR_EXIT(m) \
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}\
while (0)\ int main(void)
{
pid_t pid;
printf("before calling fork,calling process pid = %d\n",getpid());
pid = fork();
if(pid == -1)
ERR_EXIT("fork error");
if(pid == 0){
printf("this is child process and child's pid = %d,parent's pid = %d\n",getpid(),getppid());
}
if(pid > 0){
//sleep(1);
printf("this is parent process and pid =%d ,child's pid = %d\n",getpid(),pid);
} return 0;
}
运行结果:
当没给父进程没加sleep时,由于父进程先执行完,子进程成了孤儿进程,系统将其托孤给了1(init)进程,
所以ppid =1。
当加上sleep后,子进程先执行完:
这次可以正确看到想要的结果。
三,孤儿进程、僵尸进程
fork系统调用之后,父子进程将交替执行,执行顺序不定。
如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程(托孤给了init进程)。(注:任何一个进程都必须有父进程)
如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵进程(僵尸进程:只保留一些退出信息供父进程查询)
示例:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h> #define ERR_EXIT(m) \
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}\
while (0)\ int main(void)
{
pid_t pid;
printf("before calling fork,calling process pid = %d\n",getpid());
pid = fork();
if(pid == -1)
ERR_EXIT("fork error");
if(pid == 0){
printf("this is child process and child's pid = %d,parent's pid = %d\n",getpid(),getppid());
}
if(pid > 0){
sleep(100);
printf("this is parent process and pid =%d ,child's pid = %d\n",getpid(),pid);
} return 0;
}
以上程序跟前面那个基本一致,就是让父进程睡眠100秒,好让子进程先退出
运行结果:
从上可以看到,子进程先退出,但进程列表中还可以查看到子进程,[a.out] <defunct>,死的意思,即僵尸进程,如果系统中存在过多的僵尸进程,将会使得新的进程不能产生。
四,写时复制
linux系统为了提高系统性能和资源利用率,在fork出一个新进程时,系统并没有真正复制一个副本。
如果多个进程要读取它们自己的那部分资源的副本,那么复制是不必要的。
每个进程只要保存一个指向这个资源的指针就可以了。
如果一个进程要修改自己的那份资源的“副本”,那么就会复制那份资源。这就是写时复制的含义
fork 和vfork:
在fork还没实现copy on write之前。Unix设计者很关心fork之后立刻执行exec所造成的地址空间浪费,所以引入了vfork系统调用。
vfork有个限制,子进程必须立刻执行_exit或者exec函数。
即使fork实现了copy on write,效率也没有vfork高,但是我们不推荐使用vfork,因为几乎每一个vfork的实现,都或多或少存在一定的问题
vfork:
Linux Description
vfork(), just like fork(2), creates a child process of the calling pro-
cess. For details and return value and errors, see fork(2).
vfork() is a special case of clone(2). It is used to create new pro-
cesses without copying the page tables of the parent process. It may
be useful in performance-sensitive applications where a child will be
created which then immediately issues an execve(2).
vfork() differs from fork(2) in that the parent is suspended until the
child terminates (either normally, by calling _exit(2), or abnormally,
after delivery of a fatal signal), or it makes a call to execve(2).
Until that point, the child shares all memory with its parent, includ-
ing the stack. The child must not return from the current function or
call exit(3), but may call _exit(2).
Signal handlers are inherited, but not shared. Signals to the parent
arrive after the child releases the parent’s memory (i.e., after the
child terminates or calls execve(2)).
示例程序:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h> #define ERR_EXIT(m) \
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}\
while (0)\ int main(void)
{
pid_t pid;
int val = 1;
printf("before calling fork, val = %d\n",val); //pid = fork();
pid = vfork();
if(pid == -1)
ERR_EXIT("fork error");
if(pid == 0){
printf("chile process,before change val, val = %d\n",val);
val++;
//sleep(1);
printf("this is child process and val = %d\n",val);
_exit(0); }
if(pid > 0){
sleep(1);
//val++;
printf("this is parent process and val = %d\n",val);
} return 0;
}
当调用fork时:
运行结果:
可知写时复制
当使用vfork但子进程没使用exit退出时:
结果出错了,
使用vfork且exit退出:
结果正常,父子进程共享
fork之后父子进程共享文件:
fork产生的子进程与父进程相同的文件文件描述符指向相同的文件表,引用计数增加,共享文件文件偏移指针
示例程序:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h> #define ERR_EXIT(m) \
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}\
while (0)\ int main(void)
{
pid_t pid;
int fd;
fd = open("test.txt",O_WRONLY);
if(fd == -1)
ERR_EXIT("OPEN ERROR");
pid = fork();
if(pid == -1)
ERR_EXIT("fork error");
if(pid == 0){
write(fd,"child",5);
}
if(pid > 0){
//sleep(1);
write(fd,"parent",6);
} return 0;
}
运行结果:
可知父子进程共享文件偏移指针,父进程写完后文件偏移到parent后子进程开始接着写。
linux系统编程之进程(三):进程复制fork,孤儿进程,僵尸进程的更多相关文章
- linux系统编程之管道(三)
今天继续研究管道的内容,这次主要是研究一下命名管道,以及与之前学过的匿名管道的区别,话不多说,进入正题: 所以说,我们要知道命名管道的作用,可以进行毫无关系的两个进程间进行通讯,这是匿名管道所无法实现 ...
- Linux系统编程--文件描述符的复制dup()和dup2()【转】
本文转载自:http://blog.csdn.net/tennysonsky/article/details/45870459 dup() 和 dup2() 是两个非常有用的系统调用,都是用来复制一个 ...
- linux系统编程之信号(三)
今天继续对信号进行研究,话不多说,言归正传: 更多信号发送函数: 上节中我们已经接触到了一些信号的发送函数,这里更进一步学习一下其它的发送函数: alarm:只能发送SIGALRM信号 下面通过一个例 ...
- linux系统编程之进程(一)
今天起,开始学习linux系统编程中的另一个新的知识点----进程,在学习进程之前,有很多关于进程的概念需要了解,但是,概念是很枯燥的,也是让人很容易迷糊的,所以,先抛开这些抽象的概念,以实际编码来熟 ...
- Linux系统编程@进程通信(一)
进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统 ...
- linux系统编程之进程(六):父进程查询子进程的退出,wait,waitpid
本节目标: 僵进程 SIGCHLD wait waitpid 一,僵尸进程 当一个子进程先于父进程结束运行时,它与其父进程之间的关联还会保持到父进程也正常地结束运行,或者父进程调用了wait才告终止. ...
- linux系统编程-进程
进程 现实生活中 在很多的场景中的事情都是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的: 如下是一段视频,迈克杰克逊的一段视频: http://v.youku.com ...
- Linux系统编程——特殊进程之僵尸进程
僵尸进程(Zombie Process) 进程已执行结束,但进程的占用的资源未被回收.这种进程称为僵尸进程. 在每一个进程退出的时候,内核释放该进程全部的资源.包含打开的文件.占用的内存等. 可是仍然 ...
- linux shell编程,先等10秒再判断是否有进程存在,存在就再等10秒再杀了进程才运行
linux shell编程,先等10秒再判断是否有进程存在,存在就再等10秒再杀了进程才运行 crontab每分钟执行一次,但5秒以上才有更新数据,有时候一分钟可能跑不完上一个进程,需要先等10秒再判 ...
- Linux 系统编程
简介和主要概念 Linux 系统编程最突出的特点是要求系统程序员对它们工作的的系统的硬件和操作系统有深入和全面的了解,当然它们还有库和系统调用上的区别. 系统编程分为:驱动编程.用户空间编程和网络编程 ...
随机推荐
- Interface, 接口的实现初解
百度是这么说的: Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能). 两种含 ...
- day1:vcp考试
Q1. An administrator wants to provide users restricted access. The users should only be able to perf ...
- 验证二叉查找树 · Validate Binary Search Tree
[抄题]: [思维问题]: 不知道要定义resultType, 其实用仔细分析判断条件就行了:是否是bst+最大最小值 类似于平衡二叉树:是否平衡+左右的高度差 [一句话思路]: [输入量]:空: 正 ...
- php使用jquery Form ajax 提交表单,并上传文件
在html5中我们通过FormData就可以ajax上传文件数据,不过因为兼容问题.我们选用jquery.form.min.js来进行ajax的表单提交. 一.jquery.form.js下载地址 ...
- dede的cfg_keywords和cfg_description无法显示
问题:在生成html文件时,网页的keywords和description的content为空,但后台显示这两项是有值的. 解决方案: 1.设置 系统->系统基本参数->站点根网址 设 ...
- SSH三大框架需要的配置文件
1. Struts2框架 * 在web.xml中配置核心的过滤器 <filter> <filter-name>struts2</filter-name> <f ...
- 第十届Mockplus ▪ UXPA用户体验西南赛区决赛成功举行
九月的重庆,秋意渐浓. 伴随着凉爽的秋风,第十届Mockplus·UXPA国际用户体验创新大赛(UXD Award2018)西南赛区决赛于9月16日下午在四川美术学院-虎溪校区成功举办.来自西南区域各 ...
- Spring.NET学习笔记8——集合类型的注入(基础篇)
1.基础类 public class Happy { public override string ToString() { return &q ...
- 20155333 2016-2017-2 《Java程序设计》第八周学习总结
20155333 2016-2017-2 <Java程序设计>第八周学习总结 教材学习内容总结 认识NIO NIO(New IO)-from JDK1.4 Channel: 衔接数据节点( ...
- 2018.09.20 atcoder 1D Reversi(模拟)
传送门 考虑每次摆石头都会消去最外层的一个连续颜色串. 所以只用统计一下有多少段颜色即可. 代码: #include<bits/stdc++.h> using namespace std; ...