Linux 进程描述符和任务结构

一、什么是进程描述符和任务结构?

  • 进程描述符(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的作用

  1. 唯一标识进程/线程(pid/tgid)
  2. 管理进程状态(state/exit_state)
  3. 调度信息(优先级、调度实体、队列)
  4. 内存空间管理(mm_struct)
  5. 文件系统和文件描述符表
  6. 信号处理
  7. 父子关系链表
  8. CPU上下文保存与恢复(thread_struct)
  9. 安全权限

五、进程链表与进程树

  • 内核通过task_struct中的链表成员(如children、sibling)组织进程树,方便遍历所有进程。
  • 通过全局链表init_task可以遍历系统所有进程(for_each_process宏)。

六、task_struct的生命周期

  1. 创建
    通过copy_process()分配并初始化task_struct。
  2. 运行与调度
    由调度器管理,保存/恢复CPU上下文。
  3. 退出
    调用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_structtasks成员挂在双向循环链表上。

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)
  • taskstask_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_taskinit进程开始递归即可。

二、task_struct在调度和内存管理中的作用

1. 在调度中的作用

  • 调度实体(se):task_struct中的struct sched_entity se成员用于CFS调度器,保存该进程/线程在调度队列(红黑树)中的状态。
  • 优先级priosched_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.hinclude/linux/sched/signal.h
  • 调度相关:kernel/sched/kernel/fork.c
  • 内存管理相关:mm/kernel/fork.c

创作不易,点赞关注,持续更新!!!

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猩火燎猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值