通常使用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. eclipse环境问题-java版本不兼容

    有时候虽然我们给项目配置的jdk版本.项目编译版本都一直,但是还是会报如下的错误: Description Resource Path Location Type Java compiler leve ...

  2. MATLAB---dir函数

    dir函数是最常用的转换路径的函数,可以获得指定文件夹下的所有子文件夹和文件,并存放在一个文件结构的数组中,这个数组各结构体内容如下: name    -- 文件名 date    -- 修改日期 b ...

  3. 在VS中编译Opencascade6.6.0

    话说,OpenCASCADE团队真的很给力,版本更新速度也是嗖嗖地.依稀记得上次编译OCC,那时候的第三方库.OCC本身几何引擎库,全都得自己编译. 于是,编译过程之艰苦也就可想而知了.最近重换系统, ...

  4. GUID 使用方法

    GUID(全局统一标识符)是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的.通常平台会提供生成GUID的API.生成算法很有意思,用到了以太网卡地址.纳秒级时间.芯片ID码和许多可 ...

  5. Leetcode179. Largest Number最大数

    给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数. 示例 1: 输入: [10,2] 输出: 210 示例 2: 输入: [3,30,34,5,9] 输出: 9534330 说明: 输出结果 ...

  6. spring源码学习之AOP(一)

    继续源码学习,看了spring中基础的容器和AOP感觉自己也没有什么长进,哈哈,我也不知道到底有用没有,这可能是培养自己的一种精神吧,不管那么多,继续学习!AOP中 AOP中几个重要的概念:(1)Ad ...

  7. 斐波那契字符串_KMP

    前言:通过这道题恶补了一下字符串匹配的知识 思路:首先就是求出菲波那切字符串,这个很简单,但是要注意递归超时的问题,可以考虑加上备忘录,或者用递推法,接下来就是匹配问题了,常规的BF会超时,所以要用K ...

  8. Codeforces Round #573 (Div. 2)

    A:Tokitsukaze and Enhancement 当时看错条件了..以为A>C>B>D.就胡写了判断条件. #include<bits/stdc++.h> us ...

  9. springmvc 传参Required String parameter 'xxxx' is not present

    报错 请求因该是已经被分配了,但是参数补全,无法被执行 加上这个参数就好了,表示请求参数,可以为空 这样的好处是,可以进入controller之后再去判断,比较好定位错误

  10. Twitter web information

    http://developer.51cto.com/art/201307/404612.htm 150M active users 300K Qps (read, only 6000 write/s ...