第17天:线程与 LWP:轻量级进程的内核实现【基础概念】【通用】

发布时间:2026/6/30 11:51:11

第17天:线程与 LWP:轻量级进程的内核实现【基础概念】【通用】 从进程到线程Linux如何实现并发执行的艺术你是否曾好奇现代操作系统是如何实现程序的并发执行的当你打开浏览器的多个标签页、同时编辑文档和听音乐时系统是如何高效地切换这些任务的在Linux中这一切都离不开线程和轻量级进程LWP的概念。今天让我们深入内核揭开Linux线程实现的神秘面纱。一、进程与线程核心概念辨析1.1 进程资源分配的基本单位在Linux中进程是操作系统资源分配的基本单位每个进程拥有#include linux/sched.h // 进程描述符结构 struct task_struct { pid_t pid; // 进程ID pid_t tgid; // 线程组IDThread Group ID struct mm_struct *mm; // 内存描述符地址空间 struct files_struct *files; // 打开的文件描述符 struct fs_struct *fs; // 文件系统信息 struct signal_struct *signal; // 信号处理 struct sighand_struct *sighand; // 信号处理程序 // ... };1.2 线程CPU调度的基本单位线程是CPU调度的基本单位多个线程共享同一进程的资源资源类型进程间同一进程的线程间地址空间独立共享文件描述符独立共享信号处理独立共享进程ID (PID)不同相同tgid相同线程ID (TID)-不同栈独立独立二、Linux中的线程实现LWP轻量级进程2.1 什么是LWP在Linux中线程是通过轻量级进程Light Weight Process, LWP实现的。每个LWP都有自己的task_struct但它们共享同一个进程的地址空间和其他资源。2.2 LWP的内核实现#include linux/sched.h // 创建轻量级进程线程的核心参数 struct clone_args { unsigned long flags; // 控制资源共享的标志位 unsigned long stack; // 子线程的栈地址 unsigned long stack_size; // 栈大小 unsigned long tls; // 线程本地存储 pid_t *parent_tid; // 父进程中存放子TID的地址 pid_t *child_tid; // 子进程中存放TID的地址 // ... };2.3 使用clone()创建LWP#define _GNU_SOURCE #include stdio.h #include stdlib.h #include unistd.h #include sched.h #include sys/syscall.h #include sys/wait.h // 线程栈大小 #define STACK_SIZE (1024 * 1024) // 1MB // 线程局部存储TLS示例 __thread int thread_local_data 0; // 线程执行函数 int thread_function(void *arg) { char *message (char *)arg; // 获取线程IDTID和进程IDPID printf(Thread: %s\n, message); printf( PID (tgid): %d\n, getpid()); // 进程ID所有线程相同 printf( TID (pid): %d\n, syscall(SYS_gettid)); // 线程ID每个线程不同 // 使用线程局部存储 thread_local_data syscall(SYS_gettid); printf( Thread local data: %d\n, thread_local_data); // 模拟线程工作 sleep(2); return 0; } int main() { char *stack1 malloc(STACK_SIZE); char *stack2 malloc(STACK_SIZE); pid_t tid1, tid2; printf(Main thread:\n); printf( PID (tgid): %d\n, getpid()); printf( TID (pid): %d\n, syscall(SYS_gettid)); // 创建第一个LWP线程 tid1 clone(thread_function, stack1 STACK_SIZE, // 栈向低地址增长 CLONE_VM | // 共享地址空间 CLONE_FS | // 共享文件系统信息 CLONE_FILES | // 共享文件描述符 CLONE_SIGHAND | // 共享信号处理程序 CLONE_THREAD | // 加入同一线程组 CLONE_SYSVSEM | // 共享System V信号量 SIGCHLD, // 发送SIGCHLD信号 Thread 1); // 创建第二个LWP线程 tid2 clone(thread_function, stack2 STACK_SIZE, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | SIGCHLD, Thread 2); printf(\nCreated two LWPs:\n); printf( Thread 1 TID: %d\n, tid1); printf( Thread 2 TID: %d\n, tid2); // 等待线程结束 waitpid(tid1, NULL, 0); waitpid(tid2, NULL, 0); // 清理 free(stack1); free(stack2); return 0; }三、线程组与PID/TID关系3.1 线程组结构在Linux中同一进程的所有线程组成一个线程组Thread Group#include linux/sched.h struct task_struct { pid_t pid; // 线程IDTID内核中每个task_struct唯一 pid_t tgid; // 线程组IDTGID即进程IDPID struct task_struct *group_leader; // 线程组领头进程 struct list_head thread_group; // 线程组链表 // ... };3.2 PID与TID的关系概念说明获取方式PID线程组IDThread Group ID即通常所说的进程IDgetpid()TID线程IDThread ID每个LWP唯一gettid()或syscall(SYS_gettid)# 查看进程的线程信息 ps -eLf # 输出示例 # UID PID PPID LWP C NLWP STIME TTY TIME CMD # weichao 12345 12300 12345 0 3 10:00 pts/0 00:00:00 ./myprogram # weichao 12345 12300 12346 0 3 10:00 pts/0 00:00:00 ./myprogram # weichao 12345 12300 12347 0 3 10:00 pts/0 00:00:00 ./myprogram四、POSIX线程库pthread与LWP4.1 pthread库的实现POSIX线程库pthread是用户态线程库底层基于Linux的LWP实现#include pthread.h #include stdio.h #include unistd.h // 线程函数 void *thread_func(void *arg) { char *msg (char *)arg; printf(pthread: %s\n, msg); printf( PID: %d, TID: %ld\n, getpid(), pthread_self()); return NULL; } int main() { pthread_t tid1, tid2; // 创建POSIX线程底层创建LWP pthread_create(tid1, NULL, thread_func, Thread 1); pthread_create(tid2, NULL, thread_func, Thread 2); // 等待线程结束 pthread_join(tid1, NULL); pthread_join(tid2, NULL); return 0; }4.2 pthread与LWP的关系┌─────────────────────────────────────────────────────────┐ │ 用户空间 │ │ ┌───────────────────────────────────────────────────┐ │ │ │ POSIX Thread Library (pthread) │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ │ │ pthread1 │ │ pthread2 │ │ pthread3 │ │ │ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ └───────┼─────────────┼─────────────┼─────────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ ├─────────────────────────────────────────────────────────┤ │ 内核空间 │ │ ┌───────────────────────────────────────────────────┐ │ │ │ Thread Group (TGID12345) │ │ │ │ ┌─────────────────────────────────────────────┐ │ │ │ │ │ task_struct (LWP1) │ │ │ │ │ │ - pid 12345 (TID, 也是group leader) │ │ │ │ │ │ - tgid 12345 (PID) │ │ │ │ │ └─────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────┐ │ │ │ │ │ task_struct (LWP2) │ │ │ │ │ │ - pid 12346 (TID) │ │ │ │ │ │ - tgid 12345 (PID) │ │ │ │ │ └─────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────┐ │ │ │ │ │ task_struct (LWP3) │ │ │ │ │ │ - pid 12347 (TID) │ │ │ │ │ │ - tgid 12345 (PID) │ │ │ │ │ └─────────────────────────────────────────────┘ │ │ │ └───────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘五、内核调度线程级别的调度5.1 调度器如何看待线程在Linux内核中调度器不区分进程和线程每个task_structLWP都是独立的调度单位#include linux/sched.h // 调度器选择下一个要执行的task static inline struct task_struct *pick_next_task(struct rq *rq) { struct task_struct *p; // 从运行队列中选择优先级最高的task p fair_sched_class.pick_next_task(rq); if (p) return p; // 尝试其他调度类 return p; }5.2 线程优先级与调度策略# 查看线程的调度策略和优先级 chrt -p pid # 设置线程优先级 chrt -r -p 99 pid # 设置为实时调度优先级99 # 查看线程详细信息 cat /proc/pid/status | grep -E Tgid|Pid|PPid|tracerpid|Policy|Priority六、实战演示6.1 使用ps查看LWP# 查看进程及其LWP ps -eLf | grep myprogram # 输出示例 # weichao 12345 12300 12345 0 3 10:00 pts/0 00:00:00 ./myprogram # weichao 12345 12300 12346 1 3 10:00 pts/0 00:00:01 ./myprogram # weichao 12345 12300 12347 2 3 10:00 pts/0 00:00:01 ./myprogram6.2 使用top查看线程# 进入top后按H键显示线程 top -H -p pid # 输出示例 # PID USER PR NI VIRT RES SHR S %CPU %MEM TIME COMMAND # 12345 weichao 20 0 100000 5000 3000 S 0.0 0.1 0:00.01 myprogram # 12346 weichao 20 0 100000 5000 3000 R 99.9 0.1 1:23.45 myprogram # 12347 weichao 20 0 100000 5000 3000 S 0.0 0.1 0:00.00 myprogram6.3 使用/proc文件系统查看线程信息# 查看进程的线程列表 ls /proc/pid/task/ # 查看特定线程的状态 cat /proc/pid/task/tid/status # 查看线程的栈信息 cat /proc/pid/task/tid/stack互动讨论思考问题在Linux中既然线程和进程在内核层面都表现为task_struct那么为什么还需要区分线程和进程线程相比进程有哪些优势实战挑战尝试编写一个程序创建多个LWP并观察它们的PID/TID变化。然后使用ps、top和/proc文件系统查看线程信息分析线程组的结构。请帮忙点赞收藏关注内容持续更新感谢大家~~~

相关新闻