通常使用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. notes 摘自陶哲轩演讲

    摘自陶哲轩演讲http://www.youku.com/playlist_show/id_5267259.htmlA frog in a well 井底之蛙 Aristotle        亚里士多 ...

  2. 《DSP using MATLAB》Problem 7.29

    代码: %% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ %% Output In ...

  3. 修改CentOS6.5主机名引起MySQL5.6.35服务问题

    本来是心血来潮修改CentOS6.5的主机名 /****** 修改CentOS6.5默认主机名 ******/ .备份系统网络配置文件 [root@localhost ~]# cp /etc/sysc ...

  4. MATLAB---dir函数

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

  5. KOA 学习(一)

    一.安装KOA 用npm下载KOA 就会在koa文件夹下生成 二.输出hello,world 我下载的KOA版本号是2.0.1 const Koa = require('koa'); const ap ...

  6. Leetcode532.K-diff Pairs in an Array数组中的K-diff数对

    给定一个整数数组和一个整数 k, 你需要在数组里找到不同的 k-diff 数对.这里将 k-diff 数对定义为一个整数对 (i, j), 其中 i 和 j 都是数组中的数字,且两数之差的绝对值是 k ...

  7. leetcode 57 Insert Interval & leetcode 1046 Last Stone Weight & leetcode 1047 Remove All Adjacent Duplicates in String & leetcode 56 Merge Interval

    lc57 Insert Interval 仔细分析题目,发现我们只需要处理那些与插入interval重叠的interval即可,换句话说,那些end早于插入start以及start晚于插入end的in ...

  8. win10系统下安装打印机驱动

    以前安装过一次打印机的驱动,当时是从网上下载的,今天按照以前的方法安装打印机驱动,发现并不能使用,而且并不知道驱动还能自动安装. 首先在系统图标下选择设置-设备和打印机-添加打印机-搜索打印机,如果没 ...

  9. 【python之路24】装饰器

    1.装饰器的应用场景 通常IT公司的程序开发是分工的,例如某公司某个部门负责底层函数的开发,另一个部门利用其函数实现高级功能,那么如果负责底层开发的函数需要改动,一般来说不会直接在函数上进行修改,通常 ...

  10. CSS中position和header和overflow和background

    <!DOCTYPE html> <!--CSS中position属性--> <html lang="en"> <head> < ...