linux学习10,进程描述符及其任务结构
发表于: 2018-12-12 20:26:01 | 已被阅读: 100 | 分类于: Linux笔记
第8节提到 linux 中,
用于描述 linux 进程的 task_struct 结构体
linux 内核中的进程都是用

task_struct 结构体定义在 include/linux/sched.h 里。
这个结构体相当巨大,有数 KB,查看 task_struct 结构体的各个成员,就知道 linux 的进程包含的“各种资源”包括:打开的文件,进程的地址空间,挂起的信号,进程的状态,等等。其他更多信息,可以自行查看源码。
linux 内核常常需要管理不止一个进程,所以就会有多个 task_struct 结构体需要管理,内核是通过双向循环链表管理的,如下图:

链表算是 C 语言中一种非常重要的数据类型了,我的文章还没有介绍过,后面会介绍的,敬请关注!!
不仅是 x86 结构,其他寄存器较少的硬件体系结构,linux 内核都是间接计算
struct thread_info {
struct task_struct *task; /* main task structure */
struct exec_domain *exec_domain; /* execution domain */
__u32 flags; /* low level flags */
__u32 status; /* thread synchronous flags */
__u32 cpu; /* current CPU */
int preempt_count; /* 0 => preemptable,
<0 => BUG */
mm_segment_t addr_limit;
struct restart_block restart_block;
#ifdef CONFIG_IA32_EMULATION
void __user *sysenter_return;
#endif
};
它记录着

thread_info 结构体定义在 include/asm/thread_info.h 里。
进程的状态
我们再回到 linux 内核的
- TASK_RUNNING,表示进程是可执行的,或者正在运行,或者正在运行队列里排队等待运行。
- TASK_INTERRUPTIBLE,表示进程正在睡眠,并且可能随时被唤醒信号唤醒,唤醒后,进程会被设置为 TASK_RUNNING。
- TASK_UNINTERRUPTIBLE,表示进程正在睡眠,不会被信号唤醒。
- ‘__TASK_TRACED,表示进程正在被其他进程跟踪,例如正在被 gdb 调试的进程就会是这个状态。
- ‘__TASK_STOPPED,表示进程停止执行,不能被投入运行。

在 linux 内核中,可以通过 set_task_state(task, state) 宏设置进程状态,它的定义如下:
#define set_task_state(tsk, state_value) \
set_mb((tsk)->state, (state_value))
// 继续跟踪
#define set_mb(var, value) do { var = value; barrier(); } while (0)
可以看出,这个宏其实就是简单的赋值,只是多了内存屏障用于强制其他处理器做重新排序。
set_task_state 宏位于 include/linux/sched.h 里。
进程的家族树
在 linux 中,所有进程都有着明显的“家族关系”,所有进程都有父进程,也能有自己的子进程树,拥有同一个父进程的进程们是“兄弟进程”。事实上,用于描述进程的 task_struct 结构体有一个 parent 指针,指向它的父进程,还包含一个子进程链表指针 children,用于维护它的子进程们。因此查询当前进程的父进程和子进程非常容易:
struct task_struct* parent = current->parent;
struct task_struct* children = current->children;
遍历内核的全部进程也非常简单,实际上,linux 内核提供了用于遍历进程的宏 list_for_each,它的定义如下:
#define list_for_each(pos, head) \
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
pos = pos->next)
list_for_each 宏定义位于 include/linux/list.h 里。

struct task_struct* task;
struct list_head* list;
list_for_each(list, ¤t->children){
task = list_entry(list, struct task_struct, sibling);
}
因为进程们的“家族关系”,依次遍历进程的父进程也非常简单:
for(task=current; task!=&init_task; task=task->parent);
因为 init_task 是系统的第一个进程,所以此处是遍历重点。