1.进程

       进程是处于执行期间的程序以及执行该程序需要资源的总称。在操作系统中,提供了两种虚拟机制:虚拟处理器和虚拟内存。这两种抽象机制目的是让进程"独立"享有处理器以及整个内存资源。

       线程是进程活动中的对象,每个线程拥有独立的程序计数器、进程栈和一组进程寄存器。内核调度的对象是线程,而不是进程。在Linux中进程与线程并不特殊区分,线程只是一种特殊的进程。

2.进程描述符

       进程描述符,指的是包含完整描述一个进程所有资源的数据结构。该结构体struct task_struct定义如下,在32位机器上,它大约会有1.7K字节,原因是它包含了一个进程所有资源的信息。

linux/sched.h   
struct task_struct {
    volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
    void *stack;
    atomic_t usage;
    unsigned int flags;	/* per process flags, defined below */
    unsigned int ptrace;
    ...
    struct list_head tasks;
    ...
    /*
	 * pointers to (original) parent process, youngest child, younger sibling,
	 * older sibling, respectively.  (p->father can be replaced with
	 * p->real_parent->pid)
	 */
	struct task_struct __rcu *real_parent; /* real parent process */
	struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
	/*
	 * children/sibling forms the list of my natural children
	 */
    struct list_head children;	/* list of my children */
    struct list_head sibling;	/* linkage in my parent's children list */
    ...
};

       内核将进程会放置在一个双向循环链表的任务队列中。因此可以通过如下方式,遍历所有进程。

#define for_each_process(p) \
	for (p = &init_task ; (p = next_task(p)) != &init_task ; )

for_each_process(task) {
    printk("%s[%d]\n", taks->comm, task->pid);
}    

       为了方便,引入一个新的结构体struct thread_info。该结构体位于在栈的底端(或顶端)。在x86系统中,可以通过把栈指针的后13(栈的大小为8K时)个有效位屏蔽掉,计算thread_info的偏移,进而可以很方便的得到task_struct的地址。

asm/thread_info.h
struct thread_info {
	struct task_struct	*task;		/* main task structure */
	__u32			flags;		/* low level flags */
	__u32			status;		/* thread synchronous flags */
	__u32			cpu;		/* current CPU */
	int			saved_preempt_count;
	mm_segment_t		addr_limit;
	void __user		*sysenter_return;
	unsigned int		sig_on_uaccess_error:1;
	unsigned int		uaccess_err:1;	/* uaccess failed */
};

;current_thread_info()函数中,计算thread_info的地址
movl &-8192, %eax
andl  %esp, %eax

current_thread_info()->task

3.进程状态

       在进程描述符的state域描述了进程所处的状态,在系统中,进程必然处于如下5中状态中的一种。需要注意的是TASK_RUNNING,指的是该进程可以执行,而并不是正在执行。TASK_INTERRUPTIBLE与TASK_UNINTERRUPTIBLE,指的是进程正在睡眠,区别在于是否可以通过信号提前唤醒。

       1)TASK_RUNNING(可执行)进程是可执行的;正在执行或者正在运行队列中等待执行。

       2)TASK_INTERRUPTIBLE(可中断)进程正在睡眠,等待某些条件的达成;也会因为接收到信号而提前被唤醒投入运行。

       3)TASK_UNINTERRUPTIBLE(不可中断)进程正在睡眠,等待某些条件的达成;不会因为接收到信号而提前被唤醒投入运行。

       4)TASK_ZOMBIE(僵死)进程已经结束,父进程未调用wait4()系统调用,子进程的进程描述符仍然保留着。

       5)TASK_STOPPED(停止)进程没有投入运行、也不能投入运行。在调试期间接收到任何信号都会进入这种状态。

linux/sched.h
#define TASK_RUNNING		0
#define TASK_INTERRUPTIBLE	1
#define TASK_UNINTERRUPTIBLE	2
#define __TASK_STOPPED		4

set_task_state(current, state)
set_current_state(state)

4.进程家族树

       Linux进程中存在一个明显的继承关系。所有的进程都是PID为1的init进程的后代。系统中每个进程都有一个父进程。而每个进程可以有零个或多个子进程。 拥有相同父进程的所有进程被称为兄弟。

       在进程描述符中,每个进程都有一个parent指针,一个children链表以及一个sibling兄弟链表。可以通过如下方式遍历当前进程的子进程(若对下述链表操作有疑问,可参考我之前写的一篇文章Linux内核数据结构之链表)。注意,父进程的children->next指向的是子进程的sibling变量,而并不是子进程的children变量(如下图,pid为1的进程是其它所有2-n的父进程,即pid为2-n的进程互为兄弟进程)。

#define list_for_each(pos, head) \
    for (pos = (head)->next; pos != (head); pos = pos->next)

list_for_each(list, &current->children) {
    /*
    **error:
    **    task = list_entry(list, struct task_struct, children); 
    */
    task = list_entry(list, struct task_struct, sibling); 
}

LKD02