Linux内核-进程
来源:互联网 发布:美加净护手霜 知乎 编辑:程序博客网 时间:2024/04/28 14:05
进程的目的就是担当分配资源的实体,一个进程又几个用户线程程。每个线程代表一个执行流。线程的实现方法有三种:1、用户级线程;2、内核级线程;3、混合线程模型。Linux内核中的进程和线程都用相同的数据结构task_struct表示;线程是特殊的进程,共享同一地址空间、共同合作。
进程描述符都是task_struct,包含了与进程相关的所有信息。一般来说能被调度的每个执行上下文都必须拥有自己的task_struct,即使是轻量级线程。
进程状态
可执行状态(TASK_RUNNING):处于这个状态要么在CPU上执行,要么准备执行。
可中断的睡眠状态(TASK_INTERRUPTIBLE):进程被挂起,指导某个条件变为真,产生一个硬件中断,释放进程正在等待的资源或传递一个信号把进进程唤醒,从而使等待的进程回到TASK_RUNNING。
不可中断的睡眠状态(TASK_UNINTERRUPTIBLE):不能通过信号唤醒。
暂停状态(TASK_STOPPED ):进程收到SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU信号进入暂停状态。
跟踪状态( TASK_TRACED):进程的执行已由debugger程序暂停。
僵死状态(EXIT_ZOMBIE):进程的执行被终止,但是父进程还没有发布wait4()或waitpid()系统调用来返回关于死亡进程的信息。发布wait()系统调用之前,内核不能丢弃包含在死亡进程描述符中的数据,因为父进程还需要它。
僵死撤销状态(EXIT_DEAD):父进程刚发布wait4()或waitpid()系统调用,因而进程被删除。
task_struct结构
Linux用task_struct结构表示进程,2.6内核的task_struct结构相对于2.4内核有很大变化。该结构记录了进程的重要信息,与进程调度有关的信息包括:
(1)state
进程状态由state成员变量表示。一个进程共有7种可能状态,分别是:TASK_RUNNING、TASK_INTERRUPTIBLE、TASK_UNINTERRUPTIBLE、TASK_STOPPED、TASK_TRACED、EXIT_ZOMBIE和EXIT_DEAD
(2)timestamp
进程发生调度事件的时间(单位是
(3)prio,
进程的优先级和静态优先级。Prio表示进程的动态优先级,与2.4内核中goodness()的计算结果相当。在0~MAX_PRIO-1之间取值(MAX_PRIO
(4)sleep_avg
进程的平均等待时间,在0到NS_MAX_SLEEP_AVG之间取值,初值为0,相当于进程等待时间与运行时间的差值。它是动态优先级计算的关键因子,sleep_avg越大,计算出来的进程优先级也越高。
(5)interactive_credit
该变量表示进程的交互程度。在-CREDIT_LIMIT到CREDIT_LIMIT+1之间取值,初始值为0,而后根据不同的条件加1减1,一旦该值超过CREDIT_LIMIT,即表示该进程是交互进程。
(6)array
指向当前CPU的active就绪进程队列。
(7)run_list
进程通过这个list_head变量连接自己到prio_array数组中queue队列,这样相同优先级的进程连接成一个双向列表,表头为prio_array结构中的queue变量。
进程描述符
Unix系统通过pid来标识进程,linux把不同的pid与系统中每个进程或轻量级线程关联,而unix程序员希望同一组线程具有共同的pid,遵照这个标准linux引入线程组的概念。一个线程组所有线程与领头线程具有相同的pid,存入tgid字段,getpid()返回当前进程的tgid值而不是pid的值。
Linux把thread_info(线程描述符)和内核态的线程堆栈存放在一起,这块区域通常是8192K(占两个页框),其实地址必须是8192的整数倍。从图中看出thread_info和task_struct结构互联。
union thread_union {
struct thread_info thread_info;
unsigned long stack[2048];
}
esp是寄存器CPU栈指针。用来存放栈顶单元的地址。栈是从高地址向下生长,用户态刚切换到内核态以后,进程的内核栈总是空的,esp指向顶端。一旦有数据写入,esp就递减。thread_info是52字节大小,所以内核栈最大能扩大8140个字节。
可以很容易从esp寄存器获得当前正在CPU上运行的进程thread_info结构的地址。只要取高13位,低8位置0就行了。其实宏current_thread_info()实现的功能,因为thiread_info->task偏移地址为0,所以该宏等价于current_thread_info()->task。
movl $0xffffe000 %ecx
andl%esp %ecx
movl %ecx,p //地址存在p中
注:有关内核栈和用户栈的内容参考本博客:linux内核-进程内核栈、用户栈
进程运行队列(就绪队列)
Linux内核定义了一个list_head数据结构(有关listh_head结构参考本博客list_head结构),字段head和prev分别表示通用的双向链表向前和向后的指针元素。每个task_struct包含一个list_head类型的run_list字段。
在Linux
每个CPU的就绪进程队列由一个struct
描述优先级就绪数组的数据结构是prio_array_t,定义为:
struct
};
Runqueue结构中另两个重要的成员变量是best_expired_prio和expired_timestamp。前者记录expired
进程调度由schedule()函数实现。首先,schedule()利用下面的代码定位优先级最高的就绪进程。schedule()通过调用sched_find_first_bit()函数找到当前CPU就绪进程队列runqueue的active进程数组中第一个非空的就绪进程链表。这个链表中的进程具有最高的优先级,schedule()选择链表中的第一个进程作为调度器下一时刻将要运行的进程。如果prev(当前进程)和next(将要运行的进程)不是同一个进程,schedule()调用context_switch()将CPU切换到next进程运行。
prev
array
idx
queue
next
进程等待队列
Linux等待队列在内核中有很多用途,尤其在中断处理、进程同步以及定时。例如等待一个磁盘操作的终止,等待释放资源,或等待时间经过的固定的时间间隔。等待队列实现了事件上的条件结构:希望等待特定事件的进程把自己放进合适的等待队列,并放弃控制权。因此等待队列表示一组睡眠的进程,当某一条件为真时,由内核唤醒它们。
等待队列由双向链表实现。
struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
}; //等待队列的头结点,不包含其他信息
typedef struct__wait_queue_head wait_queue_head_t;
struct __wait_queue {
unsigned int flags; //标识是互斥进程还是非互斥进程
#defineWQ_FLAG_EXCLUSIVE 0x01 /* 表示等待进程想要被独占地唤醒 */
void *private; /* 指向等待进程的task_struct实例 */
wait_queue_func_t func; /* 用于唤醒等待进程 */
sruct list_head task_list; /* 用于链表元素,将wait_queue_t链接到wait_queue_head_t */
};
typedef struct__wait_queue wait_queue_t;
有关等度列的一些操作:
init_waitqueue_entry():动态初始化
DEFINE_WAIT():静态初始化
add_wait_queue():把进程加入等度队列第一个位置
add_wait_queue_exclusive():把进程加入等度队列最后一个位置
remove_wait_queue():移除队列
sleep_on():把当前进程设为task_uniterruptible,并插入等待队列,然后调用shedule()函数
sleep_on_timeout():睡眠进程的时候可以设一个时间间隔,过了间隔以后,由内核唤醒
prepare_wait():提供了另一种途径睡眠进程
wake_up()、wake_up_nr()、wake_up_all():唤醒进程
进程切换
内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程执行。这种行为称为进程切换、任务切换或上下文切换。尽管每个进程都拥有属于自己的地址空间,但所有进程必须共享CPU寄存器。因此,在恢复一个进程的执行之前,内核必须确保每个寄存器装了挂起进程时的值。进程恢复前必须装入寄存器的一组数据称为硬件上下文,它的一部分存在TSS段,而剩余部分存放在内核态堆栈中。
在每次进程切换时,被替换的硬件上下文必须保存在别处,不能像Intel原始设计那样把它保存在TSS中,因为Linux为每个器而不是每个进程使用TSS。因此每个进程描述符包含一个类型thread_struct的thread字段,来保存进程切换时的硬件上下文。
进程切换发生在schedule()函数,包括两步:(1)切换全局目录以及安装一个新的地址空间;(2)切换内核态堆栈和硬件上下文。(这一步由宏执行swith_to,又调用__swith_to()函数)
- linux内核进程
- Linux内核进程切换
- Linux进程内核栈
- linux 内核进程 线程
- Linux内核进程调度
- Linux进程内核栈
- Linux内核-进程
- Linux内核-进程退出
- Linux内核-进程wait
- Linux内核-进程退出
- linux内核-进程
- linux内核-进程调度
- Linux内核进程切换
- Linux内核-进程
- 【Linux 内核】进程管理
- 【Linux 内核】进程调度
- linux内核【进程】
- Linux内核-进程管理
- NS2(NS-2.33)中无线超宽带模块移植
- Chapter4 永久数据集的创建及使用
- EXIF 方向参数 Orientation
- java----网络编程
- Eclipse for Java 开发环境配置
- Linux内核-进程
- ajax基本封装
- Linux下C编程笔记之环境配置
- asp.net页面刷新导致重复提交
- IOS常用代码
- 【转】java Socket套接字
- 鉴客 Android Intent 用法全面总结
- js parseInt方法
- 04_Spring2.3_AOP_xml