linux中线程和进程的区别深度剖析底层实现

前言

在没有仔细了解过Linux的进程和线程实现机制之前,看过很多关于进程和线程的博客,从这些博客中我大概知道进程和线程的区别

1、进程拥有独立的内存空间,因此进程与进程之间相对独立,互不影响,但缺点进程间的通信相对复杂

2、同一进程的线程共享进程的内存和资源(线程有自己的私有空间),因此同一进程下的线程之间的通信很方便,缺点是一个线程的出问题会影响到同一进程下的其他线程

看完这些关于进程和线程的博客后,我有一些疑惑如下:

1、细心的小伙伴会发现,在探讨进程和线程的区别时,默认考虑的是同一进程下的线程,那么不同进程下的线程又当如何通信?

2、在初学Linux时,就被告知Linux是一个多进程的操作系统,那么线程在Linux中又是如何体现?

3、Linux中进程和线程的底层实现逻辑是怎么样?

Linux中进程和线程的共性

在Linux中进程和线程,到了内核里面,统一被称为任务,由结构体task_struct进行描述,也就是说关于任务的管理,最终是对结构体task_struct进行管理

task_struct结构分布图如下所示:

如何管理?

1、每一个任务都有一个自己的专属ID,通过这个ID我们可以判断出这是线程任务,还是进程任务(pid是process id,tgid是thread group )

pid_t pid;
pid_t tgid;
struct task_struct *group_leader; 

如上所示,task_struct中关于ID的变量有三个,如果进程,只有主线程(也就是进程没有创建其他的线程),那pid是自己,tgid是自己,group_leader指向的还是自己。如果进程创建了其他线程,那么这个线程对应的task_struct结构体中的pid代表自己,而tgid则等于进程的主线程的pid,group_leader指向的就是进程的主线程。

image-20220510164326295

这提一点额外的东西,看不懂没关系,一个进程里可以有很多线程,但无论是线程还是进程在内核中都通过task_strut来描述,在这个结构体中可以做什么操作,比如设置一个线程组,将同一个进程的线程,都指向这个线程组。内核可以通过操作线程组,对所以线程进行统一操作(说这个是为了表达,一个系统对于众多资源的管理思路,通过分组,分区,分块将具有相同特性的资源,通过标识符/链表之类的进行归类…)

Linux中进程的创建

首先明确一点,所以进程(0号进程除外)的创建最终通过fork实现的(fork不是一个函数哈,你可以理解为一个操作,这个操作由一系列函数来完成)

以应用层创建一个进程为例,我们来看一看,整个函数调用的过程

fork -> sys_call_table -> sys_fork -> _do_fork->copy_process

1、

copy_process

....->dup_task_struct dup_task_struct完成了对task_struct的建立(从父进程复制来的),同时创建内核栈

...->copy_creds

...->sched_fork 设置进程状态/初始化优先级/设置调度类

....…复制一个进程的文件信息/复制一个进程的目录信息 …

2、

接下来,就是根据初始化的调度类型,将这个进程加入到相应的调度队列中

其实说白了,一个进程的产生,是通过对另一个进程进行复制,然后再进行初始化,将一些不能的共用的资源,重新申请一份给新的进程,具体复制哪些信息,从第一张图中可以看出,这篇文章主要将进程和线程的实现区别,所以这里就不展开了


Linux中线程的实现

主线程和进程说的是一个东西?我感觉是,但不确定

思考:无论是进程还是线程,在内核里面都是任务,管起来不是都一样吗?如果不一样,那怎么在内核里面加以区分呢?

与进程不同的是,线程不是一个完全由内核实现的机制,它是由内核态和用户态合作完成的。

用户态:在用户态中创建线程的栈

这里值的一说的是,为什么要先创建线程栈?虽然一个进程的所有线程共用进程的内存空间,但不同的线程最终是要去执行不同的任务的,栈是时刻要用的东西如果共用那就乱套了,所以线程栈就是线程的私有空间是在进程的堆空间申请的内存。 先创建线程栈也是为了将子线程和主线程(主线程栈跟进程是一样的)进行区分,如果此时不进行区分,那么在内存态中创建的资源属于谁?要知道线程的所有东西都是用的进程的 (说得有点乱,不知道说明了没,哈哈)

内核态:创建进程的话,调用的系统调用是fork,在copy_process函数里面,会将五大结构files_struct、fs_struct、sighand_struct、signal_struct、mm_struct都复制一遍,从此父进程和子进程各用各的数据结构。而创建线程的话,调用的是系统调用clone,在copy_process函数里面, 五大结构仅仅是引用计数加一,也即线程共享进程的数据结构。

总结

进程和线程的实现函数流程图如下,创建进程的话,调用的系统调用是fork,在copy_process函数里面,会将五大结构files_struct、fs_struct、sighand_struct、signal_struct、mm_struct都复制一遍,从此父进程和子进程各用各的数据结构。而创建线程的话,调用的是系统调用clone,在copy_process函数里面, 五大结构仅仅是引用计数加一,也即线程共享进程的数据结构。

image-20220510205039051
参考资料
极客时间:趣谈Linux操作系统

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值