执行线程,是进程中活动的对象。每个线程拥有一个独立的线程计数器,进程栈和一组进程寄存器。内核调度的是线程而非进程。
1.进程描述符及其任务结构
内核把进程放在叫做任务队列的双向循环链表中。链表中的每一项都是类型为task_struct,称为进程描述符,其结构定义位于linux/sched.h文件中。进程描述符中包含一个具体进程的所有信息。task_struct在32位机器上,大约有1.7k字节,包含了描述正在执行的程序的相关信息。
1.1分配进程描述符
linux通过slab分配器分配task_struct结构,这样能达到对象复用和缓冲着色的目的。
所以只需在系统堆栈栈底创建一个新结构thread_info,该结构中的task域存放指向该任务的task_struct指针。
struct thread_info {
struct task_struct *task;
struct exec_domain *exec_domain;
unsigned long flags;
unsigned long status;
__u32 cpu;
__s32 preempt_count;
mm_segment_t addr_limit;
structrestart_block restart_block;
unsigned long previous_esp;
__u8 supervistor_stack[0];
}
1.2进程描述符的存放
内核通过一个唯一的标识符PID来标识每个进程,表示为 pid_t隐含类型,实际上是int类型。PID最大默认值为32768.
内核中大部分处理进程的代码直接通过task_struct进行的,因此通过current宏找到正在运行的进程描述符的速度显得尤为重要,x86结构中,current把栈指针的后13个有效位屏蔽,用来计算threadd_info的偏移。该操作通过current_thread_info来实现,其汇编代码为:
movl $-8192, %eax
andl %esp , %eax
这里假定栈的大小为8kb,当4kb的栈启用时,就要用4092,而不是8192.最后current再从thread_info的task域中提取并返回task——struct的得知:
current_thread_info ()->task;
1.3进程状态
进程描述符的state域描述了当前状态。该域的值必须为下列五种状态标志之一:
TASk_RUNNING 运行吗、,这是用户空间的进程执行的唯一可能状态
TASK_INTERRUPTIBLE 可中断 进程被阻塞
TASK_UNINTERRUPTIBLE 不可中断
TASK_ZOMBIE 僵死
TASK_STOP 停止
1.4设置当前进程状态
内核经常需要调整某个进程的状态。这时最好使用set_task_state(task, state)函数
set_task_state(task,state);
1.5进程树
通过下面的代码获得父进程的进程描述符
struct task_struct *my_parent=current->parent;
同样可用一下方式访问子进程
struct task_struct *task;
struct list_head *list;
list_for_each(list, ¤t->children) {
task =list_entry(list, struct task_struct, sibling); /* task 现在指向当前的某个子进程 */
}
init进程的进程描述符是作为init_task静态分配的。下面的代码可以很好地演示所有进程之间的关系:
struct task_struct *task;
for (task = current; task != &init_task;tasktask = task->parent) ;
/* task 现在指向init */
实际上,你可以通过这种继承体系从系统的任何一个进程出发查找到任意指定的其他进程。但大多数时候,只需要通过简单的重复方式就可以遍历系统中的所有进程。这非 常容易做到,因为任务队列本来就是一个双向的循环链表。对于给定的进程,获取链表中的下一个进程:
list_entry(task->tasks.next, struct task_struct, tasks)
获取前一个进程的方法与之相同:
- list_entry(task->tasks.prev, struct task_struct, tasks)
这两个例程分别通过next_task(task)宏和prev_task(task)宏实现。而实际上,for_each_ process(task)宏提供了依次访问整个任务队列的能力。每次访问,任务指针都指向链表中的下一个元素:
- struct task_struct *task;
- for_each_process(task) {
- /* 它打印出每一个任务的名称和PID*/
- printk("%s[%d]\n", task->comm, task->pid);
- }
特别提醒 在一个拥有大量进程的系统中通过重复来遍历所有的进程代价是很大的。因此,如果没有充足的理由(或者别无他法),别这样做。
2.进程创建