通常使用fork创建进程, 也可以用vfork()和clone()。
fork、vfork和clone三个用户态函数均由libc库提供,它们分别会调用Linux内核提供的同名系统调用fork,vfork和clone。
vfork与fork的区别在于创建进程时, vfork完全共享父进程的地址空间,包括页表项。vfork在早期用来替代fork以避免复制地址空间的耗时操作,
但是现在fork已经使用了写时复制技术,在进程创建时,只复制父进程的页表项,用只读的方式共享父进程地址空间,在写入时再复制数据。
fork有了这个优化之后, vfork已较少使用。

fork调用链:
| libc::fork() | --->int$0×80软中断--->| kernel: ENTRY(system_call)--->ENTRY(sys_call_table)--->sys_fork()--->do_fork() |

//----------------------------------------- file: process.c --------------------------------------------

asmlinkage long sys_fork(void)
{
struct pt_regs *regs = task_pt_regs(current);
return do_fork(SIGCHLD, regs->gprs[15], regs, 0, NULL, NULL);
}
//----------------------------------------- file: fork.c -------------------------------------------------

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;
long nr;
...
p = copy_process(clone_flags, stack_start, regs, stack_size,
child_tidptr, NULL); //最核心的工作:通过copy_process()创建子进程的描述符,分配pid等
if (!IS_ERR(p)) {
...
if (!(clone_flags & CLONE_STOPPED))
wake_up_new_task(p, clone_flags); //将进程放到运行队列上,从而让调度器进行调度运行
else
p->state = TASK_STOPPED;
...
} else {
nr = PTR_ERR(p);
}
return nr;
}
//----------------------------------------- file: fork.c -------------------------------------------------

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 retval;
struct task_struct *p;
int cgroup_callbacks_done = 0; //...
//检查clone_flags
//...
p = dup_task_struct(current); //创建内核栈, thread_info, task_struct, 里面的值是完全复制的父进程的值
if (!p)
goto fork_out; ... /*
* If multiple threads are within copy_process(), then this check
* triggers too late. This doesn't hurt, the check is only there
* to stop root fork bombs.
*/
     //检查当前进程总数是否超出最大进程数
if (nr_threads >= max_threads)
goto bad_fork_cleanup_count; //...
//复制、更新子进程描述符的flags成员,
copy_flags(clone_flags, p); //...
//初始化子进程描述符的各个字段,很多被清零,使得子进程和父进程逐渐区别出来。 不过大部分数据仍然未被修改
//... //调度器设置。调用sched_fork函数执行调度器相关的设置
/* Perform scheduler related setup. Assign this task to a CPU. */
sched_fork(p, clone_flags); //-------------------------start 根据clone_flags的具体取值来为子进程拷贝或共享父进程的某些数据结构--------//
if ((retval = security_task_alloc(p)))
goto bad_fork_cleanup_policy;
if ((retval = audit_alloc(p)))
goto bad_fork_cleanup_security;
/* copy all the process information */
if ((retval = copy_semundo(clone_flags, p)))
goto bad_fork_cleanup_audit;
if ((retval = copy_files(clone_flags, p))) //打开的文件信息
goto bad_fork_cleanup_semundo;
if ((retval = copy_fs(clone_flags, p))) //所在文件系统信息
goto bad_fork_cleanup_files;
if ((retval = copy_sighand(clone_flags, p)))
goto bad_fork_cleanup_fs;
if ((retval = copy_signal(clone_flags, p))) //信号处理函数
goto bad_fork_cleanup_sighand;
if ((retval = copy_mm(clone_flags, p))) //地址空间信息
goto bad_fork_cleanup_signal;
if ((retval = copy_keys(clone_flags, p)))
goto bad_fork_cleanup_mm;
if ((retval = copy_namespaces(clone_flags, p))) //命名空间
goto bad_fork_cleanup_keys;
//复制线程。通过copy_threads()函数更新子进程的内核栈和寄存器中的值
retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
if (retval)
goto bad_fork_cleanup_namespaces; //-----------------------------------------end -------------------------------------------------// //分配pid
if (pid != &init_struct_pid) {
retval = -ENOMEM;
pid = alloc_pid(task_active_pid_ns(p));
if (!pid)
goto bad_fork_cleanup_namespaces; if (clone_flags & CLONE_NEWPID) {
retval = pid_ns_prepare_proc(task_active_pid_ns(p));
if (retval < 0)
goto bad_fork_free_pid;
}
} p->pid = pid_nr(pid);
p->tgid = p->pid;
if (clone_flags & CLONE_THREAD)
p->tgid = current->tgid; p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; ...
//更改p的其他一些字段
... if (likely(p->pid)) {
add_parent(p);
if (unlikely(p->ptrace & PT_PTRACED))
__ptrace_link(p, current->parent); if (thread_group_leader(p)) {
if (clone_flags & CLONE_NEWPID)
p->nsproxy->pid_ns->child_reaper = p; p->signal->tty = current->signal->tty;
set_task_pgrp(p, task_pgrp_nr(current));
set_task_session(p, task_session_nr(current));
attach_pid(p, PIDTYPE_PGID, task_pgrp(current));
attach_pid(p, PIDTYPE_SID, task_session(current));
list_add_tail_rcu(&p->tasks, &init_task.tasks);
__get_cpu_var(process_counts)++;
}
attach_pid(p, PIDTYPE_PID, pid);
nr_threads++;
} total_forks++;
spin_unlock(&current->sighand->siglock);
write_unlock_irq(&tasklist_lock);
proc_fork_connector(p);
cgroup_post_fork(p);
return p; //...
//错误处理
//...
}

