信号量
信号量允许多个进程同时进入临界区,大多数情况下只允许一个进程进入临界区,把信号量的计数值设置为 1,即二值信号量,这种信号量称为互斥信号量。可允许多个锁持有者。
 和自旋锁相比,信号量适合保护比较长的临界区,因为竞争信号量时进程可能睡眠和再次唤醒,代价很高。中断服务函数不能进行睡眠,因此信号量不能用于中断当中
 信号量的使用流程:
 定义一个信号量
     ↓
 初始化信号量
     ↓
 获得信号量(减操作)
     ↓
 释放信号量(加操作)
内核使用的信号量定义如下:
 include/linux/semaphore.h
 struct semaphore {
     raw_spinlock_t      lock;
     unsigned int        count;
     struct list_head    wait_list;
 };
 成员 lock 是自旋锁,用来保护信号量的其他成员。
 成员 count 是计数值,表示还可以允许多少次进入临界区。
 成员 wait_list 是等待进入临界区的进程链表。
 struct semaphore_waiter {
     struct task_struct *task;
     bool up;
     struct list_head list;
 };
初始化静态信号量的方法如下。
 (1)    __SEMAPHORE_INITIALIZER(name, n):指定名称和计数值,允许同时n 次进入临界区。
 (2)    DEFINE_SEMAPHORE(name):初始化一个互斥信号量。
 在运行时动态初始化信号量的方法如下:
 static inline void sema_init(struct semaphore *sem, int val);
 参数 val 指定允许同时进入临界区的数量。
 获取信号量的函数如下。
 (1) void down(struct semaphore *sem);
 获取信号量,如果计数值是 0,进程深度睡眠。
 (2) int down_interruptible(struct semaphore *sem);
 获取信号量,如果计数值是 0,进程轻度睡眠(可以被系统消息打断,该函数的调用允许中断)。如果返回0,表示获得信号量正常返回,如果被信号打断,返回-EINTR。
 (3) int down_killable(struct semaphore *sem);
 获取信号量,如果计数值是 0,进程中度睡眠(可以因为受到致命信号而被唤醒)。
 (4) int down_trylock(struct semaphore *sem);
 获取信号量,如果计数值是 0,进程不等待不会导致调用者睡眠。
 (5) int down_timeout(struct semaphore *sem, long jiffies);
 获取信号量,指定等待的时间。
 释放信号量的函数如下:
 void up(struct semaphore *sem);
使用示例
 
#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/kthread.h> 
 #include <linux/semaphore.h> 
 #include <linux/delay.h>
int num[2][5]= { {
         0,2,4,6,8
     }
     , {
         1,3,5,7,9
     }
 }
 ;
 struct semaphore sem_first;
 struct semaphore sem_second;
 struct task_struct * task1;
 struct task_struct * task2;
int thread_print_first(void *p) {
     int i;
     int *num=(int *)p;
     if(kthread_should_stop()){
         return 0;
     }
     printk(KERN_ALERT"Hello World:first\n");
     for (i=0;i<5;i++) {
         down(&sem_first);
         printk(KERN_ALERT"Hello World:%d\n",num[i]);
         up(&sem_second);
     }
     do {
         msleep(1000);
     }
     while(!kthread_should_stop());
     return 0;
 }
int thread_print_second(void *p) {
     int i;
     int *num=(int *)p;
     if(kthread_should_stop()){
         return 0;
     }
     printk(KERN_ALERT"Hello World:second\n");
     for (i=0;i<5;i++) {
         down(&sem_second);
         printk(KERN_ALERT"Hello World:%d\n",num[i]);
         up(&sem_first);
     }
     do {
         msleep(1000);
     }
     while(!kthread_should_stop());
     return 0;
 }
static int hello_init(void) {
     printk(KERN_ALERT"Hello World enter\n");
     sema_init(&sem_first,1);
     sema_init(&sem_second,0);
     task1 = kthread_create(thread_print_first,num[0],"first");
     if(IS_ERR(task1)) {
         printk(KERN_ALERT"kthread_create error!\n");
         return -1;
     }
     task2 = kthread_create(thread_print_second,num[1],"second");
     if(IS_ERR(task2)) {
         printk(KERN_ALERT"kthread_create error!\n");
         kthread_stop(task1);
         return -1;
     }
     wake_up_process(task1);
     wake_up_process(task2);
     return 0;
 }
static void hello_exit(void) {
     int ret;
     if (!IS_ERR(task1)) {
         ret = kthread_stop(task1);
         printk("<<<<<<<<task1 exit, ret = %d\n", ret);
     }
     if (!IS_ERR(task2)) {
         ret = kthread_stop(task2);
         printk("<<<<<<<<task2 exit, ret = %d\n", ret);
     }
     printk(KERN_ALERT"hello world exit\n");
 }
 module_init(hello_init);
 module_exit(hello_exit);
 MODULE_LICENSE("GPL");
  



















