zoukankan      html  css  js  c++  java
  • 六、设备驱动中的并发控制(二)

    6.4 信号量

      信号量(semaphore)是操作系统中最典型的用于同步和互斥的手段,信号量的值可以是 0,1 或者 n。信号量与操作系统的经典概念 PV 操作对应。

    • P(S):
      • 将信号量 S 的值减 1,即 S = S - 1
      • 如果 S >= 0,则该进程继续执行,否则该进程置为等待状态,排入等待队列
    • V(S):
      • 将信号量 S 的值加 1,即 S= S + 1
      • 如果 S > 0,唤醒队列中等待信号量的进程
    1 /* Please don't access any members of this structure directly 
    2  * 定义信号量:struct semaphore sem;
    3  */
    4 struct semaphore {
    5     raw_spinlock_t        lock;
    6     unsigned int        count;
    7     struct list_head    wait_list;
    8 };
     1 /**
     2  * 初始化信号量
     3  *      该函数初始化信号量,并设置信号量 sem 的值为 val
     4  */
     5 static inline void sema_init(struct semaphore *sem, int val)
     6 {
     7     static struct lock_class_key __key;
     8     *sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
     9     lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);
    10 }
    1 /**
    2  *  获得信号量:
    3  *      用于获得信号量 sem,它会导致睡眠,因此不能再中断上下文中使用
    4  *      进入睡眠状态的进程不能被信号打断
    5  */
    6 extern void down(struct semaphore *sem);
    1 /**
    2  *  获得信号量:
    3  *      与 down 函数类似,不同的是进入睡眠状态的进程能被信号打断,
    4  *      信号也会导致该函数返回,这时候函数的返回值非 0
    5  *  在使用此函数的时候,对返回值一般会进行检查,如果非 0,通常立即返回 -ERESTARTSYS
    6  *  if(down_interruptible(&sem))
    7  *      return -ERESTARTSYS;
    8  */
    9 extern int __must_check down_interruptible(struct semaphore *sem);
    1 /**
    2  *  获得信号量:
    3  *      尝试获得信号量,如果能够立刻获得,它就获得信号量并返回 0,否则返回非 0 值
    4  *      它不会导致调用者睡眠,可以再中断上下文中使用
    5  */
    6 extern int __must_check down_trylock(struct semaphore *sem);
    1 /**
    2  *  释放信号量,唤醒等待者
    3  */
    4 extern void up(struct semaphore *sem);

      信号量作为一种可能互斥手段,可以保护临界区,使用方式与自旋锁类似。与自旋锁相同的是,只有得到信号量的值才能执行临界区的代码。

      与自旋锁不同的是,当获取不到信号量时,进程不会原地打转而是进入休眠等待状态。

      用作互斥时,信号量一般这样使用:

      

      

       因为内核更倾向于直接使用 mutex 作为互斥手段,所以不推荐信号量如此使用。

      信号量更适合用于同步,比如具体数值的生产者消费者问题。

      

       一个进程 A 执行 down() 等待信号量,另一个进程 B 执行 up() 释放信号量,这样进程 A 就同步等待了进程 B。

    6.5 互斥体

     1 /**
     2  *  定义互斥体:struct mutex my_mutex;
     3  */
     4 struct mutex {
     5     /* 1: unlocked, 0: locked, negative: locked, possible waiters */
     6     atomic_t        count;
     7     spinlock_t        wait_lock;
     8     struct list_head    wait_list;
     9 #if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_MUTEX_SPIN_ON_OWNER)
    10     struct task_struct    *owner;
    11 #endif
    12 #ifdef CONFIG_MUTEX_SPIN_ON_OWNER
    13     struct optimistic_spin_queue osq; /* Spinner MCS lock */
    14 #endif
    15 #ifdef CONFIG_DEBUG_MUTEXES
    16     void            *magic;
    17 #endif
    18 #ifdef CONFIG_DEBUG_LOCK_ALLOC
    19     struct lockdep_map    dep_map;
    20 #endif
    21 };
     1 /**
     2  * mutex_init - initialize the mutex
     3  *              初始化互斥体:mutex_init(&my_mutex);
     4  * @mutex: the mutex to be initialized
     5  *
     6  * Initialize the mutex to unlocked state.
     7  *
     8  * It is not allowed to initialize an already locked mutex.
     9  */
    10 # define mutex_init(mutex) 
    11 do {                            
    12     static struct lock_class_key __key;        
    13                             
    14     __mutex_init((mutex), #mutex, &__key);        
    15 } while (0)
     1 /**
     2  *  获取互斥体
     3  *      引起的睡眠不能被信号打断。
     4  */
     5 extern void mutex_lock(struct mutex *lock);
     6 /**
     7  *  获取互斥体
     8  *      引起的睡眠可被信号打断。
     9  */
    10 extern int __must_check mutex_lock_interruptible(struct mutex *lock);
    1 /*
    2  *  获取互斥体
    3  *      尝试获得 mutex,获取不到 mutex 时不会引起进程睡眠
    4  * NOTE: mutex_trylock() follows the spin_trylock() convention,
    5  *       not the down_trylock() convention!
    6  *
    7  * Returns 1 if the mutex has been acquired successfully, and 0 on contention.
    8  */
    9 extern int mutex_trylock(struct mutex *lock);
    1 /** 释放互斥体 */
    2 extern void mutex_unlock(struct mutex *lock);

      用法:

     1 /**
     2  *  如何用互斥体:
     3  *      定义互斥体
     4  *          struct mutex my_mutex;
     5  *      初始化互斥体
     6  *          mutex_init(&my_mutex);
     7  *      获取互斥体
     8  *          mutex_lock(&my_mutex);
     9  *      临界资源
    10  *          ...
    11  *      释放 mutex
    12  *          mutex_unlock(&my_mutex);
    13  */
    • 自旋锁和互斥体的区别:
      • 属于不同层次互斥手段,互斥体的实现依赖于自旋锁,自旋锁属于更底层的手段
      • 互斥体是进程级的,用于多个进程之间对资源的互斥,虽也在内核中,但内核执行路径是以进程的身份,代表进程来抢夺资源。若竞争失败,会发生进程上下文切换,当前进程进入睡眠状态,CPU 将运行其他进程。由于进程上下文切换开销大,因此只有当进程占用资源时间较长时,用互斥体才是最好的选择。临界区访问时间较短则用自旋锁
      • 互斥体所保护的临界区可包含可能引起阻塞的代码,而自旋锁要绝对避免此情况。
      • 互斥体用于进程上下文,对于中断和软中断只能使用自旋锁。

    6.6 完成量

      完成量(completion)用于一个执行单元等待另一个执行单元执行完某事。

    1 /**
    2  *  定义完成量:struct completion my_completion;
    3  */
    4 struct completion {
    5     unsigned int done;
    6     wait_queue_head_t wait;
    7 };
     1 /**
     2  * init_completion - Initialize a dynamically allocated completion
     3  *                   初始化完成量的值为 0(即没有完成的状态)
     4  * @x:  pointer to completion structure that is to be initialized
     5  *
     6  * This inline function will initialize a dynamically created completion
     7  * structure.
     8  */
     9 static inline void init_completion(struct completion *x)
    10 {
    11     x->done = 0;
    12     init_waitqueue_head(&x->wait);
    13 }
     1 /**
     2  * reinit_completion - reinitialize a completion structure
     3  *                   重新初始化完成量的值为 0(即没有完成的状态)
     4  * @x:  pointer to completion structure that is to be reinitialized
     5  *
     6  * This inline function should be used to reinitialize a completion structure so it can
     7  * be reused. This is especially important after complete_all() is used.
     8  */
     9 static inline void reinit_completion(struct completion *x)
    10 {
    11     x->done = 0;
    12 }
    1 /** 等待一个完成量被唤醒 */
    2 extern void wait_for_completion(struct completion *);
    1 /** 唤醒完成量 */
    2 extern void complete(struct completion *);  ///< 只唤醒一个等待的执行单元
    3 extern void complete_all(struct completion *);///< 释放所有等待同一完成量的执行单元

      

     6.7 globalmem 增加并发

      在 globalmem 的读写函数中,要调用 copy_from_user 等可能引起阻塞的函数,所以不能使用自旋锁,宜使用互斥体。

      1 #include <linux/module.h>
      2 #include <linux/fs.h>
      3 #include <linux/init.h>
      4 #include <linux/cdev.h>
      5 #include <linux/slab.h>
      6 #include <linux/uaccess.h>
      7 #include <linux/mutex.h>
      8 
      9 #define GLOBALMEM_SIZE      0x1000
     10 //#define MEM_CLEAR           0X1
     11 #define GLOBALMEM_MAGIC     'g'
     12 #define MEM_CLEAR           _IO(GLOBALMEM_MAGIC, 0)
     13 #define GLOBALMEM_MAJOR     230
     14 #define DEVICE_NUMBER       10
     15 
     16 static int globalmem_major = GLOBALMEM_MAJOR;
     17 module_param(globalmem_major, int, S_IRUGO);
     18 
     19 struct globalmem_dev {
     20     struct cdev cdev;
     21     unsigned char mem[GLOBALMEM_SIZE];
     22     struct mutex mutex;
     23 };
     24 
     25 struct globalmem_dev *globalmem_devp;
     26 
     27 /** 
     28  * 这里涉及到私有数据的定义,大多数遵循将文件私有数据 pirvate_data 指向设备结构体,
     29  * 再用 read write llseek ioctl 等函数通过 private_data 访问设备结构体。
     30  * 对于此驱动而言,私有数据的设置是在 open 函数中完成的
     31  */
     32 static int globalmem_open(struct inode *inode, struct file *filp)
     33 {
     34     /**
     35      *  NOTA: 
     36      *      container_of 的作用是通过结构体成员的指针找到对应结构体的指针。
     37      *      第一个参数是结构体成员的指针
     38      *      第二个参数是整个结构体的类型
     39      *      第三个参数为传入的第一个参数(即结构体成员)的类型
     40      *      container_of 返回值为整个结构体指针
     41      */ 
     42     struct globalmem_dev *dev = container_of(inode->i_cdev, struct globalmem_dev, cdev);
     43     filp->private_data = dev;
     44     return 0;
     45 }
     46 
     47 static int globalmem_release(struct inode *inode, struct file *filp)
     48 {
     49     return 0;
     50 }
     51 
     52 static long globalmem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
     53 {
     54     struct globalmem_dev *dev = filp->private_data;
     55 
     56     switch(cmd){
     57     case MEM_CLEAR:
     58         mutex_lock(&dev->mutex);
     59         memset(dev->mem, 0, GLOBALMEM_SIZE);
     60         printk(KERN_INFO "globalmem is set to zero
    ");
     61         mutex_unlock(&dev->mutex);
     62         break;
     63     default:
     64         return -EINVAL;
     65     }
     66 
     67     return 0;
     68 }
     69 
     70 static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
     71 {
     72     loff_t ret = 0;
     73     switch(orig) {
     74     case 0: /** 从文件开头位置 seek */
     75         if(offset < 0){
     76             ret = -EINVAL;
     77             break;
     78         }
     79         if((unsigned int)offset > GLOBALMEM_SIZE){
     80             ret = -EINVAL;
     81             break;
     82         }
     83         filp->f_pos = (unsigned int)offset;
     84         ret = filp->f_pos;
     85         break;
     86     case 1: /** 从文件当前位置开始 seek */
     87         if((filp->f_pos + offset) > GLOBALMEM_SIZE){
     88             ret = -EINVAL;
     89             break;
     90         }
     91         if((filp->f_pos + offset) < 0){
     92             ret = -EINVAL;
     93             break;
     94         }
     95         filp->f_pos += offset;
     96         ret = filp->f_pos;
     97         break;
     98     default:
     99         ret = -EINVAL;
    100         break;
    101     }
    102 
    103     return ret;
    104 }
    105 
    106 static ssize_t globalmem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
    107 {
    108     unsigned long p = *ppos;
    109     unsigned int count = size;
    110     int ret = 0;
    111     struct globalmem_dev *dev = filp->private_data;
    112 
    113     if(p >= GLOBALMEM_SIZE)
    114         return 0;
    115     if(count > GLOBALMEM_SIZE - p)
    116         count = GLOBALMEM_SIZE - p;
    117 
    118     mutex_lock(&dev->mutex);
    119     if(copy_from_user(dev->mem + p, buf, count))
    120         ret = -EFAULT;
    121     else {
    122 
    123         *ppos += count;
    124         ret = count;
    125         printk(KERN_INFO "written %u bytes(s) from %lu
    ", count, p);
    126     }
    127     mutex_unlock(&dev->mutex);
    128     return ret;
    129 }
    130 
    131 /**
    132  * *ppos 是要读的位置相对于文件开头的偏移,如果该偏移大于或等于 GLOBALMEM_SIZE,意味着已经独到文件末尾
    133  */
    134 static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
    135 {
    136     unsigned long p = *ppos;
    137     unsigned int count = size;
    138     int ret = 0;
    139     struct globalmem_dev *dev = filp->private_data;
    140 
    141     if(p >= GLOBALMEM_SIZE)
    142         return 0;
    143     if(count > GLOBALMEM_SIZE - p)
    144         count = GLOBALMEM_SIZE - p;
    145 
    146     mutex_lock(&dev->mutex);
    147     if(copy_to_user(buf, dev->mem + p, count)) {
    148         ret = -EFAULT;
    149     } else {
    150         *ppos += count;
    151         ret = count;
    152         printk(KERN_INFO "read %u bytes(s) from %lu
    ", count, p);
    153     }
    154 
    155     mutex_unlock(&dev->mutex);
    156     return ret;
    157 }
    158 
    159 static const struct file_operations globalmem_fops = {
    160     .owner = THIS_MODULE,
    161     .llseek = globalmem_llseek,
    162     .read = globalmem_read,
    163     .write = globalmem_write,
    164     .unlocked_ioctl = globalmem_ioctl,
    165     .open = globalmem_open,
    166     .release = globalmem_release,
    167 };
    168 
    169 
    170 /**
    171  * @brief  globalmem_setup_cdev     
    172  *
    173  * @param  dev
    174  * @param  index    次设备号
    175  */
    176 static void globalmem_setup_cdev(struct globalmem_dev *dev, int index)
    177 {
    178     int err;
    179     int devno = MKDEV(globalmem_major, index);
    180 
    181     /** 使用 cdev_init 即是静态初始化了 cdev */
    182     cdev_init(&dev->cdev, &globalmem_fops);
    183     dev->cdev.owner = THIS_MODULE;
    184 
    185     /** 设备编号范围设置为1,表示我们只申请了一个设备 */
    186     err = cdev_add(&dev->cdev, devno, 1);
    187     if(err)
    188         printk(KERN_NOTICE "Error %d adding globalmem%d
    ", err, index);
    189 }
    190 
    191 static int __init globalmem_init(void)
    192 {
    193     int ret;
    194     int i;
    195     dev_t devno = MKDEV(globalmem_major, 0);
    196 
    197     if(globalmem_major)
    198         ret = register_chrdev_region(devno, DEVICE_NUMBER, "globalmem");
    199     else {
    200         ret = alloc_chrdev_region(&devno, 0, DEVICE_NUMBER, "gobalmem");
    201         globalmem_major = MAJOR(devno);
    202     }
    203 
    204     if(ret < 0)
    205         return ret;
    206 
    207     globalmem_devp = kzalloc(sizeof(struct globalmem_dev), GFP_KERNEL);
    208     if(!globalmem_devp){
    209         ret = -ENOMEM;
    210         goto fail_malloc;
    211     }
    212 
    213     mutex_init(&globalmem_devp->mutex);
    214     for(i = 0; i < DEVICE_NUMBER; i++){
    215         globalmem_setup_cdev(globalmem_devp + i, i);
    216     }
    217 
    218 fail_malloc:
    219     unregister_chrdev_region(devno, 1);
    220     return ret;
    221 }
    222 
    223 static void __exit globalmem_exit(void)
    224 {
    225     int i;
    226     for(i = 0; i < DEVICE_NUMBER; i++) {
    227         cdev_del(&(globalmem_devp + i)->cdev);
    228     }
    229     kfree(globalmem_devp);
    230     unregister_chrdev_region(MKDEV(globalmem_major, 0), 1);
    231 }
    232 
    233 module_init(globalmem_init);
    234 module_exit(globalmem_exit);
  • 相关阅读:
    [小知识]如何查看IIS6应用程序池所对应的进程ID
    继续向大家汇报服务器情况
    CNBlogs DotText 1.0 Beta 2 重要更新
    垃圾广告记录
    Firefox 11正式发布
    Firefox 10正式发布
    Firefox 6 正式发布
    Firefox 5 正式发布
    Firefox 9正式发布
    Firefox 8正式发布
  • 原文地址:https://www.cnblogs.com/kele-dad/p/11690817.html
Copyright © 2011-2022 走看看