二十二、Linux 进程与信号---进程创建
22.1 fork 和 vfork 函数
22.1.1 函数说明
#include <unistd.h>
#include <sys/types.h>
pid_t fork( void);
- 函数说明:
- 一个现有进程可以调用fork函数创建一个新进程。
- 由fork创建的新进程被称为子进程(child process)。
- fork函数被调用一次但返回两次。
- 两次返回的唯一区别是子进程中返回 0 值而父进程中返回子进程 ID。
- 子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。
- 注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。
- UNIX将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。
- 在不同的UNIX (Like)系统下,我们无法确定fork之后是子进程先运行还是父进程先运行,这依赖于系统的实现。
- 所以在移植代码的时候我们不应该对此作出任何的假设。
- 函数返回值:
- 子进程中为 0,父进程中为子进程 ID,出错为 -1
- 子进程的继承属性
- 用户信息和权限、目录信息、信号信息、环境、共享存储段、资源限制、堆、栈和数据段,共享代码段。
- 子进程特有属性
- 进程 ID、锁信息、运行时间、未决信号
- 操作文件时的内核结构变化
- 子进程只继承父进程的文件描述表,不继承但共享文件表项和 i-node。
- 父进程创建一个子进程后,文件表项中的引用计数器加1变成2,当父进程作 close 操作后,计数器减 1,子进程还是可以使用文件表项,只有当计数器为 0 时,才会释放文件表项。
#include <unistd.h>
pid_t vfork(void);
- 函数说明
- vfork()会产生一个新的子进程,其子进程会复制父进程的数据与堆栈空间,并继承父进程的用户代码,组代码,环境变量、已打开的文件代码、工作目录和资源限制等。
- Linux 使用 copy-on-write(COW)技术,只有当其中一进程试图修改欲复制的空间时才会做真正的复制动作,由于这些继承的信息是复制而来,并非指相同的内存空间,因此子进程对这些变量的修改和父进程并不会同步。
- 此外,子进程不会继承父进程的文件锁定和未处理的信号。
- 注意,Linux不保证子进程会比父进程先执行或晚执行,因此编写程序时要留意死锁或竞争条件的发生。
- 返回值
- 如果 vfork()成功则在父进程中会返回新建立的子进程代码(PID),而在新建立的子进程中则返回 0。
- 如果 vfork 失败则直接返回-1,失败原因存于errno中。
- 错误代码
- EAGAIN 内存不足。
- ENOMEM 内存不足,无法配置核心所需的数据结构空间。
两个函数的附加说明:
- fork 创建的新进程被称为子进程,该函数被调用一次,但返回两次。两次返回的区别是:在子进程中的返回值是 0,而在父进程中的返回值则是新子进程的进程 ID。
- 创建子进程,父子进程哪个先运行根据系统调度且子进程会复制父进程的内存空间
- vfork 创建的子进程会先运行,但不会复制父进程的内存空间
22.1.2 例子
22.1.2.1 查看父子进程的运行
process_fork.c
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h> int main(void)
{
printf("pid: %d", getpid()); pid_t pid;
pid = fork();//创建子进程
//在 fork 后,会运行两个进程(父进程和子进程)
if(pid < ) {
perror("fork error");
} else if(pid > ) {
//父进程(在父进程中返回的是子进程的 pid)
//父进程执行的代码
printf("I am parent process pid is %d, ppid is %d, fork return is %d\n",
getpid(), getppid(), pid);
} else {
//子进程(在子进程中 fork 返回的是0)
//子进程执行的代码
printf("I am child process pid is %d, ppid is %d, fork return is %d\n",
getpid(), getppid(), pid);
} //这里的代码是父子进程都要执行的代码
printf("pid: %d\n", getpid());
sleep(); return ;
}

