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, ¤t->children) {
/*
**error:
** task = list_entry(list, struct task_struct, children);
*/
task = list_entry(list, struct task_struct, sibling);
}