Linux 多线程详解 —— 什么是线程
什么是线程?
linux 内核中是没有线程这个概念的,而是轻量级进程的概念:LWP。一般我们所说的线程概念是 C 库当中的概念。
线程是怎样描述的?
线程实际上也是一个task_struct
,工作线程拷贝主线程的task_struct
,然后共用主线程的mm_struct
。线程 ID 是在用task_struct
中 pid 描述的,而task_struct
中 tgid 是线程组 ID,表示线程属于该线程组,对于主线程而言,其 pid 和 tgid 是相同的,我们一般看到的进程 ID 就是 tgid。
即:
获取线程 ID 和主线程 ID 的值
但是获取该 gettid 系统调用接口并没有被封装起来,如果确实需要获取线程 ID,可使用:
则对线程组而言,所有的 tgid 一定是一样的,所有的 pid 一定是不一样的。主线程 pid 和 tgid 一样,工作线程 pid 和 tgid 一定不一样。
如何查看一个线程的 ID
上述 polkitd 进程是多线程的,进程 ID 为 731,进程内有 6 个线程,线程 ID 为 731,764,765,768,781,791。
如图:
多线程如何避免调用栈混乱的问题?
工作线程和主线程共用一个mm_struct
,如果都向栈中压栈,必然会导致调用栈出错。
实际上工作线程压栈是压了共享区,该共享区包含了许多线程独有的资源。如图:
每一个线程,默认在共享区中占有的空间为 8M,可以使用ulimit -s
修改。
进程是资源分配的基本单位,线程是调度的基本单位。
线程独有资源
线程 ID
一组寄存器
errno
信号屏蔽字
调度优先级
线程共享资源和环境
文件描述符表
信号的处理方式
当前工作目录
用户 id 和组 id
为什么要有多线程?
举个生活中的例子, 这就好比去银行办理业务。 到达银行后, 首先取一个号码, 然后坐下来安心等待。 这时候你一定希望, 办理业务的窗口越多越好。 如果把整个营业大厅当成一个进程的话, 那么每一个窗口就是一个工作线程。
线程带来的优势
线程会共享内存地址空间。
创建线程花费的时间要少于创建进程花费的时间。
终止线程花费的时间要少于终止进程花费的时间。
线程之间上下文切换的开销, 要小于进程之间的上下文切换。
线程之间数据的共享比进程之间的共享要简单。
充分利用多处理器的可并行数量。(线程会提高运行效率,但当线程多到一定程度后,可能会导致效率下降,因为会有线程调度切换。)
线程带来的缺点
健壮性降低:多个线程之中, 只要有一个线程不够健壮存在 bug(如访问了非法地址引发的段错误) , 就会导致进程内的所有线程一起完蛋。
线程模型作为一种并发的编程模型, 效率并没有想象的那么高, 会出现复杂度高、 易出错、 难以测试和定位的问题。
注意
并不是只有主线程才能创建线程, 被创建出来的线程同样可以创建线程。
不存在类似于 fork 函数那样的父子关系, 大家都归属于同一个线程组, 进程 ID 都相等, group_leader 都指向主线程, 而且各有各的线程 ID。
通过 group_leader 指针, 每个线程都能找到主线程。 主线程存在一个链表头,后面创建的每一个线程都会链入到该双向链表中。
并非只有主线程才能调用 pthread_join 连接其他线程, 同一线程组内的任意线程都可以对某线程执行 pthread_join 函数。
并非只有主线程才能调用 pthread_detach 函数, 其实任意线程都可以对同一线程组内的线程执行分离操作。
线程的对等关系:
Linux、C/C++技术交流群:【960994558】整理了一些个人觉得比较好的学习书籍、大厂面试题、技术教学视频资料共享在里面(包括 C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK 等等.),有需要的可以自行添加哦!~
以上有不足的地方欢迎指出讨论。
评论