news 2026/7/3 6:02:06

Linux 系统编程 09:线程基础

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux 系统编程 09:线程基础

前言:

承接上一篇 System V IPC 三大进程间通信机制,多进程模型实现了任务并发,但进程间切换开销大、通信成本高,在高频并发场景下并非最优解。本篇引入更轻量的并发执行单元 —— 线程,讲解 Linux 线程的底层本质、POSIX 线程库的创建与回收、线程与进程的资源差异,是理解多线程并发、后续线程同步机制的前置基础,也是笔试面试的核心必考内容。


一、线程核心概念

1. 什么是线程

进程是操作系统分配资源的基本单位,而线程是 CPU 调度和执行的基本单位。一个进程内可以包含一个或多个线程,所有线程共享所属进程的地址空间与系统资源,仅保留自身独立的执行上下文与栈空间。

Linux 系统中没有专门的 “线程” 内核结构体,线程本质是轻量级进程(LWP,Light Weight Process),内核同样用task_struct描述,和进程共用同一套调度框架,核心区别在于是否共享地址空间与系统资源。

2. 进程与线程核心对比

对比维度进程线程
本质定位资源分配的基本单位CPU 调度的基本单位
地址空间拥有独立完整的虚拟地址空间无独立地址空间,共享所属进程的地址空间
切换开销大,需切换页表、刷新 TLB、替换进程资源小,仅切换寄存器、栈等私有上下文
通信方式需要管道、共享内存等 IPC 机制直接通过全局变量、堆内存交换数据
稳定性进程间相互独立,一个崩溃不影响其他一个线程异常崩溃,整个进程终止
创建销毁速度慢,资源占用高速度快,资源占用极低

3. 多线程的优劣势

优势

  1. 线程上下文切换开销远低于进程,高并发场景下 CPU 利用率更高
  2. 同进程内线程通信成本极低,无需内核中转,直接读写共享内存
  3. 创建、销毁速度快,适合短任务高频并发的场景

劣势

  1. 稳定性弱,单个线程触发内存错误会导致整个进程崩溃
  2. 多线程并发访问共享资源需要同步处理,增加编程复杂度
  3. 调试与问题排查难度远高于单进程程序

二、POSIX 线程库:pthread 基础

Linux 标准线程库为 pthread(POSIX Thread),属于用户态库,底层封装了内核 clone 系统调用,编译时需要链接-lpthread库。

1. 线程创建:pthread_create

#include <pthread.h> int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
  • thread:传出参数,保存创建成功的线程 ID(进程内唯一)
  • attr:线程属性结构体,传NULL使用默认属性
  • start_routine:线程入口函数,格式为void* 函数名(void*)
  • arg:传递给线程入口函数的参数
  • 返回值:成功返回0,失败直接返回错误码(不设置 errno)

注意:pthread_t是库层面的线程 ID,和内核中的 LWP 号不是同一个概念,仅在当前进程内有效。

2. 线程退出:pthread_exit

void pthread_exit(void *retval);
  • 功能:主动终止当前线程,retval为退出状态,可被其他线程通过pthread_join获取
  • return的区别:
    • return只是函数返回,仅在线程主入口函数中 return 才会退出线程
    • pthread_exit在任意函数层级调用,都会直接终止当前线程
    • main 函数中 return 会终止整个进程,所有线程一同退出;主线程调用pthread_exit只会退出主线程,子线程继续运行

核心坑点:不能返回线程栈上局部变量的地址,线程退出后栈空间会被回收,成为野指针。返回值必须使用全局变量或堆内存。

3. 线程回收:pthread_join

int pthread_join(pthread_t thread, void **retval);
  • 功能:阻塞等待指定线程退出,回收其资源,等价于进程的waitpid
  • retval:传出参数,接收线程的退出状态
  • 每个非分离线程必须被 join 一次,否则会残留线程资源,造成资源泄漏

4. 线程分离:pthread_detach

int pthread_detach(pthread_t thread);
  • 功能:将指定线程设置为分离状态,线程退出后由系统自动回收资源,无需手动 join
  • 分离后的线程无法再被 join,也无法获取其退出返回值
  • 适用于不需要关心返回值的后台常驻线程

5. 线程取消:pthread_cancel

int pthread_cancel(pthread_t thread);
  • 功能:向指定线程发送取消请求,请求其终止执行
  • 注意:这是请求而非强制终止。默认情况下线程会在取消点(如系统调用、IO 操作)响应退出;线程也可以设置取消状态,忽略取消请求。