看到这里应该可以知道刚开始提出的第一个问题, 子进程是否能使用父进程的文件描述符?
    答案是肯定的,因为task_struct里面成员struct files_struct *files 保存了所有进程打开的文件描述符。
创建子进程的时候,会将files复制一份到子进程files结构体里, 里面的值跟父进程的值是一样的,可以用来操作父进程打开的文件。
如下面代码里的:newf = dup_fd(oldf, &error);
如果调用do_fork的时候传入clone_flags: CLONE_FILES, 那么子进程直接共享父进程files成员,不会复制, 这时候当然更可以用来操作父进程的文件。
线程就是这么做的。

static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
{
struct files_struct *oldf, *newf;
int error = 0; /*
* A background process may not have any files ...
*/
oldf = current->files;
if (!oldf)
goto out; if (clone_flags & CLONE_FILES) {
atomic_inc(&oldf->count);
goto out;
} /*
* Note: we may be using current for both targets (See exec.c)
* This works because we cache current->files (old) as oldf. Don't
* break this.
*/
tsk->files = NULL;
newf = dup_fd(oldf, &error);
if (!newf)
goto out; tsk->files = newf;
error = 0;
out:
return error;
}

子进程复制父进程的所有文件描述符:

Linux进程管理(二、 进程创建)的更多相关文章

  1. Linux进程管理(二)

    目录 Linux进程管理(二) 参考 vmstat命令 top命令 Linux进程管理(二)

  2. Linux进程管理 (1)进程的诞生

    专题:Linux进程管理专题 目录: Linux进程管理 (1)进程的诞生 Linux进程管理 (2)CFS调度器 Linux进程管理 (3)SMP负载均衡 Linux进程管理 (4)HMP调度器 L ...

  3. Linux 内核进程管理之进程ID 。图解

    http://www.cnblogs.com/hazir/tag/kernel/ Linux 内核进程管理之进程ID   Linux 内核使用 task_struct 数据结构来关联所有与进程有关的数 ...

  4. linux进程管理之进程创建

    所谓进程就是程序执行时的一个实例. 它是现代操作系统中一个很重要的抽象,我们从进程的生命周期:创建,执行,消亡来分析一下Linux上的进程管理实现. 一:前言 进程管理结构; 在内核中,每一个进程对应 ...

  5. linux进程管理之进程创建(三)

    在linux系统中,许多进程在诞生之初都与其父进程共同用一个存储空间.但是子进程又可以建立自己的存储空间,并与父进程“分道扬镳”,成为与父进程一样真正意义上的进程. linux系统运行的第一个进程是在 ...

  6. Linux 内核进程管理之进程ID

    Linux 内核使用 task_struct 数据结构来关联所有与进程有关的数据和结构,Linux 内核所有涉及到进程和程序的所有算法都是围绕该数据结构建立的,是内核中最重要的数据结构之一.该数据结构 ...

  7. Linux内存管理 一个进程究竟占用多少空间?-VSS/RSS/PSS/USS

    关键词:VSS.RSS.PSS.USS._mapcount.pte_present.mem_size_stats. 在Linux里面,一个进程占用的内存有不同种说法,可以是VSS/RSS/PSS/US ...

  8. Linux 内核进程管理之进程ID【转】

    转自:http://www.cnblogs.com/hazir/p/linux_kernel_pid.html Linux 内核使用 task_struct 数据结构来关联所有与进程有关的数据和结构, ...

  9. 【Android手机测试】linux内存管理 -- 一个进程占多少内存?四种计算方法:VSS/RSS/PSS/USS

    在Linux里面,一个进程占用的内存有不同种说法,可以是VSS/RSS/PSS/USS四种形式,这四种形式首字母分别是Virtual/Resident/Proportional/Unique的意思. ...

  10. linux进程篇 (二) 进程的基本控制

    2. 进程的基本操作 接口函数 #include <unistd.h> //创建子进程 pid_t fork(void); //结束子进程 void exit(int status); / ...

