Linux使用进程描述符数据结构记录现场信息,然后给予进程描述符管理进程,包括进程的创建、调度、消亡等操作。
进程除了包括运行着的程序,还包括系统资源(当前CPU现场、调度信息、进程间关系等)。记录这些线程信息的数据结构就是进程描述符 task_struct(include/linux/sched.h中)
每个进程都有一个进程描述符,记录以下重要信息:进程标识符、进程当前状态、栈地址空间、内存地址空间、文件系统、打开的文件、信号量等。
获取方式:
在x86体系中,通过SP寄存器可以快速获取当前进程栈的位置;linux在栈的末端存放了一个特殊的数据结构thread_info,thread_info中存放了指向task_struct的指针。根据这个原理,首先当前进程通过SP寄存器获取栈的位置,然后根据栈大小(一般为1-2页)获取thread_info的地址,最后通过thread_info获取当前进程的地址。
linux不仅有进程ID,而且给每个线程也分配了线程ID。对于task_struct数据结构的成员来说,pid是线程ID,tgid是线程的进程ID(该进程也叫线程组长)。
进程状态:
调度程序根据进程状态决定是否调度进程,linux是用概念bitmap(位图)表示进程状态,一共有11种状态;这些状态可以分为三类:运行态、睡眠态、退出态。只有运行态的进程才能被调度程序调度;进程等待某个资源时处于睡眠态(可中断态、不可中断态);进程退出时处于退出态(僵尸态、死亡态)。其他的进程状态还包括停止态、跟踪态等,这些状态处于特定的使用场景中,就不介绍了。
- 运行态:进程在cpu上运行或者等待运行。
- 睡眠态:睡眠态分为可中断态和不可中断态。进程因为等待某个资源而处于睡眠状态。这两者的区别是不可中断态忽略发送过来的唤醒信号量,因为这个状态的进程获取了重要的系统资源,因此不能被轻易打断,该状态较少使用。
- 退出态:退出态分为僵尸态和死亡态。进程完成使命退出后处于僵尸态,此时进程的资源已经被释放,仅仅保留了task_struct结构(父进程可能使用);而死亡态不仅释放了所有资源,并且连task_struct结构也释放了。
系统给每个状态分配了一个字母缩写“RSDTtZXxKWP”,对应关系如下图所示。
#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define __TASK_STOPPED 4
#define __TASK_TRACED 8
/* in tsk->exit_state */
#define EXIT_ZOMBIE 16
#define EXIT_DEAD 32
/* in tsk->state again */
#define TASK_DEAD 64
#define TASK_WAKEKILL 128
#define TASK_WAKING 256
#define TASK_PARKED 512
#define TASK_STATE_MAX 1024#define TASK_STATE_TO_CHAR_STR "RSDTtZXxKWP"
进程栈
linux系统为每个用户进程分配了两个栈:用户栈和内核栈。当一个进程在用户空间执行时,系统使用用户栈;当在内核空间执行时,系统使用内核栈。由于内核栈地址空间的限制,内核栈不会分配很大的空间。此外,内核进程只有内核栈,没有用户栈。
当进程从用户空间陷入到内核空间时,首先,操作系统在内核栈中记录用户栈的当前位置,然后将栈寄存器指向内核栈;内核空间的程序执行完毕后,操作系统根据内核栈中记录的用户栈位置,重新将栈寄存器指向用户栈。由于每次从内核空间中返回时,内核栈肯定已经使用完毕,所以从用户栈切换到内核栈时,只需要简单的将栈寄存器指向内核栈顶即可。
linux进程管理(1)---进程描述符 https://blog.youkuaiyun.com/luomoweilan/article/details/21196093
为了进程管理,内核必须对每个进程所做的事情进行清楚的描述。比如内核需要知道进程的优先级,进程当前的状态,在挂起和恢复进程的时候,需要对进程进行相应的操作。进程描述符还描述了进程使用的地址空间,访问的文件等等,这些都是进程描述符的作用。包括了很多进程属性的字段,还有一些字段包括了指向其他数据结构的指针,如下图:
- TASK_RUNNING: 要么在CPU上执行,要么准备执行。
- TASK_INTERRUPTIBLE: 进程被挂起(睡眠),直到某个为真的条件触发,产生一个硬件中断,释放进程正等待的系统资源,或传递一个信号都可以唤醒进程。
- TASK_UNINTERRUPTIBLE: 不可中断的等待状态,与把信号传递给睡眠进程不能改变它的状态1。
- TASK_STOPPED: 进程的执行被暂停,当收到SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU信号后,进入暂停状态。
- TASK_TRACED: 跟踪状态,进程的执行由debugger程序暂停。
- TASK_ZOMBIE: 进程执行被终止,但是父进程还没有发布wait4或waitpid系统调用返回有关死亡进程的信息。
- TASK_DEAD: 僵死撤销状态。
task_struct 结构体:
<include/linux/sched.h>
struct task_struct {
// -1表示不可运行,0表示可运行,大于0表示停止
volatile long state;
void *stack;
atomic_t usage;
// 每进程标志,上下文定义
unsigned int flags;
unsigned int ptrace;
// 大内核锁的深度
int lock_depth;
#ifdef CONFIG_SMP
#ifdef __ARCH_WANT_UNLOCKED_CTXSW
int oncpu;
#endif
#endif
// 优先级
int prio, static_prio, normal_prio;
unsigned int rt_priority;
const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;
#ifdef CONFIG_PREEMPT_NOTIFIERS
/* 同步的通知者 */
struct hlist_head preempt_notifiers;
#endif