1、进程休眠
(1)进程有三种基本状态:就绪态、阻塞态、运行态。 <1>阻塞态:进程缺少除了CPU之外的某些资源,因此该进程不能被运行,被阻塞住了不能被CPU调度; <2>就绪态:进程分配到了除CPU之外的所有资源,等待CPU调度执行; <3>运行态:进程获得CPU资源,程序在CPU上运行; (2)进程休眠:进程休眠就是进程因为缺少除了CPU之外的某些资源而进入阻塞态,会从调度器的运行队列中移走该进程,并把进程放到等待资源的队列中,直到分配到资源从而被唤醒,再次进入到就绪态,等待CPU调度;
2、进程休眠的注意事项
进程进入休眠是很容易的,只要申请不到资源都可能休眠,但是我们要保证进程以一种安全的方式进入休眠,也就是进程进入休眠后, 将来能被成功的唤醒; (1)永远不要在原子上下文中进入休眠,原子操作本身就是要不可中断的一次性执行完; (2)不能在拥有锁的时候进入休眠,否则可能会造成死锁;比如A进程在拥有自旋锁时进入休眠等待B进程唤醒,B进程需要先获得自旋锁进行一些操作才能唤醒A进程, B进程等待A进程释放自旋锁,A进程等待B进程唤醒后才能释放锁,这就死锁了; (3)拥有信号量时进程是可以进入休眠的,但是要非常注意:用于信号量而休眠的代码必须很短,并且还要确保拥有信号量斌不会阻塞最终唤醒我们自己的那个进程; (4)进程被唤醒时,无法知道休眠了多长时间或者休眠期间都发生了什么事,所以在进程被唤醒后不要对状态进行任何假定,必须去检查我们等待的条件是否为真,因为 同时可能有别的进程因为等待同一资源而休眠,我们等待的资源可能被别的进程拿走; (5)除非知道有其他进程会在其他地方唤醒我们,否则进程不能进入休眠;
3、进程的唤醒
(1)要唤醒进程,前提是要知道哪些进程进入休眠,是因为等待什么资源而进入休眠; (2)在linux中,维护者一个称为等待队列的数据结构,相当于一个进程链表;将等待同一资源的进程放到同一个等待队列里,将来资源有空闲时就从 相应的等待队列中唤醒一个线程;
4、等待队列
4.1、初始化等待队列
DECLARE_WAIT_QUEUE_HEAD(name)
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue)
4.2、将进程添加到等待队列
wait_event(wq, condition);
wait_event_timeout(wq, condition, timeout);
wait_event_interruptible(wq, condition);
(1)举例:wait_event_interruptible(wq, havedata==1); (2)分析:在进程中执行上面的代码,进程会被加入到wq等待队列中,直到将来被wake_up后,并且havedata==1时才能被唤醒;
4.3、唤醒等待队列中的进程
#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
上面的两个唤醒函数和添加到等待队列的函数要对应使用,比如wake_up(x)对应wait_event(wq, condition);
5、驱动代码中等待队列的使用
wait_queue_head_t rwq,wwq;
int havedata = 0;
static int hello_init(void)
{
······
init_waitqueue_head(&rwq);
init_waitqueue_head(&wwq);
······
}
static ssize_t hello_read (struct file *filep, char __user *buf, size_t size, loff_t *pos)
{
int error;
wait_event_interruptible(rwq,havedata == 1);
if(size > strlen(kbuf))
{
size = strlen(kbuf);
}
if(copy_to_user(buf,kbuf, size))
{
error = -EFAULT;
return error;
}
havedata = 0;
wake_up_interruptible(&wwq);
return size;
}
static ssize_t hello_write (struct file *filep, const char __user *buf, size_t size, loff_t *pos)
{
int error;
wait_event_interruptible(wwq,havedata == 0);
if(size > KMAX_LEN)
{
size = KMAX_LEN;
}
memset(kbuf,0,sizeof(kbuf));
if(copy_from_user(kbuf, buf, size))
{
error = -EFAULT;
return error;
}
printk("%s\n",kbuf);
havedata = 1;
wake_up_interruptible(&rwq);
return size;
}
上面是摘抄的部分驱动代码,在读写接口中实现休眠和唤醒;
|