Linux进程和线程
基本概念
要理解的原理知识点:
- 程序是存储在磁盘上的指令集合。进程是程序在处理器上执行时的实体,包括代码、数据和状态信息。
- Linux进程是操作系统进行资源分配和调度的基本单位。每个进程都有自己的独立内存空间、数据、代码和状态。
- 进程的状态包括:新建、就绪、运行、等待、终止。
- 线程是CPU调度的基本单位,它比进程更轻量级,同一进程下的线程共享进程的地址空间和资源,如文件描述符和环境变量等。
- 每个进程至少包含一个线程,即主线程。
- 进程间通常通过进程间通信(IPC)机制进行通信,如管道、信号、消息队列、共享内存和套接字等。
- 线程间的同步可以通过自旋锁、互斥锁、和读写锁、信号量、条件变量、屏障等机制实现。
- 进程的创建可以通过调用
fork()、vfork()和exec()系列函数
实现。 - 用户线程的创建可以通过调用
pthread_create()
函数实现,内核线程的创建可以通过调用kthread_create()
函数实现。 - 无论是进程还是线程,它们在内核中都是通过
task_struct
结构体来表示和管理的。 - 现代Linux系统默认使用的是一对一的线程模型,即每个用户级线程都映射到一个独立的内核级线程。这种模型由Linux的NPTL(New POSIX Thread Library)实现,它提供了更好的并发性能和更细粒度的线程管理。这种模型有时也被称为“轻量级进程”模型,因为它允许线程共享某些资源,如内存空间和文件描述符,从而减少了创建和维护线程的开销。一对一线程模型使得每个用户线程都有自己的内核线程结构(
struct task_struct
),所以无论是进程还是线程,它们在内核中都是通过task_struct
结构体来表示和管理的,这些结构包含了线程的上下文信息、调度信息、优先级、打开的文件描述符、信号处理状态等。 - 对于进程的
task_struct
,task_struct
中的某些字段会反映进程的全局状态,如进程ID(PID)、父进程ID(PPID)等。task_struct
会包含与进程相关的资源管理信息,如虚拟内存结构(mm_struct
)等。 - 对于线程的
task_struct
,task_struct
中的某些字段会反映线程在进程中的角色和状态,如线程组ID(TGID)、线程ID(PID,对于线程来说,PID通常与TGID不同)等。task_struct
会包含与线程执行上下文相关的信息,如线程的栈指针、寄存器状态等。线程可能会共享进程的某些资源,如文件描述符表。 - 在操作系统中,内核态是指CPU的一种特权执行模式,它允许代码直接访问所有硬件资源和内存。内核态是操作系统核心组件运行的状态,包括内核本身、设备驱动程序和其他系统级服务。
- 在内核态下,操作系统执行的是内核级的单进程多线程,而不是多个并发的内核级进程。这个内核级进程或线程整个内核空间,负责管理整个系统,包括任务调度、内存管理、设备控制和系统调用等。虽然内核态代码可以创建和管理多个用户态进程,但内核态本身并不以多进程的形式运行。
- 操作系统通过在用户态创建和管理多个进程来实现多任务处理。
- 用户态的线程和进程都是可以睡眠的,而内核态的线程在某些情况下是不可以进入睡眠。
- 内核态线程在执行关键任务时,如处理器调度、内存管理等,通常是不允许进入睡眠状态的,因为这可能会导致系统死锁或性能问题。
Linux进程/线程涉及到它的的创建、调度、同步、通信等多个方面。
以上信息提供了Linux进程和线程的基本概念、创建、管理和同步机制的详细介绍。在实际的系统编程和应用开发中,合理地使用进程和线程可以提高程序的并发性和执行效率。
linux 内核源码分析
在创建线程时,通常使用 clone()
系统调用,它允许调用者通过标志位来控制哪些资源应该在子进程(在这种情况下是线程)中被共享。例如,通过设置 CLONE_VM
标志,子进程将共享父进程的地址空间,这是线程的一个关键特征。
现在linux内核已经更新到了 linux-6.8 版本,由于我觉得目前新版本的代码可读性与优美性更好,同时我也想了解最新的代码实现,所以我所有的介绍都是基于最新版本 linux-6.8(网上资料是基于4.4版本的讲解比较多,所以我的内容或许跟网上其他人讲的不一样,请见谅)。
在Linux内核中,创建进程和线程的代码主要位于 kernel/fork.c
文件中。这个文件包含了进程创建(包括线程创建,因为线程在Linux中是通过共享进程地址空间的轻量级进程来实现的)相关的核心逻辑。
以下是几个关键的函数和它们在进程/线程创建中的作用:
kernel_clone()
: 这是Linux内核