0
点赞
收藏
分享

微信扫一扫

Kernel module编程(十三):信号量、互斥锁、读写信号量和完成量


  本文也即《Linux Device Drivers》,LDD3的第五章Concurrency and Race Conditions的读书笔记之二,但我们不限于此内容。

信号量(Semaphore)

信号量和互斥锁

  

  

if(!dptr->data[s_pos]){ 
         dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL); 
         if(dptr->data[s_pos] == NULL) 
                goto out; 
         … … 
 }

   信号量的使用例子见kernel module编程(五):设备读写 ,相关操作已经重点标出,这里不再重复。使用方式如下:

  1. 头文件加载:#include <asm/semaphore.h>, 相关的数据结构为struct semaphore
  2. 创建信号量:void sema_init(struct semaphore * sem , int val ); 当val设置为1时就成为互斥锁。对于互斥锁,也可以采用如下的方式:
  1. 声明和初始化一个互斥锁:DECLARE_MUTEX( name ) 或者DECLARE_MUTEX_LOCKED( name ), 其中name是一个struct semaphore的变量。 在后一种方式,起始状态就是locked,其他线程/进程需要等待其unlocked才能进入。
  2. 如果一个互斥锁需要实时初始化,即动态产生,使用:void int_MUTEX(struct semaphore * sem ) ;或者void init_MUTEX_LOCKED(struct semaphore * sem );
  1. 对于信号量获取,即P函数,通过down来调用,对信号量进行减一操作,如果信号量不满足要求,则将调用者置为sleep状态直至资源获取。方式如下:
  1. void down (struct semaphore * sem); 对信号量减一,并一直等到资源获取。
  2. int down_interruptible (struct semaphore *sem); 同上,但是采用中断的方式(interruptible),这是最为常用的使用方式,它允许用户中断一个正在等待信号量获取的用户空间的进程。如果采用非 中断方式,则这个这个期间,进程是无法killed的。我们需要注意的是,采用这种方式,如果在等待期间用户终止了进程,则将返回一个非零的值,所以必须 判断返回值,并作出相应处理。一般返回为-ERESTARTSYS ,在返回之前,应当确保undo之前用户可察觉的所有操作,如果不能确保,返回-EINTR .
  3. int down_trylock(struct semaphore * sem ); 这种方式不会sleep,如果不能成功会的信号量,将马上返回非零值。
  1. 当一个线程成功调用down,即获得的semahore,将进入这段敏感代码区,执行完后,必须释放信号量。通过up来调用V操作。void up(struct semaphre * sem) 。我们要非常注意,如果在处理过程中出现异常或者错误而需要return,必须要保证信号量的释放,即无论是正常的还是异常地离开这段敏感代码区,都必须释放信号量 。否则任何线程/进程将无法获取信号量。

  

读写信号量

   在scull 的例子中,读和写都通过信号量进行保护,防止一起写,也防止在写和读同时操作,这些都是我们应当避免的,当时它同时也不允许两个读的操作一通进行,而这种 情况是不会产生危害的。Linux kernel提供了一个rwsem的特别的信号量用于处理这种情况,允许多个读同时存在以提高程序处理能力。读写信号量在驱动中一般较少使用,但是有时会 非常有效。使用方式如下:

  • 头文件加载:#include <linux/rwsem.h> ,相应的的数据结构为struct rw_semaphore
  • 初始化操作:void init_rw_sem(struct rw_semaphore * sem );
  • 对于只读操作,相关函数如下:
  • void down_read(struct rw_semaphore * sem ); 提供只读获取的保护,可以同时有其他的只读操作。他将置调用者与非中断的sleep,这是需要特别注意的,即如果另外有写的操作,而引起sleep,是非中断,用户不能在此刻中断进程。
  • int down_read_trylock(struct rw_semaphore * sem ); 马上返回,如果可读,返回非零,不可读,返回0。注意这个返回和一般的kernel函数的方式方式不一样。也可信号量的返回方式不一样。
  • void up_read(struct rw_semaphore * sem ); 释放读写信号量。
  • 对于写的保护,相关函数如下:
  • void down_write(struct rw_semaphore * sem ); 类似down_read
  • int down_write_trylock(struct rw_semaphore * sem ); 类似down_read_trylock
  • void up_write(struct rw_semaphore * sem ); 类似up_read
  • void downgrade_write(struct rw_semaphore * sem ); 当一个写保护后,跟着一个耗费时间长的读保护,我们可以在使用能够downgrade_write,它允许其他读操作在你结束写后,马上获得读写型号了。否侧通常的处理是需要等待这个紧跟的漫长的读操作。

  

completion(不知道中文名字,可能是完成量,^_^)

  

  • 头文件#include ,数据结构为struct completion ,初始化为init_completion(struct completion * comp ) ,也可以直接使用DECLARE_COMPLETION( comp );
  • 在A函数中,如果需要等待其他的处理,使用void wait_for_completion(struct completion * comp ); 则在这个位置上将处于非中断的sleep,进行等待,也就是相关的线程/进程,用户是无法kill的。
  • 在B函数,如果已经处理完,可以交由A函数处理,有下面两种方式
  • void complete(struct completion * comp ); 如果要执行A必须等待B先执行,B执行后,A可以继续执行。如果A需要再次执行,则需要确保下一次B执行完。如果连续执行两次B,则可以执行两次A,第三次A要等第三次B执行完。
  • void complete_all(struct completion * comp ); 只要B执行完,A就可以执行,无论执行多少次。如果需要再等待B的直系个可以使用INIT_COMPLETION(struct completion * comp ) 。重新初始化completion即可。
  • void complete_and_exit(struct completion * comp ,long retval ) ; 这个处理具有complete的功能外,还将调用它的线程/进程终止。可用于一些无限循环的场景,例如受到某个cleaned up的信息后,e通知用户程序终止,允许A函数执行。

  

#include <linux/completion.h>
... ...  
 DECLARE_COMPLETION(comp); //为了试验方便,就不分每个scull都持有一个完成量,本来应当如此int scull_read(... ...){ 
     struct scull_qset * dptr; 
     ... ...  
     printk("scull_read waiting for completed from write function./n"); 
     wait_for_completion (&comp); 
     printk("scull_read awake for reading,continue ..../n"); 
     ... .... 
 } int scull_write(... ...){ 
     ... ...  
     complete (&comp); 
// complete_all (&comp); 
 // complete_and_exit (&comp); 
 }  
file = fopen("dev/scull0","r"); 
 ... ...  
 while((len = fread(read_buf,1,512,file)) > 0){ 
     t otal_len +=len; 
     printf("%s",read_buf); 
     memset(read_buf,0,512); 
 }  
int scull_open(....) 
 { 
     ... ... 
     if((filp->f_flags & O_ACCMODE) == O_RDONLY){ 
         printk("waiting for completed from write function./n");  
         wait_for_completion(&comp); 
         printk("awake for reading,continue ..../n"); 
     } 
     ... ... 
 }


举报

相关推荐

0 条评论