linux内核中经常需要进行进程的调度
struct wait_queue_head {
spinlock_t lock;
struct list_head head;
};
struct wait_queue_head *wq_head,
#define wait_event(wq_head, condition) \
do { \
might_sleep(); \
if (condition) \
break; \
__wait_event(wq_head, condition); \
} while (0)
定义了一个 wait结构,设置进程睡眠。如果有其他进程唤醒这个进程后,判断条件是否满足,
如果满足,从睡眠链表中删除wait对象退出睡眠,否则进程继续睡眠。
系列函数:
wait_event(wq_head, condition); 睡眠不可中断,不可接收信号处理;如ctrl +c等
wait_event_timeout(wq_head, condition, timeout) ; 条件满足 或者时间到达,进程回复
wait_event_interruptible(wq_head, condition) 可以接收信号打断睡眠,
wait_event_interruptible_timeout(wq_head, condition, timeout) 可以打断并且时间到了,进程回复
TASK_RUNNING: 正在运行或处于就绪状态:就绪状态是指进程申请到了CPU以外的其他所有资源,提醒:一般的操作系统教科书将正在CPU上执 行的进程定义为RUNNING状态、而将可执行但是尚未被调度执行的进程定义为READY状态,这两种状态在Linux下统一为 TASK_RUNNING状态.
TASK_INTERRUPTIBLE: 处于等待队伍中,等待资源有效时唤醒(比如等待键盘输入、socket连接、信号等等),但可以被中断唤醒.一般情况下,进程列表中的绝大多数进程都处于 TASK_INTERRUPTIBLE状态.毕竟皇帝只有一个(单个CPU时),后宫佳丽几千;如果不是绝大多数进程都在睡眠,CPU又怎么响应得过来.
TASK_UNINTERRUPTIBLE:处于等待队伍中,等待资源有效时唤醒(比如等待键盘输入、socket连接、信号等等),但不可以被中断唤醒.
TASK_ZOMBIE:僵死状态,进程资源用户空间被释放,但内核中的进程PCB并没有释放,等待父进程回收.
TASK_STOPPED:进程被外部程序暂停(如收到SIGSTOP信号,进程会进入到TASK_STOPPED状态),当再次允许时继续执行(进程收到SIGCONT信号,进入TASK_RUNNING状态),因此处于这一状态的进程可以被唤醒.
分析代码:
条件满足后调用 __wait_event();
#define wait_event(wq_head, condition) \
do { \
might_sleep(); \
if (condition) \
break; \
__wait_event(wq_head, condition); \
} while (0)
TASK_UNINTERRUPTIBLE 表示进程运行状态
#define __wait_event(wq_head, condition) \
(void)___wait_event(wq_head, condition, TASK_UNINTERRUPTIBLE, 0, 0, \
schedule())
目的:创建一个等待条目 struct wait_queue_entry __wq_entry;
初始化: init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0); 获取当前进程id
调用: prepare_to_wait_event
#define ___wait_event(wq_head, condition, state, exclusive, ret, cmd) \
({ \
__label__ __out; \
struct wait_queue_entry __wq_entry; //创建一个等待条目
long __ret = ret; /* explicit shadow */ \
//初始化 创建的条目, 获取当前进程的pid
init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0); \
for (;;) { \
long __int = prepare_to_wait_event(&wq_head, &__wq_entry, state);\
\
if (condition) \
break; \
\
if (___wait_is_interruptible(state) && __int) { \
__ret = __int; \
goto __out; \
} \
\
cmd; \
} \
finish_wait(&wq_head, &__wq_entry); \
__out: __ret; \
})
void init_wait_entry(struct wait_queue_entry *wq_entry, int flags)
{
wq_entry->flags = flags;
wq_entry->private = current; //保存了当前进程的pid
wq_entry->func = autoremove_wake_function; //系统默认的 处理函数
INIT_LIST_HEAD(&wq_entry->entry);
}
prepare_to_wait_event 实现:
将 新创建的等待队列条目放入等待队列链表中 :
WQ_FLAG_EXCLUSIVE : 区分插入睡眠链表中的方式,队列或者栈;
设置当前进程运行状态 就是current->state
long prepare_to_wait_event(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry, int state)
{
unsigned long flags;
long ret = 0;
spin_lock_irqsave(&wq_head->lock, flags);
if (unlikely(signal_pending_state(state, current))) {
list_del_init(&wq_entry->entry);
ret = -ERESTARTSYS;
} else {
if (list_empty(&wq_entry->entry)) {
if (wq_entry->flags & WQ_FLAG_EXCLUSIVE)
__add_wait_queue_entry_tail(wq_head, wq_entry);
else
__add_wait_queue(wq_head, wq_entry);
}
set_current_state(state);
}
spin_unlock_irqrestore(&wq_head->lock, flags);
return ret;
}
#define __wait_event_interruptible_exclusive(wq, condition) \
___wait_event(wq, condition, TASK_INTERRUPTIBLE, 1, 0, \
schedule())
WQ_FLAG_EXCLUSIVE 值就来自于上面标红的1传入的.区别排他性的等待;
测试举例:
创建一个进程,调度一下新创建的进程, 老进程睡眠, 新创建的进程中唤醒;
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/wait.h>
int flag = 0;
static struct task_struct *test_task, *poldtask;
wait_queue_head_t test_que;
//static DECLARE_WAIT_QUEUE_HEAD(test_que);
int test_thread(void * testPtr)
{
printk("cj the old task state :%ld \n", poldtask->state);
printk("cj the thread current pid : %d\n", current->pid);
flag = 1;
wake_up_interruptible(&test_que);
return 0;
}
static int test_wait_init(void )
{
struct wait_queue_entry data;
int i = 0;
//创建线程
test_task = kthread_create(test_thread, NULL, "test_task");
printk("the pid of test_task is %d \n", test_task->pid);
printk("current pid is %d \n", current->pid);
init_waitqueue_head(&test_que);
init_waitqueue_entry(&data, current);
add_wait_queue(&test_que, &data);
poldtask = current;
wake_up_process(test_task); //唤醒新创建的进程
schedule_timeout_interruptible(10*10); //调度一下
wait_event_interruptible(test_que, flag); //默认flag == 0 会进入睡眠
for(; i<10; i++)
printk("test_wait_init end %d \n", i );
return 0 ;
}
static void test_wait_exit(void)
{
}
module_init(test_wait_init);
module_exit(test_wait_exit);
MODULE_LICENSE("GPL");