随机推荐

  1. NSIS语法解析

    注释.!define.变量.!include.常量 ; Script generated by the HM NIS Edit Script Wizard. ; HM NIS Edit Wizard ...

  2. megacli在线raid构建详解(转载自用)

    版权声明:本文为博主原创文章,未经博主允许不得转载,转载附上原文链接即可. https://blog.csdn.net/GX_1_11_real/article/details/83213959 ht ...

  3. IDEA快速定位错误快捷键

  4. 装配SpringBean(六)--配置文件加载方式

    spring中的配置文件有两种: 以XML结尾的spring配置文件 以properties结尾的属性配置文件 在spring中有两种方式加载这两种文件: 通过注解+java配置的方式 通过XML的方 ...

  5. Delphi代码规范

    1. 前言 本文档主要是为Delphi开发人员提供一个源代码书写标准,以及程序和文件的命名标准,使他们在编程时有一致格式可遵循.这样,每个编程人员编写的代码能够被其他人理解. 2. 源程序书写规范 2 ...

  6. spring boot项目启动报DataSource错误

    初建一个简单的spring boot 项目,启动后会报错. Exception encountered during context initialization - cancelling refre ...

  7. JasperReports报表表达式12

    报表表达式是JasperReports使我们能够显示在报表上的数据计算的强大功能.计算出数据不是一个静态数据,并且不受特别的报表参数或数据源字段传递的数据.报表表达式是由组合报表参数,字段和静态数据. ...

  8. consul原理

    阅读目录 一.使用Consul做服务发现的若干姿势 1.https://www.cnblogs.com/bossma/p/9756809.html 阅读目录 启动第1个Server节点,集群要求要有3 ...

  9. win10 基础上装一个 ubuntu 双系统

    1.准备一个空闲的分区: 1)确定每个磁盘都已经是 EFI 分区格式,如果不是,可以使用分区工具,将分区都转成EFI  (例如 DiskGenius 工具挺好) 2)选择一个剩余空间较大的压缩空间,压 ...

  10. CesiumLab V1.4 分类3dtiles生成(倾斜单体化、楼层房间交互)我记得我是写过一篇关于倾斜单体化的简书文章的,但是现在找不到了。不过找不到也好,就让他随风逝去吧,因为当时我写那篇文章的时候,就发现了cesium实际是有另一种更高效的单体化。就下面这个示例https://cesiumjs.org/Cesium/Build/Apps/Sandcastle/index.html?src=

    我记得我是写过一篇关于倾斜单体化的简书文章的,但是现在找不到了.不过找不到也好,就让他随风逝去吧,因为当时我写那篇文章的时候,就发现了cesium实际是有另一种更高效的单体化.就下面这个示例 http ...