Linux系统中是存在内核级线程的,它的线程实现机制和传统操作系统的内核级线程模型略有不同,采用轻量级进程(LWP)的方式来实现线程功能,所有线程的调度和管理都由内核直接完成。

什么是内核级线程
内核级线程是由操作系统内核直接管理和调度的线程,内核维护线程的上下文信息,包括程序计数器、寄存器集合、栈空间等,线程的创建、销毁、切换等操作都需要内核参与。和内核级线程相对的是用户级线程,用户级线程的调度由用户空间的线程库完成,内核感知不到线程的存在,只能看到对应的进程。
内核级线程的特点
- 内核直接调度,线程切换需要陷入内核态,开销相对用户级线程更大
- 内核可以感知到线程的存在,能够将不同线程分配到不同的CPU核心上并行执行
- 如果某个线程发生阻塞,内核可以调度同进程的其他线程继续执行,不会因为一个线程阻塞导致整个进程挂起
Linux的线程实现机制
Linux内核本身并没有单独定义线程的数据结构,而是将线程和进程都视为可调度实体,线程在Linux中被称为轻量级进程(LWP)。每个轻量级进程都拥有自己独立的任务结构体task_struct,内核调度器直接对task_struct进行调度,因此Linux的线程本质上是由内核直接管理的,属于内核级线程的范畴。
Linux的线程是通过clone系统调用创建的,和创建进程的fork系统调用不同,clone允许新创建的任务共享父任务的地址空间、文件描述符、信号处理等资源,这些共享资源的任务就组成了同一个进程下的多个线程。
clone系统调用的参数说明
| 参数标志 | 含义 |
|---|---|
| CLONE_VM | 父子任务共享地址空间 |
| CLONE_FS | 父子任务共享文件系统信息 |
| CLONE_FILES | 父子任务共享打开的文件描述符表 |
| CLONE_SIGHAND | 父子任务共享信号处理函数表 |
| CLONE_THREAD | 父子任务属于同一个线程组,共享进程ID |
Linux线程和用户级线程的差异
很多早期的线程库比如GNU Portable Threads实现的是用户级线程,这类线程和Linux的原生线程有本质区别,具体差异如下:
- 调度主体不同:Linux原生线程由内核调度,用户级线程由用户空间线程库调度
- 内核感知度不同:Linux原生线程内核可以感知,用户级线程内核无法感知
- 并行能力不同:Linux原生线程可以跨CPU核心并行执行,用户级线程只能在一个核心上串行执行
- 阻塞影响不同:Linux原生线程单个阻塞不影响同进程其他线程,用户级线程单个阻塞会导致整个进程的所有线程阻塞
代码示例验证Linux线程的内核级属性
我们可以通过创建线程后查看进程和线程的内核信息,来验证Linux线程属于内核级线程。以下代码创建两个线程,然后打印每个线程的ID和进程ID。
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
// 线程执行函数
void* thread_func(void* arg) {
// 获取内核层面的线程ID,SYS_gettid是获取线程ID的系统调用号
pid_t tid = syscall(SYS_gettid);
printf("子线程ID:%d,所属进程ID:%dn", tid, getpid());
sleep(10); // 让线程休眠,方便查看系统线程信息
return NULL;
}
int main() {
pthread_t tid1, tid2;
// 创建两个线程
pthread_create(&tid1, NULL, thread_func, NULL);
pthread_create(&tid2, NULL, thread_func, NULL);
// 打印主线程的ID和进程ID
printf("主线程ID:%d,所属进程ID:%dn", syscall(SYS_gettid), getpid());
// 等待两个线程执行完成
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
return 0;
}
编译运行上述代码后,我们可以在另一个终端使用ps -eLf命令查看系统的线程信息,会看到同一个进程ID下存在多个不同的线程ID(LWP列的值),这些线程都是由内核直接管理和调度的,进一步证明Linux的线程属于内核级线程。
总结
Linux是存在内核级线程的,它通过轻量级进程的方式实现线程功能,所有线程的调度、管理都由内核完成,内核可以感知到每个线程的存在。和传统的用户级线程相比,Linux的线程支持多核心并行执行,单个线程阻塞不会影响同进程的其他线程,更符合现代多核处理器的使用场景。理解Linux的线程实现机制,对于编写多线程程序、排查线程相关问题都有重要的帮助。