字符设备的信号量,阻塞和非阻塞,poll实现

信号量的结构体定义如下:
linux+v2.6.28/include/linux/semaphore.h:

struct semaphore {
    spinlock_t lock;  //自旋锁
    unsigned int count;
    struct list_head wait_list; //内核的双向链表
};


初始化信号量:

#define init_MUTEX(sem)         sema_init(sem, 1)
#define init_MUTEX_LOCKED(sem)  sema_init(sem, 0)


获取信号量:

extern void down(struct semaphore *sem);
extern int __must_check down_interruptible(struct semaphore *sem);


以上两个函数都是用来获取信号量的。不同之处在于down会导致睡眠,而且不能被信号打断。而down_interruptible也会导致睡眠,但是它可以被信号打断。

释放信号量:

extern void up(struct semaphore *sem);

/***********************************************************************************************/
为了更好的理解阻塞和非阻塞,先来看两段代码:

/******************************代码1***************************************/

/* 阻塞地从串口读取一个字符*/
fd = open("/dev/ttyS1", O_RDWR);
...
ret = read(fd, buf, 1); //当串口上有数据时才返回
if(ret > 0)
    printf("%c\n", buf);
/******************************代码2***************************************/
/* 非阻塞地从串口读取一个字符 */
fd = open("/dev/ttyS2", O_RDWR|O_NONBLOCK);
...
while(read(fd, buf, 1) != 1); //串口上没有数据也返回,所以要循环读取串口
printf("%c", buf);


阻塞是指在执行设备操作时如果不能获得资源则挂起进程,直到资源可以被获取后再进行操作。
阻塞从字面上听起来意味着低效率,实则不然,如果设备不阻塞,则用户想要获取设备资源只能不停的查询,这反而会无谓地消耗CPU资源。而阻塞访问时,不能获取资源的进程将进入休眠,它将CPU资源让给其他进程,直到资源可以被获取。
在LInux驱动程序中,可以使用等待队列来实现阻塞进程的唤醒。

等待队列结构体如下:
linux+v2.6.28/include/linux/wait.h:

typedef struct wait_queue wait_queue_t;
struct __wait_queue {
        unsigned int flags;
#define WQ_FLAG_EXCLUSIVE 0x01
        void *private;
        wait_queue_func_t func;
        struct list_head task_list;
};   

等待队列头定义如下:
linux+v2.6.28/include/linux/wait.h:

struct __wait_queue_head {
       spinlock_t lock;
       struct list_head task_list;
};
typedef struct __wait_queue_head     wait_queue_head_t;


初始化等待队列头和等待队列:

初始化等待队列头有两种方式:
void init_waitqueue_head(wait_queue_head_t *q);
DECLARE_WAIT_QUEUE_HEAD(name);
linux+v2.6.28/include/linux/wait.h:

#define __WAIT_QUEUE_HEAD_INITIALIZER(name) { \
       .lock = __SPIN_LOCK_UNLOCKED(name.lock), \
       .task_list = { &(name).task_list, &(name).task_list } }
   
#define DECLARE_WAIT_QUEUE_HEAD(name) \

wait_queue_head_t name =  WAIT_QUEUE_HEAD_INITIALIZER(name)


linux+v2.6.28/kernel/wait.c:

void init_waitqueue_head(wait_queue_head_t *q)
{
    spin_lock_init(&q->lock);
    INIT_LIST_HEAD(&q->task_list);
}


对于等待队列,使用一个宏去定义和初始化:

DECLARE_WAITQUEUE(name, tsk)

#define __WAITQUEUE_INITIALIZER(name, tsk) { \
    .private = tsk, \
    .func = default_wake_function, \
    .task_list = { NULL, NULL } }
   
#define DECLARE_WAITQUEUE(name, tsk) \
     wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)


在使用等待队列前,需要定义和初始化等待队列头和等待队列。

定义和初始化完成后,需要从等待队列头添加或者移出等待队列:
linux+v2.6.28/kernel/wait.c:

/* 添加等待队列*/

void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
    unsigned long flags;
   
    wait->flags &= ~WQ_FLAG_EXCLUSIVE;
    spin_lock_irqsave(&q->lock, flags);
    __add_wait_queue(q, wait);
    spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(add_wait_queue);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值