《linux内核设计与实现》
进程管理
1、进程
进程是处于执行期的程序,进程不仅仅包括可执行代码,还需保安打开文件,挂起信号等其他资源。
线程是在进程中活动的对象,在linux中线程只是一种特殊的进程。
一个进程是在创建他的时候开始存活,通常是调用fork(注:现在的fork实际是用clone实现的)的结果,调用fork的叫父进程,创建的叫子进程。fork调用结束后,返回,父进程恢复执行,子进程开始执行。紧接着调用exec这组函数创建新的地址空间,将新的程序载入其中。最终进程通过exit退出执行,并释放资源,进入僵死,等待父进程调用wait或waitpid为止。
2、进程描述符及任务结构
内核把进程的列表存放在任务队列中,这是一个双向循环链表,其每项的类型都是task_struct(具体存放在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;
int lock_depth; / BKL lock depth */
…
}
2.1 分配进程描述符
linux通过slab分配器分配task_struct结构,预先分配能够避免资源损耗。
如今的slab分配器是动态生成task_struct,因此只需要在栈底或者栈顶生成一个thread_info即可,该结构中已经包含了一个指向实际任务task_struct的指针task。
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;
void __user sysenter_return;
#ifdef CONFIG_X86_32
unsigned long previous_esp; / ESP of the previous stack in case of nested (IRQ) stacks/
__u8 supervisor_stack[0];
#endif
unsigned int sig_on_uaccess_error:1;
unsigned int uaccess_err:1; / uaccess failed */
};
2.2 进程描述符的存放
内核通过唯一的进程标识值或PID标识每个进程,其是pid_t类型,底层其实也是一个int类型。PID默认最大值为32768