三、线程的共享资源与私有资源(面试高频)

线程共享进程的大部分资源,但也保留自身独立的执行上下文,明确两者边界是理解多线程行为的核心。

资源类别线程间共享线程私有
内存空间代码段、全局数据段、堆内存、共享映射区线程栈、线程局部存储(TLS)
系统资源文件描述符表、信号处理函数配置寄存器上下文、程序计数器
进程属性用户 ID / 组 ID、工作目录、umask 掩码线程 ID、errno 变量、信号屏蔽字
调度相关进程优先级基准线程独立调度优先级

关键细节:errno是每个线程独立的变量,多线程下不会互相干扰;但文件描述符完全共享,一个线程修改文件偏移量,其他所有线程都会受影响。


四、实战:多线程创建与回收

#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h> // 线程1:接收参数,计算后返回堆上的结果 void *thread_task1(void *arg) { int num = *(int *)arg; printf("线程1启动,参数:%d,线程ID:%lu\n", num, pthread_self()); int *result = malloc(sizeof(int)); *result = num * 2; sleep(1); pthread_exit(result); } // 线程2:简单任务执行后退出 void *thread_task2(void *arg) { printf("线程2启动,线程ID:%lu\n", pthread_self()); sleep(2); printf("线程2执行完毕\n"); pthread_exit(NULL); } int main(void) { pthread_t tid1, tid2; int param = 10; // 创建两个子线程 int ret = pthread_create(&tid1, NULL, thread_task1, &param); if (ret != 0) { fprintf(stderr, "线程1创建失败\n"); return 1; } ret = pthread_create(&tid2, NULL, thread_task2, NULL); if (ret != 0) { fprintf(stderr, "线程2创建失败\n"); return 1; } printf("主线程运行,等待子线程回收\n"); // 回收线程1并获取返回值 void *ret_val; pthread_join(tid1, &ret_val); printf("线程1已回收,计算结果:%d\n", *(int *)ret_val); free(ret_val); // 回收线程2 pthread_join(tid2, NULL); printf("线程2已回收,程序结束\n"); return 0; }

编译命令:gcc demo.c -o demo -lpthread


五、Linux 线程底层本质:轻量级进程

Linux 的线程实现属于内核级线程,和 Windows 的用户态线程不同:

  1. 底层通过clone系统调用创建,和fork同源,区别在于 clone 可以指定共享哪些资源
  2. 创建线程时,共享地址空间、文件描述符表、信号处理表等进程资源,仅分配独立的栈与寄存器上下文
  3. 内核调度器对进程和线程一视同仁,都作为独立调度单元参与 CPU 调度

这种实现的优势是线程由内核调度,一个线程阻塞不会影响同进程的其他线程;劣势是线程操作都需要系统调用,有一定开销。


六、面试高频考点与易错坑点

1. 经典面试问答

Q1:进程和线程有什么本质区别?

答:

  1. 进程是操作系统分配资源的基本单位,线程是 CPU 调度的基本单位。
  2. 进程拥有独立的虚拟地址空间,线程没有独立地址空间,共享所属进程的地址空间与系统资源。
  3. 进程切换开销大,需要切换页表与进程资源;线程切换开销小,仅切换私有上下文。 进程间通信需要 IPC 机制,线程间可直接通过共享内存通信。

Q2:为什么线程切换的开销远小于进程?

答: 进程切换需要替换整个地址空间的页表、刷新 TLB 缓存,同时切换文件描述符表、信号处理等进程资源; 而线程共享进程的地址空间,切换时只需要保存和恢复线程的寄存器、栈指针等私有执行上下文,不需要切换地址空间,因此开销小很多。

Q3:pthread_join 和 pthread_detach 有什么核心区别?

答: pthread_join 是阻塞等待指定线程退出,手动回收线程资源,同时可以获取线程的退出返回值; pthread_detach 是将线程设置为分离状态,线程退出后由系统自动回收资源,不需要手动 join,也无法获取返回值。

Q4:线程退出时,return 和 pthread_exit 有什么区别?

答: 在线程入口主函数中,两者效果一致,都会终止当前线程。 但 return 只是函数返回,在线程调用的子函数中 return 只会返回到上一层,不会退出线程;pthread_exit 在任意层级调用都会直接终止当前线程。 另外 main 函数中 return 会终止整个进程,所有线程一同退出;主线程调用 pthread_exit 只会退出主线程,子线程继续运行。

