3.2.4 进程是如何组织的
可运行链表把TASK_RUNNING状态的进程组织了起来,而其他一些状态的进程则是通过其他的方式组织的:
TASK_STOPPED、EXIT_ZOMBIE、EXIT_DEAD三个状态是不会被链接起来的,因为访问处于这些状态的进程只通过PID或者父进程;
TASK_INTERRUPTIBLE、TASK_UNINTERRUPTIBLE两个状态被分成了很多类,每类都与一个特定的事件关联,由于通过进程状态分量不能快速的定位到进程描述符,
所以增加了一些链表来达到目的,这些链表就是等待队列(wait queues)。
3.2.4.1 等待队列
等待队列在内核中使用很多,尤其在中断处理、进程同步、定时等方面,这里只介绍进程为获取某个事件而进行的等待,
这里的事件是指磁盘操作终止、系统资源释放或固定时间到达等,
等待队列实现的是有条件的在事件上进行等待:一个进程期望获取某个事件时,它会把自己放到合适的等待队列中,然后释放控制权;
因此一个等待队列描述的是一组睡眠中的进程,在一些条件成立时内核将唤醒这些进程。
等待队列是使用struct list_head实现的双链表,每个等待队列由wait_queue_head引起:
struct _ _wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct _ _wait_queue_head wait_queue_head_t;
由于等待队列在中断处理程序以及内核主程序中会被修改,所以需要由lock来保证数据一致性,task_list为等待队列的头。
等待队列中的每个元素为wait_queue_t结构:
struct _ _wait_queue {
unsigned int flags;
struct task_struct * task;
wait_queue_func_t func;
struct list_head task_list;
};
typedef struct _ _wait_queue wait_queue_t;
等待队列中每个元素都代表了一个处于睡眠状态的进程,该进程正在等待某些事件的发生,task分量存放该进程的进程描述符,task_list用于链接处于统一等待队列的其他进程。
并不是每次有事件到来时都把相应的等待队列中进程唤醒,在有的时候,比如,如果有多个进程在等待一个需要独占访问的资源被释放,当该资源可用时只有一个进程被唤醒,资源由该进程独占使用,而其他进程继续睡眠等待该资源可用;
这样做的是为了避免“惊群”的出现,即,被唤醒的多个进程同时去竞争一个需要独占访问的资源,而该资源只能被一个进程获取到,其他进程则只能再次进入睡眠状态。
因此,有两类睡眠进程:资源独占进程(wait_queue_t结构中flags分量为1),当事件到达时,被内核有选择的唤醒;非资源独占进程(wait_queue_t中flags为0),当事件到达时,内核将全部唤醒等待该事件的这类进程。
wait_queue_t中的func分量确定了睡眠中的进程如何被唤醒。
阅读(2238) | 评论(0) | 转发(0) |