前言
为了管理进程,OS必须对每个进程所作的事情进行清楚地描述,OS使用数据结构来代表处理不同的实体,这个数据结构就是通常所说的进程描述符和进程控制块PCB,在linux中,使用task_struct结构体,每个进程都会被分配一个该结构,包含了进程的所有信息。
注:task_struct结构体被定义在<include/linux/sched.h>中
结构体成员
进程状态:
volatile long state;
//volatile确保本条指令不会因编译器的优化而省略,且要求每次直接读值
预定义的状态值:
/* Used in tsk->state: */
#define TASK_RUNNING 0x0000
//进程要么正在执行,要么准备执行
#define TASK_INTERRUPTIBLE 0x0001
//可中断的睡眠,可以通过一个信号唤醒
#define TASK_UNINTERRUPTIBLE 0x0002
//不可中断的睡眠,不可以通过信号进行唤醒
#define __TASK_STOPPED 0x0004
//进程停止执行
#define __TASK_TRACED 0x0008
//进程被追踪
/* Used in tsk->exit_state: */
#define EXIT_DEAD 0x0010
//进程的最终状态,进程死亡
#define EXIT_ZOMBIE 0x0020
//僵尸状态的进程,表示进程被终止,但是父进程还没有获取它的终止信息,比如进程有没有执行完等信息
#define EXIT_TRACE (EXIT_ZOMBIE | EXIT_DEAD)
/* Used in tsk->state again: */
#define TASK_PARKED 0x0040
#define TASK_DEAD 0x0080
//死亡
#define TASK_WAKEKILL 0x0100
//唤醒并杀死的进程
#define TASK_WAKING 0x0200
//唤醒进程
#define TASK_NOLOAD 0x0400
#define TASK_NEW 0x0800
#define TASK_STATE_MAX 0x1000
进程标识符:
pid_t pid;
pid_t tgid;
每个进程都有自己的pid,每个线程都有自己的线程id(pthread_t类型),但这是在用户空间的层面,而在内核层面中,线程其实就是进程,所以为了更好区分这些概念,一般使用task来指代内核中的进程概念,依旧用进程来指定用户空间层面的进程。
那么在内核中,每个线程都是一个task,所以每个线程都有自己的一份task_struct,而且都有自己独特的pid,那么此时tgid是干什么的那?
答:是用来说明线程是属于哪一个进程的,一个进程就是一个线程组,所以每个进程的所有线程都有着相同的tgid。当程序开始运行的时候,只有一个主线程,这个主线程的tgid就是pid,而当该主线程创建其他线程的时候,就继承了主线程的tgid。
进程内核栈:
当进程通过系统调用陷入内核时,内核代码所使用的栈并不是用户空间中的栈,而是一个内核空间的栈,也就是进程内核栈,它用于支持系统调用中的函数调用和自动变量,还用于保存一些系统调用前的应用信息,如用户空间栈指针、系统调用参数。
每个进程在创建的时候都会得到一个内核栈空间,内核栈和进程的对应关系是通过2个结构体中的指针成员完成的,其中就有tas_struct:这里的stack指向栈底。
void *stack;
//通过alloc_thread_info函数分配它的内核栈,通过free_thread_info函数释放所分配的内核栈,具体函数在fork.c中
另外一个是thread_info位于<thread.h>中,保存了线程所需的特定处理器的信息, 也就是进程所依赖体系结构的信息,以及通用的task_struct的指针:
/*
* low level task data that entry.S needs immediate access to.
* __switch_to() assumes cpu_context follows immediately after cpu_domain.
*/
struct thread_info {
unsigned long flags; /* low level flags */
int preempt_count; /* 0 => preemptable, <0 => bug */
mm_segment_t addr_limit; /* address limit */
struct task_struct *task; /* main task structure */
struct exec_domain *exec_domain; /* execution domain */
__u32 cpu; /* cpu */
__u32 cpu_domain; /* cpu domain