Linux通过clone()系统调用实现fork()、vfork()和__clone()库函数创建新的进程,这个调用通过一系列的参数标志来指明父子进程的共享资源,终将各自的参数标志位传递给clone,由clone()去调用do_fork()来实现创建新的进程的目的。

do_fork的实现源码在kernel/fork.c文件中,其主要的作用就是复制原来的进程成为另一个新的进程,它完成了整个进程的创建过程。do_fork()的实现主要由以下5个步骤,在分析代码之前,先了解以下do_fork()函数的参数的含义,其参数的含义如下。

clone_flags:该标志位的4个字节分为两部分。低的一个字节为子进程结束时发送给父进程的信号代码,通常为SIGCHLD;剩余的三个字节则是各种clone标志的组合。通过clone标志可以有选择的对父进程的资源进行复制。例如CLONE_VM表示共享内存描述符合所有的页表; CLONE_FS共享根目录和当前工作目录所在的表以及权限掩码。

statck_start:子进程用户态堆栈的地址;

regs:指向pt_regs结构体的指针。当系统发生系统调用,即用户进程从用户态切换到内核态时,该结构体保存通用寄存器中的值,并被存放于内核态的堆栈中;

stack_size:未被使用,通常被赋值为0;

parent_tidptr:父进程在用户态下pid的地址,该参数在CLONE_PARENT_SETTID标志被设定时有意义;

child_tidptr:子进程在用户态下pid的地址,该参数在CLONE_CHILD_SETTID标志被设定时有意义。

函数原型及实现为:

long do_fork(unsigned long clone_flags,unsigned long stack_start, struct pt_regs *regs,unsigned long stack_size,int __user *parent_tidptr, int __user *child_tidptr)

{

struct task_struct *p;

p = copy_process(clone_flags, stack_start, regs, stack_size,

child_tidptr, NULL, trace); (1)

if (!IS_ERR(p)) {

if (clone_flags & CLONE_VFORK) {

p->vfork_done = &vfork;

init_completion(&vfork);

}

……

}

一、首先调用copy_process()函数

copy_process()函数实现了进程的大部分拷贝工作。

static struct task_struct *copy_process(unsigned long clone_flags, unsigned long stack_start,struct pt_regs *regs,unsigned long stack_size, int __user *child_tidptr, struct pid *pid,int trace)

{

//对传入的clone_flag进行检查

//为新进程创建一个内核栈、thread_info结构和task_struct;其值域当前进程的值完全相同(父子进程的描述符此时也相同)

p = dup_task_struct(current);

//判断是否超出进城用户可以拥有的总进城数量,检查是否有权对指定的资源进行操作

if (atomic_read(&p->real_cred->user->processes) >=

task_rlimit(p, RLIMIT_NPROC)) {

if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE) &&

p->real_cred->user != INIT_USER)

goto bad_fork_free;

}

//在task_struct结构中有一个指针user,该指针指向一个user_struct结构,一个用户的多个进程可以通过user指针共享该用户的资源信息,该结构定义在include/linux/sched.h中,

retval = copy_creds(p, clone_flags);

//copy_creds函数中调用:

//检查创建的进程是否超过了系统进程总量

if (nr_threads >= max_threads)

goto bad_fork_cleanup_count;

//获得进程执行域

if (!try_module_get(task_thread_info(p)->exec_domain->module))

goto bad_fork_cleanup_count;

//调用copy_flags函数更新task_struct结构中flags成员。表明进程是否拥有超级用户权限的PF_SUPERPPRIV标志被清除,表明进程还没有exec()的PF_FORKNOEXEC被设置

copy_flags(clone_flags, p);

//根据clone的参数标志,拷贝或共享打开的文件、文件系统信息、信号处理函数、进程地址空间和命名空间,代码如下图所示。

//为新进程获取一个有效的PID,调用pid = alloc_pidmap();紧接着使用alloc_pidmap函数为这个新进程分配一个pid。由于系统内的pid是循环使用的,所以采用位图方式来管理,用每一位(bit)来标示该位所对应的pid是否被使用。分配完毕后,判断pid是否分配成功。

pid = alloc_pid(p->nsproxy->pid_ns);

//父子进程平分共享的时间片

sched_fork(p, clone_flags);

//返回子进程的指针。

return p;

}

再回到do_fork函数,如果copy_process函数成功返回,新创建的子进程被唤醒并投入运行。内核有意选择子进程首先执行。因为一般子进程都会马上调用exec函数,这样可以避免写时拷贝的额外开销,如果父进程首先执行的话,有可能开始向地址空间写入。

二、init_completion(&vfork);

如果clone_flags包含CLONE_VFORK标志,那么将进程描述符中的vfork_done字段指向这个完成量,之后再对vfork完成量进行初始化。完成量的作用是,直到任务A发出信号通知任务B发生了某个特定事件时,任务B才会开始执行;否则任务B一直等待。我们知道,如果使用vfork系统调用来创建子进程,那么必然是子进程先执行。究其原因就是此处vfork完成量所起到的作用:当子进程调用exec函数或退出时就向父进程发出信号。此时,父进程才会被唤醒;否则一直等待。此处的代码只是对完成量进行初始化,具体的阻塞语句则在后面的代码中有所体现。

三、检查子进程是否设置了CLONE_STOPPED标志。

