一、什么是进程描述符和任务结构?
-
进程描述符(Process Descriptor):
是内核用来描述和管理进程/线程的一个数据结构。 -
任务结构(task_struct):
是Linux内核实现进程描述符的具体结构体,定义在内核源码include/linux/sched.h中。 -
线程与进程:
在Linux内核中,线程和进程本质上都是task_struct的实例,只是资源共享程度不同。
二、源码位置
- 头文件:
include/linux/sched.h - 结构体:
struct task_struct
三、task_struct的主要成员(常见核心字段)
下面以Linux 6.x为例,省略部分细节,仅列出关键成员:
struct task_struct {
// 进程状态与标志
volatile long state; // 进程状态(如TASK_RUNNING等)
unsigned int flags; // 进程标志
int exit_state; // 退出状态
// 标识与关系
pid_t pid; // 进程ID
pid_t tgid; // 线程组ID
struct task_struct *real_parent; // 父进程
struct list_head children; // 子进程链表
struct list_head sibling; // 兄弟进程链表
// 调度相关
struct sched_entity se; // 调度实体(CFS相关)
int prio; // 动态优先级
struct list_head run_list; // 运行队列
// 内存管理
struct mm_struct *mm; // 进程的内存描述符
struct mm_struct *active_mm; // 活动内存描述符
// 文件系统
struct fs_struct *fs; // 文件系统信息
struct files_struct *files; // 打开文件表
// 信号处理
struct signal_struct *signal; // 信号描述符(线程组共享)
struct sighand_struct *sighand; // 信号处理函数
// 用户与权限
kuid_t uid, euid, suid, fsuid;
kgid_t gid, egid, sgid, fsgid;
// 时间与统计
struct timespec start_time; // 启动时间
unsigned long utime; // 用户态时间
unsigned long stime; // 内核态时间
// 内核线程标志
unsigned int flags; // 包含PF_KTHREAD等
// 内核栈
void *stack; // 内核栈指针
// 其他
struct thread_struct thread; // 保存寄存器等CPU上下文
...
};
四、task_struct的作用
- 唯一标识进程/线程(pid/tgid)
- 管理进程状态(state/exit_state)
- 调度信息(优先级、调度实体、队列)
- 内存空间管理(mm_struct)
- 文件系统和文件描述符表
- 信号处理
- 父子关系链表
- CPU上下文保存与恢复(thread_struct)
- 安全权限
五、进程链表与进程树
- 内核通过
task_struct中的链表成员(如children、sibling)组织进程树,方便遍历所有进程。 - 通过全局链表
init_task可以遍历系统所有进程(for_each_process宏)。
六、task_struct的生命周期
- 创建
通过copy_process()分配并初始化task_struct。 - 运行与调度
由调度器管理,保存/恢复CPU上下文。 - 退出
调用do_exit()释放资源,状态置为EXIT_DEAD。
七、相关结构体
mm_struct:进程的虚拟内存空间描述files_struct:打开文件表signal_struct:信号信息thread_struct:CPU相关上下文
八、示意图
+----------------------+
| struct task_struct |
+----------------------+
| pid, tgid |
| state, prio |
| parent, children |
| mm, files, fs |
| signal, sighand |
| thread |
| ... |
+----------------------+
一、进程树遍历源码解析
1. 进程树组织
Linux用task_struct中的链表成员维护进程的父子关系:
real_parent:指向父进程children:所有子进程链表头sibling:兄弟进程链表节点
每个进程都挂在父进程的children链表上,形成进程树。
2. 遍历所有进程
内核用init_task(即PID 0的idle进程)作为进程链表的起点,所有进程通过task_struct的tasks成员挂在双向循环链表上。
2.1 遍历所有进程(for_each_process宏)
源码位置:include/linux/sched/signal.h
#define for_each_process(p) \
for (p = &init_task ; (p = next_task(p)) != &init_task ; )
或者更常见的是:
#define for_each_process(p) \
list_for_each_entry(p, &init_task.tasks, tasks)
tasks是task_struct中的struct list_head tasks成员。init_task是系统第一个进程(PID 0),所有进程都挂在它的tasks链表上。
示例伪代码:
struct task_struct *p;
for_each_process(p) {
printk("pid=%d, comm=%s\n", p->pid, p->comm);
}
2.2 遍历进程树(父子关系)
每个进程的children链表挂着子进程:
struct task_struct *child;
list_for_each_entry(child, &parent->children, sibling) {
printk("child pid=%d\n", child->pid);
}
parent为父进程指针。children是父进程的子进程链表头。sibling是每个子进程在兄弟链表中的节点。
递归遍历整个进程树:
void traverse_tree(struct task_struct *parent, int depth) {
struct task_struct *child;
list_for_each_entry(child, &parent->children, sibling) {
printk("%*s pid=%d, comm=%s\n", depth*2, "", child->pid, child->comm);
traverse_tree(child, depth+1); // 递归遍历子进程
}
}
- 从
init_task或init进程开始递归即可。
二、task_struct在调度和内存管理中的作用
1. 在调度中的作用
- 调度实体(se):task_struct中的
struct sched_entity se成员用于CFS调度器,保存该进程/线程在调度队列(红黑树)中的状态。 - 优先级:
prio和sched_class成员决定进程的调度优先级和所属调度算法(如CFS、RT)。 - CPU亲和性:
cpus_mask成员决定进程可在哪些CPU上运行。 - 调度队列挂载:task_struct通过
run_list等成员挂在调度器的运行队列上。
调度相关伪代码:
// 进程被唤醒,加入调度队列
enqueue_task(struct rq *rq, struct task_struct *p, int flags) {
// 挂到CFS红黑树
__enqueue_entity(&rq->cfs, &p->se);
}
- 调度器通过task_struct保存和恢复进程的CPU上下文(thread_struct),实现上下文切换。
2. 在内存管理中的作用
- mm_struct:task_struct中的
struct mm_struct *mm成员指向进程的虚拟内存描述符,管理进程的地址空间、页表、VMA(虚拟内存区域)。 - active_mm:内核线程没有自己的mm_struct,使用
active_mm指向当前活动进程的mm_struct。 - fork/exec时的内存复制与切换:fork时子进程的task_struct会复制父进程的mm_struct(写时复制),exec时会替换mm_struct指针。
- 资源释放:进程退出时,task_struct负责释放mm_struct、files_struct等资源。
内存管理相关伪代码:
// 创建进程时复制mm_struct
struct task_struct *child = copy_process(...);
child->mm = copy_mm(parent);
// 进程切换时切换mm_struct
switch_mm(old->mm, new->mm, new);
三、参考源码路径
- 进程树组织与遍历:
include/linux/sched.h、include/linux/sched/signal.h - 调度相关:
kernel/sched/、kernel/fork.c - 内存管理相关:
mm/、kernel/fork.c
创作不易,点赞关注,持续更新!!!

867

被折叠的 条评论
为什么被折叠?



