linux 阻塞等待

        对于一个进程"睡眠"意味着什么? 当一个进程被置为睡眠, 它被标识为处于一个特殊的状态并且从调度器的运行队列中去除. 直到发生某些事情改变了那个状态, 这个进程将不被在任何 CPU 上调度, 并且, 因此, 将不会运行. 一个睡着的进程已被搁置到系统的一边, 等待以后发生事件.

        当你运行在原子上下文时不能休眠,如果在获得自旋锁情况下,在中断处理函数中;当进程从休眠中醒来时,必须对等待的条件进行检查,因为有可能是信号或超时等机制唤醒休眠进程的。

在linux中,内核等待队列由一个等待队列头来管理 ,即wait_queue_head_t类型结构;

定义和初始化等待队列:

静态:DECLARE_WAIT_HEAD(name);

动态:

wait_queue_head_t my_queue;

init_waitqueue_head(&my_queue);

休眠函数:

wait_event(my_queue,condition);       不可中断休眠,建议不用;

 wait_event_interruptible(my_queue,condition);     可以被信号中断休眠,返回非零值表示休眠是被中断的;

 wait_event_timeout(my_queue, condition, timeout);

  wait_event_interruptible_timeout(my_queue, condition, timeout);   超时时,返回0

        其中my_queue时要用的等待队列头,condition时休眠前后所要求值的布尔量,如果在休眠前条件为真,则不休眠,否则休眠,被唤醒时检查该条件,如果条件为真则继续执行,否则保持继续休眠;在timeout中如果时间超时,则会继续运行接下来程序,而不会去管condition是否为真;

唤醒休眠函数:

 wake_up(&my_queue);    唤醒所有等待队列上的等待进程;

 wake_up_interruptible(&my_queue);    唤醒可中断休眠;

wait_event不能通过wake_up_interruptible来唤醒;

测试程序:

1.wait_queue

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/device.h>
wait_queue_head_t my_queue;
int flag = 0;
struct cdev cdev;
dev_t num;
static int dev_open(struct inode * inode, struct file * file)
{
       printk(KERN_ERR"Open the cdev.\n");
       return 0;
}

static ssize_t dev_read( struct file *file, char *buf, size_t count, loff_t * pp)
{
       ssize_t ret = 0;
       printk("read .......\n");

       flag = 0;
       wait_event_interruptible(my_queue, flag != 0);
       flag = 0;
       if (ret < 0)
              printk("awake from sleep by signal.\n");
       printk("awake ...............\n");

       return 2;
}

static ssize_t dev_write(struct file *file, char *buf, size_t count , loff_t *pp)
{
       printk("wite...... and call wake up.....\n");
       flag = 1;
       wake_up(&my_queue);
       return 1;
}

static int dev_release(struct inode * inode, struct file *file)
{
       return 0;
}

struct file_operations ops = {
       .owner = THIS_MODULE,
       .open = dev_open,
       .read = dev_read,
       .write = dev_write,
       .release = dev_release,
};
static struct class myclass= {
       .name = "test_dev",
       .owner = THIS_MODULE,
};
static int __init dev_init(void)
{
       int val = 0;

        init_waitqueue_head(&my_queue);

       val = alloc_chrdev_region(&num,0,1, "test_dev");
       if (val)
              return 1;
       cdev_init(&cdev, &ops);
       cdev_add(&cdev, num,1);
       class_register(&myclass);
       device_create(&myclass, NULL, num,NULL,"test_dev");

       return 0;
}

static void __exit dev_exit(void)
{
       device_destroy(&myclass,num);
       class_unregister(&myclass);
       cdev_del(&cdev);
       unregister_chrdev_region(num, 1);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");

2.wait_write

#include <stdio.h>
#include <fcntl.h>

void main(void)
{
       int fd = 0;
       char buf[2];

       fd = open("/dev/test_dev", O_RDWR);
       if (fd < 0)
              printf("can not open test_dev.\n");
       write(fd, buf, sizeof(buf));
       close(fd);
}

3.wait_read

#include <stdio.h>
#include <fcntl.h>
void main(void)
{
       int fd;
       char buf[10];
       fd = open("/dev/test_dev",O_RDWR);
       if (fd < 0) {
              printf("can not open test_dev.\n");
       }
       read(fd,buf, sizeof(buf));
       close(fd);
}

休眠函数低层实现过程:

主要数据结构:

struct wait_queue_head_t 

{

spinlock_t lock;

struct list_head task_list;

}

struct wait_queue_t 

{

unsigned int flags;

void *private;

wait_queue_func_t func;

struct list_head task_list;

}

其中struct wait_queue_head_t为休眠队列列表头,面struct wait_queue_t为列表成员;

低层实现步骤:

1.通过DECLARE_WAIT_HEAD(queue)定义一个wait_queue_head_t 变量,通过DECLARE_WAITQUEUE(wait, current)来定义并初始化一个wait_queue_t变量;

#define DECLARE_WAITQUEUE(name, tsk)     (wait_queue_t  name= __WAITQUEUE_INITIALIZER(name, tsk))   

#define __WAITQUEUE_INITIALIZER(name , tsk)  {

.private = tak,

.func = default_wake_function,

task_list = {NULL, NULL},

}

2. 通过add_wait_queue(&queue, &wait)将wait加入到queue等待队列中;

3.通过set_current_state函数设置当时进程状态为休眠态,休眠态主要可以分为TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE.

4.运行任务调度函数schedule(),在任务高度前会先判断休眠等待的条件是否满足,如果已经满足则不休眠,这样可以防止丢掉这一次被唤醒的机会,从而进行更长时间休眠;对于可中断休眠,在进行任务高度前还会判断当前进程中是否有等待信号;

if (condition )

break;

if (!signal_pending(current)

schedule();

5.运行完休眠后将通过remove_wait_queue(&queue,&wait)将wait移出等待队列,并将当前进程状态设置回TASK_RUNNING;

/*摘自深入理解linux内核*/

schedule 函数是用来实现进程调度,它从运行队列链表中找到一个进程,并将CPU分配给这个进程。schedule可分直接调用和延迟调用 方式 ;

直接调用方式:

如果当前进程因不能获得必要的资源而要立刻被阻塞,就可以使用直接调用方式,直接调用方式 的步骤:

1.把当前current进程插入到合适的等待队列;

2.把current进程状态改成TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE; 

3.调用schedule();

4.检查资源是否可用,如果不可用就转到第二步;

5.一但资源可用,就从等待队列中删除current进程;


这些过程一般会放在do while循环里实现,内核会反复的检测资源是否可用,如果不可以,就调用 schedule进行任务调度,当调度程序再次允许把CPU分配给这个进程时,要重新检查资源是否可用,这个过程和wait_event过程 相似;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值