进程管理-1
进程管理应该可以说是操作系统最重要的部分了,几个月前学到这里也没学完就忙毕设去了,忙完毕设重新捡起来看看。
进程描述符
进程描述符task_struct由很大一块组成,定义在linux/include/linux/sched.h中。
进程基本信息
pid_t pid;
pid_t tgid;
char comm[TASK_COMM_LEN];
unsigned int __state;
/*
* Pointers to the (original) parent process, youngest child, younger sibling,
* older sibling, respectively. (p->father can be replaced with
* p->real_parent->pid)
*/
/* Real parent process: */
struct task_struct __rcu *real_parent;
/* Recipient of SIGCHLD, wait4() reports: */
struct task_struct __rcu *parent;
/*
* Children/sibling form the list of natural children:
*/
struct list_head children;
struct list_head sibling;
struct task_struct *group_leader;
- pid:进程 ID,即进程的唯一标识符。
- tgid:线程组 ID,对于线程来说,它与 PID 相同,但对于线程组中的其他线程,TGID 是相同的。通过tgid == pid判断是否为线程组主进程
- comm:进程的名称(字符串),通常是程序名称,最长 16 字符。
- __state:进程的当前状态(TASK_RUNNING、TASK_INTERRUPTIBLE、TASK_UNINTERRUPTIBLE 等)。
- parent:指向父进程的指针(task_struct *parent)。指向逻辑父进程,可能因调试或信号处理被临时修改(如调试时指向调试器进程)
- children:指向子进程链表的指针(list_head children)。
- sibling: 兄弟进程链表指针,用于将当前进程插入父进程的children链表中
- real_parent:指向进程创建者的指针(进程的实际父进程,可能是某个线程)。指向进程的实际父进程。即使父进程被调试器(如GDB)替换,该字段仍指向原始父进程
- group_leader:指向线程组领导者(主进程)的指针。
此处与书中/大多数博客相比,将原来的volatile state变更为了unsigned int __state,下面是更改日志
Commit 2f064a5
sched: Change task_struct::state
Change the type and name of task_struct::state. Drop the volatile and
shrink it to an ‘unsigned int’. Rename it in order to find all uses
such that we can use READ_ONCE/WRITE_ONCE as appropriate.
Signed-off-by: Peter Zijlstra (Intel) peterz@infradead.org
Reviewed-by: Daniel Bristot de Oliveira bristot@redhat.com
Acked-by: Will Deacon will@kernel.org
Acked-by: Daniel Thompson daniel.thompson@linaro.org
Link: https://lore.kernel.org/r/20210611082838.550736351@infradead.org
- 目标背景
task_struct 是 Linux 内核中的一个结构体,用于表示一个任务(或线程)。其中的 state 成员变量表示任务的当前状态(如就绪、运行、睡眠等)。传统上,这个变量被定义为 volatile 类型,目的是避免编译器优化,确保访问该变量时直接从内存中读取或写入。
然而,volatile 关键字并不能提供真正的内存同步保障,在多核环境下存在缺陷,因此更现代的代码习惯是用 READ_ONCE 和 WRITE_ONCE 宏来确保变量的正确访问,而不是依赖 volatile。- 具体任务
a. 去掉 volatile 关键字
- 传统的 volatile 使用已经不推荐,因为它无法保证多核同步或者避免数据竞争。
- 改用更明确的机制(如 READ_ONCE 和 WRITE_ONCE),这些宏在 Linux 内核中提供了对内存访问的安全控制。
b. 缩小变量类型为 unsigned int- 将变量类型改为 unsigned int(无符号整数)可以减小结构体的大小,同时满足状态的需求。
- 一般来说,任务状态只需要几个位来表示,因此不需要使用较大的数据类型(如 long 或其他)。
c. 重命名 state- 重命名 task_struct::state 是为了方便后续代码检查和替换。
- 新的名字会使代码中对该变量的引用更容易被识别(通过搜索),从而确保在所有读/写访问处添加 READ_ONCE 和 WRITE_ONCE 宏。
- READ_ONCE 和 WRITE_ONCE
- READ_ONCE(x):确保读取变量时,编译器不会优化读取操作,并从内存中正确地读取该值。
- WRITE_ONCE(x, val):确保写入变量时,编译器不会优化写入操作,并将值正确地写入内存。
这两个宏被广泛用于内核代码中以替代 volatile,特别是在涉及并发访问时,能提供更好的性能和正确性。
调度相关信息
int on_rq;
int prio;
int static_prio;
int normal_prio;
unsigned int rt_priority;
unsigned int policy;
const struct sched_class *sched_class;
int nr_cpus_allowed;
- on_rq: 标记进程当前是否处于可运行队列中。取值:0(未在队列)或1(在队列)
- prio:进程的优先级。范围:-20 (最高) 到 139 (最低)
- static_prio:静态优先级(不受动态调整的影响)。用户空间设置的原始优先级.通过nice值转换:static_prio = nice + 120
- normal_prio:常规优先级(经过调度器计算得到)。
- 实时进程:normal_prio = 99 - rt_priority。
- 普通进程:normal_prio = static_prio
- rt_priority: 仅对实时进程有效,数值越大优先级越高.范围:1 (最低) 到 99 (最高)
- policy:调度策略
- SCHED_NORMAL (0):普通进程,使用CFS调度器
- SCHED_FIFO (1):实时先进先出,无时间片限制
- SCHED_RR (2):实时轮转调度,有时间片分配
- SCHED_BATCH (3):批处理任务,减少上下文切换
- SCHED_IDLE (5):最低优先级任务
- sched_class:调度类,表示进程所属的调度类别(例如 CFS 调度类)。
- nr_cpus_allowed: 进程可运行的CPU核心数量限制