linux进程学习-创建新进程
init进程将系统启动后,init将成为此后所有进程的祖先,此后的进程都是直接或间接从init进程“复制”而来。完成该“复制”功能的函数有fork()和clone()等。
一个进程(父进程)调用fork()函数后将会把自己复制一份,而这个被复制出来的新进程称为子进程,就这么简单地完成了新进程的创建。fork函数几乎完整地复制了父进程,除了几个特殊的方面外(至少pid不一样吧,具体的查看这里有一个完整的列表)
fork函数原型:pid_t fork(void);
其包含在 unistd.h 头文件中,其中pid_t是表示“type of process id”的32位整数, 至于函数的返回值,取决于在哪个进程中来检测该值,如果是在新创建的进程中,其为0;如果是在父进程中(创建新进程的进程),其为新创建的进程的id; 如果创建失败,则返回负值。
#include <stdio.h>
#include <unistd.h>
int main ()
{
printf("app start...\n");
pid_t id = fork();
if (id<) {
printf("error\n");
}else if (id==) {
printf("hi, i'm in new process, my id is %d \n", getpid());
}else {
printf("hi, i'm in old process, the return value is %d\n", id);
}
return ;
}
(上面使用了getpid函数,其返回当前进程的pid。)
程序输出为:
app start...
hi, i'm in old process, the return value is 5429
hi, i'm in new process, my id is 5429
新进程被创建后将被放入“可执行队列”而进入“TASK_RUNNING”状态,并从父进程当前位置独立地运行。看下面的DEMO:
#include <stdio.h>
#include <unistd.h>
int main ()
{
printf("app start...\n");
int counter = ;
fork();
counter++;
printf("the counter value %d\n", counter);
return ;
}
输出为:
app start...
the counter value
the counter value
画个图就很能容易解释了:
新进程得到的是父进程的副本,所以,父子进程counter变量不会相互影响
fork函数将复制父进程的地址空间给子进程,但为了提高效率,复制过程并不会真正的进行物理内存的完整复制,而是采用“写拷贝(copy-on-write)”技术让父子进程尽可能地长久地共享该物理内存,仅仅是复制内存页入口地址并标记写拷贝对应的页面,当修改真正发生时才真正复制。
再来一个demo:
#include <stdio.h>
#include <unistd.h>
int main ()
{
printf("app start...");
fork();
return ;
}
输出为:
app start...app start...
好奇怪是吧?情况是这样的:
当你调用printf时,字符串被写入stdout缓冲区(还没刷到屏幕上),然后fork,子进程复制了父进程的缓冲区,所以子进程的stdout缓冲区中也包含了“app start ...”这个字符串,然后父子进程各自运行,当他们遇到return语句时,缓冲器会被强制刷新,然后就分别将“app start...”刷到了屏幕上。如果想避免,在fork前,调用fflush强制刷新下缓冲区就可以了,在字符串后面加上“\n”也可以,因为stdout是按行缓冲的。更多的,参考我的一篇博文。
与fork()函数非常类似的还有一个交vfork()的函数,它需要一点exec的知识,可以先阅读完“运行新程序”后再回头来看。
我们知道,调用fork函数后,新进程会复制父进程的内存空间并继续运行,也就是说子进程仍然运行着和父进程相同的程序代码,在大多数情况下这并非我们的本意,我们一般会fork一个新的进程,然后调用exec族函数(族函数表示由几个功能类似的函数组成的一组函数)来运行新的程序,exec族函数会用新的进程映像重写原复制过来的内存空间,比如用新程序的代码去覆盖原来代码段的内容等。很明显,fork时的复制工作白干了。所以,在这种情形下(fork后立即exec),一个聪明的做法是,fork时不复制父进程的内存空间而是共享(占用)父进程的内存空间以暂时在父进程的内存空间内运行(类似于线程),等到调用exec族函数后便拥有了自己的内存空间,然后脱离父进程独立运行,而这正是vfork所做的事情,也是vfork和fork的主要区别。
在调用exec前,由于子进程共享了父进程的内存空间,如果子进程篡改父进程数据结果将不可预期,而这是不允许的,同时,父进程会等待子进程调用exec函数后(或子进程调用_exit()退出,比如exec失败时)才继续运行。如果不按此约定编写代码则可能会引起死锁或其它不可。预期的情况。
还有一个相对较复杂的方法来创建新进程:clone()。我们知道fork()创建的进程和父进程是独立的,两者之间没有干扰,并且需要专门的“进程间通讯(IPC)”机制来进行沟通。其实,在某些情况之下我们并不希望父子进程之间显得那么独立,因为那可能带来更多的通讯成本和资源复制带来的浪费。clone()这个系统调用便允许我们选择性地继承(共享)父进程资源,比如我们共享父进程的内存空间的话,那么创造出来的新进程实际上就是一个线程了。
clone 函数:
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ... );
第1个参数是一个函数指针,表示新进程要做的工作。
第2个参数指向新进程所需的堆栈空间。
第3个参数,指示子进程如何与父进程共享资源,可选项很多,请参考这里。但一些常用的如下所示:
Ÿ CLONE_SIGHAND 子进程与父进程共享相同的信号处理(signal handler)表。
Ÿ CLONE_VM 子进程与父进程运行于相同的内存空间。
Ÿ CLONE_FILES 子进程与父进程共享相同的文件描述符(file descriptor)表
Ÿ CLONE_FS 子进程与父进程共享相同的文件系统,包括root、当前目录、umask。
Ÿ CLONE_PARENT 创建的子进程的父进程是调用者的父进程,新进程与创建它的进程成了“兄弟”而不是“父子”
另外,为了更好的理解fork()和clone()的关系:
fork的实现:
do_fork(CLONE_SIGCHLD,...)
clone的实现:
do_fork(CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGCHLD,...)
linux进程学习-创建新进程的更多相关文章
- Linux内核分析— —创建新进程的过程
分析Linux内核创建一个新进程的过程 实验过程 要求:使用gdb跟踪分析一个fork系统调用内核处理函数sys_clone ,验证对Linux系统创建一个新进程的理解,推荐在实验楼Linux虚拟机环 ...
- Linux内核分析-创建新进程的过程
分析Linux内核创建一个新进程的过程 task_struct结构体分析 struct task_struct{ volatile long state; //进程的状态 unsigned long ...
- Linux内核学习笔记-2.进程管理
原创文章,转载请注明:Linux内核学习笔记-2.进程管理) By Lucio.Yang 部分内容来自:Linux Kernel Development(Third Edition),Robert L ...
- Linux内核学习笔记二——进程
Linux内核学习笔记二——进程 一 进程与线程 进程就是处于执行期的程序,包含了独立地址空间,多个执行线程等资源. 线程是进程中活动的对象,每个线程都拥有独立的程序计数器.进程栈和一组进程寄存器 ...
- Linux下SVN创建新的项目
Linux下SVN创建新的项目 Linux环境下的SVN创建新的项目 一.前置条件: 1)有安装了linux系统的服务器,123.*.*.29 2)服务器上安装了svn,本人服务器的svn的数据安 ...
- Linux下如何创建新用户
Linux下如何创建新用户 Linux系统中,只有root用户有创建其他用户的权限.创建过程如下: useradd -d /home/newuser newuser(设定了该用户的主目录和用户名) ...
- python并发编程02 /多进程、进程的创建、进程PID、join方法、进程对象属性、守护进程
python并发编程02 /多进程.进程的创建.进程PID.join方法.进程对象属性.守护进程 目录 python并发编程02 /多进程.进程的创建.进程PID.join方法.进程对象属性.守护进程 ...
- ATMS中去拉起新的进程,并在新进程启动后调用attachApplication时,resume待启动的Activity
相关文章: ATMS中去pause Activity A. 目录 ATMS拉起新进程 堆栈 resumeTopActivityInnerLocked:1684, ActivityStack start ...
- Linux系统基于fork()新进程的创建
作者:严哲璟 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 fork属于系 ...
随机推荐
- mysql数据库中表记录的玩法
一.增加表记录(相当于插入表记录) 1. 插入完整数据(顺序插入) 语法一: INSERT INTO 表名(字段1,字段2,字段3…字段n) VALUES(值1,值2,值3…值n); 语法二: INS ...
- windows下客户端开发hdf--环境搭建
1.引入依赖 <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop- ...
- [原创]spring及springmvc精简版--IOC
本篇博客为自己学习spring和springmvc的一个总结.主要以代码为主,至于文字性描述理解性东西,可以自行百度.有认识不妥的地方,还望指出,相互学习. 以前很困惑spring中的一些概念,在学习 ...
- 自己的第一个MapReduce程序
数据源:来自互联网招聘hadoop岗位的薪资数据,其中几行示例数据如下: 美团 3-5年经验 15-30k 北京 [够牛就来]hadoop高级工程... 北信源 3-5年经验 15-20k 北京 Ja ...
- 20145231《Java程序设计》第四次实验报告
实验四 Android开发基础 实验内容 •安装Android Studio •运行安卓AVD模拟器 •使用Android运行出模拟手机并显示自己的学号 实验步骤 一.安装Android Studio ...
- INSPIRED启示录 读书笔记 - 第15章 特约用户
产品开发伙伴 为了解决两个问题——既深入洞察目标用户的需求,又赢得用户对产品的推荐,建议征集特约用户协助完成产品研发 在项目的开始阶段物色至少六位积极.活跃.乐于分享的目标户,要求是他们在产品的目标用 ...
- 百度竞价推广URL通配符使用说明
{keywordid} 被替换为触发该创意的关键词ID(全局唯一ID,不是字面ID),当没有对应的keywordid时,替换为0. {creative} 被替换为所点击的创意ID(全局唯一ID). 2 ...
- Mysql -- You can't specify target table 'address' for update in FROM clause
做地址管理时,需要先根据要设为默认的地址的用户将用户的其他地址都设置为非默认 需要select出用户id然后update 原语句 update address set isdeafult = 0 wh ...
- Nuxt / Vue.js in TypeScript: Object literal may only specify known properties, but 'components' does not exist in type 'VueClass'
项目背景, Nuxt(vue), TypeScript 生成完项目框架, 添加测试demo页面. 在生成的模板代码中添加layout配置如下: <script lang="ts&quo ...
- 集群环境ssh免密码登录设置
一.准备工作 1) 用客户端工具(ssh client或者putty)连接到linux服务器.在root用户下输入命令 vi /etc/hosts,用vi编辑hosts文件,如下: #127.0.0. ...