Q5:线程之间哪些资源共享,哪些私有?

答: 共享资源:代码段、全局数据、堆内存、文件描述符表、信号处理方式、用户 ID 与组 ID、工作目录。 私有资源:线程 ID、线程栈、寄存器上下文、errno、信号屏蔽字、线程局部存储。

2. 常见易错坑点

  1. 线程退出返回栈上局部变量的地址,线程退出后栈空间回收,导致野指针,必须使用堆内存或全局变量传递返回值
  2. 编译 pthread 程序忘记添加-lpthread链接选项,出现函数未定义报错
  3. 非分离线程忘记调用 pthread_join 回收,造成线程资源泄漏,耗尽进程线程数上限
  4. 误以为 pthread_self 返回的线程 ID 就是内核 LWP 号,两者分属不同层面,并不等价
  5. 多线程并发修改全局变量不加同步保护,导致数据竞争、计算结果异常
  6. main 函数直接 return 退出,误以为子线程还能继续运行,实际整个进程会终止,所有线程被销毁
  7. 对已分离的线程重复调用 pthread_join,导致调用失败

以上就是 Linux 线程基础的全部核心内容,线程是高并发编程的基础执行单元,但共享资源带来的数据竞争问题必须通过同步机制解决。下一篇我们将讲解线程同步的三大核心工具:互斥锁、条件变量与读写锁,拆解死锁成因与规避方案。


制作不易,如果对你有用,希望能点赞收藏支持一下。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/3 6:02:02

SPECTROLAB S光谱仪火花激发异常故障排查与解决方案

SPECTROLAB S直读光谱仪火花激发异常是设备高频故障&#xff0c;主要表现为无火花、火花微弱、火花偏斜、连续拉弧、激发报错&#xff08;No Spark、Source Off、Clamp Up、Argon Low&#xff09;、激发斑点发黑/不规则等&#xff0c;直接导致样品无法激发、数据漂移、检测失效…

作者头像 李华
网站建设 2026/7/3 6:00:18

MySQL索引完整教程:创建、查看、修改、删除与日常管理

一、索引基础说明 InnoDB 支持&#xff1a;主键索引、普通索引、唯一索引、联合复合索引、前缀索引、全文索引&#xff1b; 索引核心作用&#xff1a;加速 WHERE / JOIN / ORDER BY / GROUP BY 查询&#xff1b; 代价&#xff1a;插入、更新、删除时需要维护 B 树&#xff0c;索…

作者头像 李华
网站建设 2026/7/3 5:52:28

美国站儿童首饰ASTM F2923-20标准

亚马逊美国站儿童首饰ASTM F2923-20标准一、标准基础概述ASTM F2923-20是美国材料与试验协会&#xff08;ASTM&#xff09;2020年3月发布的儿童首饰消费品安全强制规范&#xff0c;也是亚马逊美国站12岁及以下儿童首饰品类的核心准入检测标准&#xff0c;替代旧版ASTM F2923-17…

作者头像 李华
网站建设 2026/7/3 5:51:46

2026谷歌排名受外链权重影响:月IP从0到破万,我们只做了这20条链接

二零二六年六月&#xff0c;我们那个做工业阀门的海外独立站&#xff0c;在外贸行业里煎熬了整整十个月。那个时候&#xff0c;通过国外主流测试工具看到&#xff0c;网页每天在谷歌上的访问量少得可怜&#xff0c;每天只有五个或者六个孤零零的点击。当时很多同行劝我们花一百…

作者头像 李华
网站建设 2026/7/3 5:51:38

三分钟掌握ncmdump:轻松解密网易云音乐NCM格式的完整指南

三分钟掌握ncmdump&#xff1a;轻松解密网易云音乐NCM格式的完整指南 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 你是否曾经下载了网易云音乐的歌单&#xff0c;却发现那些NCM格式的音乐文件无法在其他播放器上播放&#xff1f;…

作者头像 李华
网站建设 2026/7/3 5:49:37

【如何快速用空数据(零字节)覆盖指定文件的原有内容】

Linux平台dd 命令dd执行例子参数的含义说明实际执行效果执行输出的解读为什么执行这个命令&#xff1f;dd 用空数据&#xff08;零字节&#xff09;覆盖指定文件的原有内容&#xff0c;但保持文件大小不变。 执行例子 ** [roottest /]# dd if/dev/zero oftest.dat bs1 count…

作者头像 李华