信号量的结构体定义如下:
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);
|