可以看见 父子进程的 pid 是不同的,父进程中通过fork 获得是子进程的ID号,子进程的父进程是102839,fork 后返回的是0
子进程的ID 和 父进程的ID 在公共代码区都打印了出来。
22.1.2.2 两进程循环启动后,各自输出内容
/* 父子进程交替运行 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> int main(void)
{
printf("current pid : %d\n", getpid()); pid_t pid = fork();
if(pid < ) {
perror("fork error");
} else if(pid > ) {
//父进程执行
int i;
for(i = ; i < ; i++) {
printf("This is parent process pid is : %d\n", getpid());
sleep();
}
} else {
//子进程
int i;
for(i = ; i < ; i++) {
printf("This is child process pid is : %d\n", getpid());
sleep();
}
} return ;
}
编译运行:

虽然设置了交替运行,但是不是真的交替运行,这个与系统调度来决定的。
22.1.2.3 子进程内存的复制

案例:
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h> int g_val = ;//全局变量,存放在数据段 int main(void)
{
int a_val = ;//局部变量,调用的时候存放在栈中
static int s_val = ;//静态变量,存放在数据段
printf("pid: %d", getpid()); pid_t pid;
pid = fork();//创建子进程
//在 fork 后,会运行两个进程(父进程和子进程)
if(pid < ) {
perror("fork error");
} else if(pid > ) {
//父进程(在父进程中返回的是子进程的 pid)
//父进程执行的代码
g_val = ;
a_val = ;
s_val = ; printf("I am parent process pid is %d, ppid is %d, fork return is %d\n",
getpid(), getppid(), pid);
printf("g_val: %p, a_val: %p, s_val: %p\n", &g_val, &a_val, &s_val);
} else {
//子进程(在子进程中 fork 返回的是0)
//子进程执行的代码
g_val = ;
a_val = ;
s_val = ;
printf("I am child process pid is %d, ppid is %d, fork return is %d\n",
getpid(), getppid(), pid);
printf("g_val: %p, a_val: %p, s_val: %p\n", &g_val, &a_val, &s_val);
} //这里的代码是父子进程都要执行的代码
printf("pid: %d, g_val: %d, a_val: %d, s_val: %d\n", getpid(), g_val, a_val, s_val);
sleep(); return ;
}

可以看见变量所在的虚拟地址都是一样的,但是输出的值却不一样。
值不一样是因为,物理空间不一样,子进程和父进程指向的物理空间不一样。数据段、堆和栈都由各自的物理空间。虚拟内存存放的数据最终都会存放在物理地址上。最终修改的也是物理内存中的内容。程序中最终输出的也是物理地址的内容。

二十二、Linux 进程与信号---进程创建的更多相关文章
- Linux学习之CentOS(二十六)--Linux磁盘管理:LVM逻辑卷的创建及使用
在上一篇随笔里面 Linux学习之CentOS(二十五)--Linux磁盘管理:LVM逻辑卷基本概念及LVM的工作原理,详细的讲解了Linux的动态磁盘管理LVM逻辑卷的基本概念以及LVM的工作原理, ...
- JAVA基础知识总结:一到二十二全部总结
>一: 一.软件开发的常识 1.什么是软件? 一系列按照特定顺序组织起来的计算机数据或者指令 常见的软件: 系统软件:Windows\Mac OS \Linux 应用软件:QQ,一系列的播放器( ...
- 智课雅思词汇---二十二、-al即是名词性后缀又是形容词后缀
智课雅思词汇---二十二.-al即是名词性后缀又是形容词后缀 一.总结 一句话总结: 后缀:-al ②[名词后缀] 1.构成抽象名词,表示行为.状况.事情 refusal 拒绝 proposal 提议 ...
- [分享] IT天空的二十二条军规
Una 发表于 2014-9-19 20:25:06 https://www.itsk.com/thread-335975-1-1.html IT天空的二十二条军规 第一条.你不是什么都会,也不是什么 ...
- Bootstrap <基础二十二>超大屏幕(Jumbotron)
Bootstrap 支持的另一个特性,超大屏幕(Jumbotron).顾名思义该组件可以增加标题的大小,并为登陆页面内容添加更多的外边距(margin).使用超大屏幕(Jumbotron)的步骤如下: ...
- Web 前端开发精华文章推荐(HTML5、CSS3、jQuery)【系列二十二】
<Web 前端开发精华文章推荐>2014年第一期(总第二十二期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML ...
- 二十二、OGNL的一些其他操作
二十二.OGNL的一些其他操作 投影 ?判断满足条件 动作类代码: ^ $ public class Demo2Action extends ActionSupport { public ...
- WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇]
原文:WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇] 在[上篇]中,我们分别站在消息交换和编程的角度介绍了SOAP Fault和FaultException异常.在服务执行过 ...
- VMware vSphere 服务器虚拟化之二十二桌面虚拟化之创建View Composer链接克隆的虚拟桌面池
VMware vSphere 服务器虚拟化之二十二桌面虚拟化之创建View Composer链接克隆的虚拟桌面池 在上一节我们创建了完整克隆的自动专有桌面池,在创建过程比较缓慢,这次我们将学习创建Vi ...
- Bootstrap入门(二十二)组件16:列表组
Bootstrap入门(二十二)组件16:列表组 列表组是灵活又强大的组件,不仅能用于显示一组简单的元素,还能用于复杂的定制的内容. 1.默认样式列表组 2.加入徽章 3.链接 4.禁用的列表组 5. ...
随机推荐
- Nginx log日志参数详解
$args #请求中的参数值$query_string #同 $args$arg_NAME #GET请求中NAME的值$is_args #如果请求中有参数,值为"?",否则为空字符 ...
- Annihilate(SA)
题目描述 黑暗之主的蜈蚣几乎可以毁灭一切,因此小正方形陷入了苦战…… 小正方形现在需要减弱黑暗之主的攻击. 一个黑暗之主的攻击可以用一个仅有小写字母的字符串表示. 现在黑暗之主向小正方形发动了若干攻击 ...
- poj1958 strange towers of hanoi
说是递推,其实也算是个DP吧. 就是4塔的汉诺塔问题. 考虑三塔:先从a挪n-1个到b,把最大的挪到c,然后再把n-1个从b挪到c,所以是 f[i] = 2 * f[i-1] + 1; 那么4塔类似: ...
- A1106. Lowest Price in Supply Chain
A supply chain is a network of retailers(零售商), distributors(经销商), and suppliers(供应商)-- everyone invo ...
- Django(三)框架之第二篇
https://www.cnblogs.com/haiyan123/p/7717788.html 一.知识点回顾 1.MTV模型 model:模型,和数据库相关的 template:模板,存放html ...
- 第二篇 - python爬取免费代理
代理的作用参考https://wenda.so.com/q/1361531401066511?src=140 免费代理很多,但也有很多不可用,所以我们可以用程序对其进行筛选.以能否访问百度为例. 1. ...
- CSUST 1011 神秘群岛 (Dijkstra+LCA)
神秘群岛 Description 小J继续着周游世界的旅程,这次他来到了一个神奇的群岛.这片群岛有n个岛屿,同时这些岛屿被标上了1-n的编号. 每个岛屿上面都有神奇的传送门,传送门可以把小J从当前 ...
- CentOS 添加新的硬盘之后不停机操作
echo "- - -" > /sys/class/scsi_host/host0/scan echo "- - -" > /sys/class/s ...
- socket编程 ------ UDP服务器
void vLANcommunication( void *pvParameters ) { int32 listenfd; do{ listenfd = socket(AF_INET, SOCK_D ...
- linux c 编程 ------ 通过设备节点调用驱动
驱动程序如下,加载驱动后,会在/dev文件夹下生成一个文件hello_device_node,是此驱动的设备节点 #include <linux/init.h> #include < ...