Linux20源码剖析(一)之fork

fork剖析

在这里插入图片描述

1. fork 的实现原理

fork 系统调用是Unix和类Unix系统中创建新进程的主要方式。它通过复制当前进程(父进程)来创建一个几乎完全相同的新进程(子进程)。子进程继承了父进程的大部分属性,如文件描述符、环境变量、当前工作目录等,但它们是两个独立的进程,拥有独立的进程ID。在Linux系统中, fork 的实现主要依赖于 clone 系统调用和写时复制(Copy-On-Write, COW)技术。写时复制(COW)写时复制是一种高效的内存管理技术,它允许父进程和子进程共享相同的物理内存页面,直到其中一个进程尝试修改页面内容。当发生写操作时,操作系统会为修改的进程创建一个新的内存页面副本,从而保证两个进程的内存空间独立。这种技术大大减少了 fork 操作的开销,特别是在创建大量子进程时。

2. fork 的源码剖析

在Linux内核中, fork 的实现主要涉及以下几个关键函数:

2.1 do_fork 函数

do_fork 是 fork 系统调用的内核入口点。它负责处理 fork 的大部分逻辑,包括参数解析、进程创建和资源分配。

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;
    int trace = 0;
    long nr;

    // 检查是否需要跟踪
    if (unlikely(current->ptrace & PT_TRACE_FORK)) {
        trace = 1;
        if (ptrace_event_enabled(current, PTRACE_EVENT_FORK)) {
            if (clone_flags & CLONE_VFORK)
                ptrace_event_notify(PTRACE_EVENT_VFORK);
            else
                ptrace_event_notify(PTRACE_EVENT_FORK);
        }
    }

    // 调用copy_process创建新进程
    p = copy_process(clone_flags, stack_start, regs, stack_size,
                     parent_tidptr, child_tidptr, trace);
    if (!IS_ERR(p)) {
        nr = task_pid_vnr(p);
        wake_up_new_task(p);
    } else {
        nr = PTR_ERR(p);
    }

    return nr;
}

2.2 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 *parent_tidptr,
                                        int __user *child_tidptr,
                                        int trace)
{
    struct task_struct *p;
    int retval;

    // 分配新的任务结构
    p = alloc_task_struct();
    if (!p)
        return ERR_PTR(-ENOMEM);

    // 初始化任务结构
    retval = init_task(p, clone_flags, stack_start, stack_size);
    if (retval < 0)
        goto fork_out;

    // 复制父进程的上下文信息
    retval = copy_thread(clone_flags, stack_start, stack_size, regs, p);
    if (retval < 0)
        goto fork_out;

    // 复制父进程的内存空间
    retval = copy_mm(clone_flags, p);
    if (retval < 0)
        goto fork_out;

    // 复制父进程的文件描述符
    retval = copy_files(clone_flags, p);
    if (retval < 0)
        goto fork_out;

    // 复制父进程的信号处理信息
    retval = copy_signal(clone_flags, p);
    if (retval < 0)
        goto fork_out;

    // 复制父进程的命名空间
    retval = copy_namespaces(clone_flags, p);
    if (retval < 0)
        goto fork_out;

    // 复制父进程的进程ID
    retval = copy_pid(clone_flags, p);
    if (retval < 0)
        goto fork_out;

    // 设置子进程的父进程
    p->real_parent = current;
    p->parent = current;

    // 设置子进程的PID
    p->pid = get_pid(clone_flags);

    // 设置子进程的线程ID
    p->tgid = get_tgid(clone_flags);

    // 设置子进程的用户ID和组ID
    p->uid = current->uid;
    p->gid = current->gid;

    // 设置子进程的线程组ID
    p->tgid = current->tgid;

    // 设置子进程的线程组头
    p->group_leader = current->group_leader;

    // 设置子进程的线程组锁
    p->group_lock = current->group_lock;

    // 设置子进程的线程组信号
    p->group_signal = current->group_signal;

    // 设置子进程的线程组信号锁
    p->group_siglock = current->group_siglock;

    // 设置子进程的线程组信号队列
    p->group_sigqueue = current->group_sigqueue;

    // 设置子进程的线程组信号队列锁
    p->group_sigqueue_lock = current->group_sigqueue_lock;

    // 设置子进程的线程组信号队列头
    p->group_sigqueue_head = current->group_sigqueue_head;

    // 设置子进程的线程组信号队列尾
    p->group_sigqueue_tail = current->group_sigqueue_tail;

    // 设置子进程的线程组信号队列长度
    p->group_sigqueue_len = current->group_sigqueue_len;

    // 设置子进程的线程组信号队列最大长度
    p->group_sigqueue_max_len = current->group_sigqueue_max_len;

    // 设置子进程的线程组信号队列最大长度锁
    p->group_sigqueue_max_len_lock = current->group_sigqueue_max_len_lock;

    // 设置子进程的线程组信号队列最大长度头
    p->group_sigqueue_max_len_head = current->group_sigqueue_max_len_head;

    // 设置子进程的线程组信号队列最大长度尾
    p->group_sigqueue_max_len_tail = current

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我的sun&shine

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值