• 进程管理1——进程和线程的差异 Hello


    一、创建传参差异

    1. 线程创建

    /*
    pthread_create //glibc
        __pthread_create_2_1 
            create_thread
                do_clone
                    clone //系统调用
    */
    static int create_thread (struct pthread *pd, ...)
    {
        /* x86上的传参,非Arm64的 */
        int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL
                | CLONE_SETTLS | CLONE_PARENT_SETTID
                | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM
                | 0);
    
        int ret = do_clone (pd, attr, clone_flags, start_thread, STACK_VARIABLES_ARGS, 1);
        ...
    }
    
    SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
        int __user *, parent_tidptr, unsigned long, tls, int __user *, child_tidptr)
    {
        struct kernel_clone_args args = {
            .flags        = (lower_32_bits(clone_flags) & ~CSIGNAL),
            .pidfd        = parent_tidptr,
            .child_tid    = child_tidptr,
            .parent_tid    = parent_tidptr,
            .exit_signal    = (lower_32_bits(clone_flags) & CSIGNAL),
            .stack        = newsp,
            .tls        = tls,
        };
    
        return kernel_clone(&args);
    }         

    调用 pthread_create()创建线程时的标记有 CLONE_VM、CLONE_FS、CLONE_FILES、CLONE_SIGNAL、CLONE_SETTLS、CLONE_PARENT_SETTID、CLONE_CHILD_CLEARTID、CLONE_SYSVSEM。

    2. 进程创建

    SYSCALL_DEFINE0(fork) //kernel/fork.c
    {
        struct kernel_clone_args args = {
            .exit_signal = SIGCHLD, //.flags 一个标志也没有设置
        };
    
        return kernel_clone(&args);
    }

    通过fork系统调用创建进程时一个CLONE标志都没有指定。

    SYSCALL_DEFINE0(vfork) //kernel/fork.c
    {
        struct kernel_clone_args args = {
            .flags        = CLONE_VFORK | CLONE_VM,
            .exit_signal    = SIGCHLD,
        };
    
        return kernel_clone(&args);
    }

    通过vfork系统调用创建进程时指定了 CLONE_VM 标志,两个进程会共享地址空间。

    3. 内核线程创建

    pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
    {
        struct kernel_clone_args args = {
            .flags        = ((lower_32_bits(flags) | CLONE_VM | CLONE_UNTRACED) & ~CSIGNAL),
            .exit_signal    = (lower_32_bits(flags) & CSIGNAL),
            .stack        = (unsigned long)fn,
            .stack_size    = (unsigned long)arg,
        };
    
        return kernel_clone(&args);
    }

    内核线程至少包含 CLONE_VM 标记,都是共享地址空间的。

    4. 创建时传参差异

    创建进程时的flag:一个CLONE标志都没有指定
    创建线程时的flag:CLONE_VM、CLONE_FS、CLONE_FILES、CLONE_SIGNAL、CLONE_SETTLS、CLONE_PARENT_SETTID、CLONE_CHILD_CLEARTID、CLONE_SYSVSEM。
    创建内核线程时的flag:至少包含 CLONE_VM

    其中:
    CLONE_VM: 新task和父进程共享地址空间。
    CLONE_FS:新task和父进程共享文件系统信息。
    CLONE_FILES:新task和父进程共享文件描述符表。

    二、task_struct 初始化差异

    1. copy_process 实现

    pid_t kernel_clone(struct kernel_clone_args *args)
    {
        p = copy_process(NULL, trace, NUMA_NO_NODE, args);
    }
    
    struct task_struct *copy_process(struct pid *pid, int trace, int node, struct kernel_clone_args *args)
    {
        struct task_struct *p;
    
        /* 1. 复制进程 task_struct 结构体 */
        p = dup_task_struct(current, node);
    
        /* 2. 拷贝 files_struct */
        retval = copy_files(clone_flags, p);
        /* 3. 拷贝 fs_struct */
        retval = copy_fs(clone_flags, p);
        /* 4. 拷贝 mm_struct */
        retval = copy_mm(clone_flags, p);
        
        ...
        return p;
    }

    2. dup_task_struct()

    static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
    {
        struct task_struct *tsk;
        
        err = arch_dup_task_struct(tsk, orig);
    
        tsk->stack = stack;
        tsk->stack_vm_area = stack_vm_area;
        refcount_set(&tsk->stack_refcount, 1);
        ...
    
        return tsk;
    }
    
    int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
    {
        /* 直接结构体值拷贝 */
        *dst = *src;
    }

    直接进行值拷贝,浅拷贝,后续没有覆盖赋值的话,新建任务 p 的 task_struct 结构就和 current 是一样的。


    3. copy_files()

    static int copy_files(unsigned long clone_flags, struct task_struct *tsk)
    {
        struct files_struct *oldf, *newf;
        int error = 0;
    
        /* 若是指定了 CLONE_FILES,直接共用current的 */
        if (clone_flags & CLONE_FILES) {
            atomic_inc(&oldf->count);
            goto out;
        }
    
        /* 若没有指定 CLONE_FILES 才会拷贝一份 */
        newf = dup_fd(oldf, NR_OPEN_MAX, &error);
        if (!newf)
            goto out;
        tsk->files = newf;
    out:
        return error;
    }

    若指定了 CLONE_FILES 标志,就直接复用 current 进程的 files_struct 结构体。

    4. copy_fs()

    static int copy_fs(unsigned long clone_flags, struct task_struct *tsk)
    {
        struct fs_struct *fs = current->fs;
    
        /* 若是指定了 CLONE_FS,直接共用current的 */
        if (clone_flags & CLONE_FS) {
            fs->users++;
            return 0;
        }
    
        /* 若没有指定 CLONE_FS 才会拷贝一份 */
        tsk->fs = copy_fs_struct(fs);
    
        return 0;
    }

    若指定了 CLONE_FS 标志,就直接复用 current 进程的 fs_struct 结构体。


    4. copy_mm()

    static int copy_mm(unsigned long clone_flags, struct task_struct *tsk)
    {
        struct mm_struct *mm, *oldmm;
        int retval;
    
        tsk->mm = NULL;
        tsk->active_mm = NULL;
        oldmm = current->mm;
    
        /* 若是指定了 CLONE_VM,直接共用current的 */
        if (clone_flags & CLONE_VM) {
            mmget(oldmm);
            mm = oldmm;
            goto good_mm;
        }
    
        /* 若没有指定 CLONE_VM 才会拷贝一份 */
        mm = dup_mm(tsk, current->mm);
    
    good_mm:
        tsk->mm = mm;
        tsk->active_mm = mm;
    
        return 0;
    }

    若指定了 CLONE_VM 标志,就直接复用 current 进程的 mm_struct 结构体。创建线程和内核线程时都指定了这个标志。

    三、总结

    1. 内核中线程和进程都是用 task_struct 来表示,只不过线程和创建它的父进程共享打开文件列表、目录信息、虚拟地址空间等数据结构,会更轻量一些。所以也叫轻量级进程。

    2. 对于内核任务,都指定了 CLONE_VM 标志,致使其使用地址空间都是同一个,所以一般都叫内核线程,而不是内核进程。

  • 相关阅读:
    插入排序法
    二分查找
    排序算法
    牛客网 猜数游戏
    决策树及随机森林(笔记)
    knn的缺陷及改进
    区块链、比特币简易PYTHON实现版笔记
    B树,B+树,以及它们和数据库索引之间的关系
    Balanced Binary Tree
    Advantages & Disadvantages of Recursion
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/16788213.html
Copyright © 2020-2023  润新知