Linux中进程和线程区别

发布于:2024-07-02 ⋅ 阅读:(11) ⋅ 点赞:(0)

进程在内核中的描述符 task_struct 结构:

struct task_struct {
        // 进程id
     pid_t    pid;
        // 用于标识线程所属的进程 pid
     pid_t    tgid;
        // 进程打开的文件信息
        struct files_struct  *files;
        // 内存描述符表示进程虚拟地址空间
        struct mm_struct  *mm;

        .......... 省略 .......
}

在进程描述符 task_struct 结构中,有一个专门描述进程虚拟地址空间的内存描述符 mm_struct 结构,这个结构体中包含了前边几个小节中介绍的进程虚拟内存空间的全部信息。

每个进程都有唯一的 mm_struct 结构体,也就是前边提到的每个进程的虚拟地址空间都是独立,互不干扰的。

通过  fork() 函数创建子进程:

copy_mm  函数首先会将父进程的虚拟内存空间 current->mm 赋值给指针 oldmm。然后通过 dup_mm 函数将父进程的虚拟内存空间以及相关页表拷贝到子进程的 mm_struct 结构中。最后将拷贝出来的 mm_struct 赋值给子进程的 task_struct 结构。

通过 fork() 函数创建出的子进程,它的虚拟内存空间以及相关页表相当于父进程虚拟内存空间的一份拷贝,直接从父进程中拷贝到子进程中。

通过 vfork 或者 clone 系统调用创建出的子进程:

首先会设置 CLONE_VM 标识,这样来到 copy_mm 函数中就会进入  if (clone_flags & CLONE_VM)  条件中,在这个分支中会将父进程的虚拟内存空间以及相关页表直接赋值给子进程。这样一来父进程和子进程的虚拟内存空间就变成共享的了。也就是说父子进程之间使用的虚拟内存空间是一样的,并不是一份拷贝。

进程和线程:

子进程共享了父进程的虚拟内存空间,这样子进程就变成了我们熟悉的线程,是否共享地址空间几乎是进程和线程之间的本质区别。Linux 内核并不区别对待它们,线程对于内核来说仅仅是一个共享特定资源的进程而已

内核线程和用户态线程:

内核线程和用户态线程的区别就是内核线程没有相关的内存描述符 mm_struct ,内核线程对应的 task_struct 结构中的 mm 域指向 Null,所以内核线程之间调度是不涉及地址空间切换的。

当一个内核线程被调度时,它会发现自己的虚拟地址空间为 Null,虽然它不会访问用户态的内存,但是它会访问内核内存,聪明的内核会将调度之前的上一个用户态进程的虚拟内存空间 mm_struct 直接赋值给内核线程,因为内核线程不会访问用户空间的内存,它仅仅只会访问内核空间的内存,所以直接复用上一个用户态进程的虚拟地址空间就可以避免为内核线程分配 mm_struct 和相关页表的开销,以及避免内核线程之间调度时地址空间的切换开销。

父进程与子进程的区别,进程与线程的区别,以及内核线程与用户态线程的区别其实都是围绕着这个 mm_struct 展开的。

参考:一步一图带你深入理解 Linux 虚拟内存管理