设置了CLONE_STOPPED标志通过sigaddset函数为子进程增加挂起信号。signal对应一个unsigned long类型的变量,该变量的每个位分别对应一种信号。具体的操作是,将SIGSTOP信号所对应的那一位置1。

如果子进程并未设置CLONE_STOPPED标志,那么通过wake_up_new_task函数使得父子进程之一优先运行;否则,将子进程的状态设置为TASK_STOPPED。

四、检查CLONE_VFORK标志被设置

如果CLONE_VFORK标志被设置,则通过wait操作将父进程阻塞,直至子进程调用exec函数或者退出。

五、返回pid

return nr; //其中nr后一次赋值为:nr = task_pid_vnr(p);即子进程的pid号。

至此,fork函数的系统调用过程结束,子进程和父进程各返回一次,子进程返回值为0,父进程返回值为子进程的pid号。应用程序可通过fork的返回值来判断是在子进程中还是父进程中,从而实现多进程程序的编写。

Linux多进程拷贝fork,浅析linux中fork函数相关推荐

  1. 浅析 JavaScript 中的 函数 uncurrying 反柯里化

    柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...

  2. linux+c+时间间隔+ns,浅析 Linux 中的时间编程和实现原理,第 1 部分: Linux 应用层的时间编程...

    http://www.ibm.com/developerworks/cn/linux/1307_liuming_linuxtime1/index.html#ibm-pcon 本文试图完整地描述 Lin ...

  3. linux 进程通信比较,浅析Linux进程通信的几种方式及其比较

    摘要:本文通过对Linux下几种主要通信方式进行概述,并结合Linux系统的系统调用对OS中的实现进行简要概述,并对其优缺点进行分析,阐述了在不同通信情况下应该选择何种通信方式进行选择. 关键词:Li ...

  4. linux下mkdir头文件_Linux中mkdir函数与Windows中_mkdir函数的区别

    下面先来给大家介绍windows下_mkdir函数 #include int _mkdir( const char *dirname ); 参数: dirname是目录的路径名指针 返回值: 如果新目 ...

  5. python中int函数的用法浅析_Python中int()函数的用法浅析

    int()是Python的一个内部函数 Python系统帮助里面是这么说的 >>> help(int) Help on class int in module __builtin__ ...

  6. java js中 function函数报错_浅析JS中对函数function的理解(基础篇)

    正文:我们知道,在js中,函数实际上是一个对象,每个函数都是Function类型的实例,并且都与其他引用类型一样具有属性和方法.因此,函数名实际上是指向函数对象的指针,不与某个函数绑定.在常见的两种定 ...

  7. Linux多进程拷贝文件

    学习了mmap以后,实现一个简单的小程序,进行多个进程对一个文件进行拷贝. Linux mmap共享内存学习可以参考我的另一篇博客:传送门 实现思想 我们可以将原来的文件利用mmap分成多个段分别进行 ...

  8. linux多进程server 进程池_Python 中的进程池与多进程

    封面图片来源:沙沙野 内容概览 进程池 进程池和多进程的性能测试 进程池的其他机制 进程池的回调函数 进程池 如果有多少个任务,就开启多少个进程,实际上并不划算.由于计算机的 cpu 个数是非常有限的 ...

  9. linux内核声卡管理,浅析linux 2.6.30.4内核中uda134x声卡驱动源码 - audio和bluetooth

    文件:sound/soc/s3c24xx/s3c24xx_uda134x.c static int __init s3c24xx_uda134x_init(void) { return platfor ...

最新文章

  1. Gut-2018-早期肝癌肠道生物标志物鉴定
  2. windows10中 git 本地仓库的使用
  3. 英文linux学习app,Linux应用软件,Linux Application Software,音标,读音,翻译,英文例句,英语词典...
  4. 独家 | Python中的SOLID原则(附链接)
  5. 评论:索尼爱立信能否重振雄风?
  6. chrome+android+浏览器下载图片不显示,chrome谷歌浏览器部分图片不显示怎么办
  7. 苹果将允许iPhone直接使用NFC接受信用卡付款
  8. 一个转行学习前端的初学者,应该如何计划自己的学习规划?
  9. codeforce #164 div2
  10. kong插件应用(熔断 限流,黑白名单,认证(basic,key,jwt,hmac,),授权,加密,zipkin链路跟踪,日志, prometheus可视化, 爬虫控制插件)
  11. Visual Studio常用的快捷键
  12. nvme驱动架构分析1
  13. 拿什么拯救你,我的头发丝--PS抠图神器
  14. R语言数据分析及可视化实战
  15. newsgroup_txt
  16. 使用蓝牙模块和笔记本自带蓝牙通讯
  17. 项目3-商城-1-注册登录首页
  18. 关于麒麟!关于互联网
  19. 小程序 实现两种导航功能
  20. ESP32 报错is installed, but the tool failed to run

热门文章

  1. 给定一个32位有符号整数,将整数中的数字进行翻转
  2. 记一次laravel-jwt修改黑名单所用redis数据库
  3. Open vSwitch系列实验(一):Open vSwitch使用案例扩展实验
  4. linux文件管理和 对bash的理解
  5. 搭建Python+Eclipse开发环境
  6. 情感分析之电影评论分析-基于Tensorflow的LSTM
  7. DotnetSpider (二) Downloader的设置 Request自定义数据字典
  8. 重载VerifyRenderingInServerForm
  9. file input 点击没反应_动态input file多文件上传到后台没反应的解决方法!!!
  10. redis 使用管道提升写入的性能[pipeline]