自旋锁与信号量(转载)

来源:互联网 发布:java获取12时间戳 编辑:程序博客网 时间:2024/05/17 13:40
原文地址:自旋锁与信号量(转载)作者:joee33
在驱动程序中,当多个线程同时访问相同的资源时
(驱动程序中的全局变量是一种典型的共享资源),
可能会引发"竞态",因此我们必须对共享资源进行并发控制。
Linux内核中解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用)。
  自旋锁与信号量"类似而不类",类似说的是它们功能上的相似性,"不类"指代它们在本质和实现机理上完全不一样,不属于一类。
  自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环查看是否该自旋锁的保持者已经释放了锁,"自旋"就是"在原地打转"。
而信号量则引起调用者睡眠,它把进程从运行队列上拖出去,除非获得锁。这就是它们的"不类"。
  但是,无论是信号量,还是自旋锁,在任何时刻,最多只能有一个保持者,
即在任何时刻最多只能有一个执行单元获得锁。这就是它们的"类似"。
  鉴于自旋锁与信号量的上述特点,一般而言,自旋锁适合于保持时间非常短的情况,
它可以在任何上下文使用;信号量适合于保持时间较长的情况,只能在进程上下文使用。
如果被保护的共享资源只在进程上下文访问,则可以以信号量来保护该共享资源,
如果对共享资源的访问时间非常短,自旋锁也是好的选择。
但是,如果被保护的共享资源需要在中断上下文访问
(包括底半部即中断处理句柄和顶半部即软中断),就必须使用自旋锁。
  与信号量相关的API主要有:
  定义信号量
  struct semaphore sem;
  初始化信号量
  void sema_init (struct semaphore *sem, int val);
  该函数初始化信号量,并设置信号量sem的值为val
  void init_MUTEX (struct semaphore *sem);
  该函数用于初始化一个互斥锁,即它把信号量sem的值设置为1,等同于
  sema_init (struct semaphore *sem, 1);
  void init_MUTEX_LOCKED (struct semaphore *sem);
  该函数也用于初始化一个互斥锁,但它把信号量sem的值设置为0,等同于
  sema_init (struct semaphore *sem, 0);
  获得信号量
  void down(struct semaphore * sem);
  该函数用于获得信号量sem,它会导致睡眠,因此不能在中断上下文使用;
  int down_interruptible(struct semaphore * sem);
  该函数功能与down类似,不同之处为,down不能被信号打断,但
  down_interruptible能被信号打断;可被中断的睡眠,当信号来到,睡眠的任务被唤醒
  int down_trylock(struct semaphore * sem);
  该函数尝试获得信号量sem,如果能够立刻获得,它就获得该信号量并返回0,否则,返回非0值。它不会导致调用者睡眠,可以在中断上下文使用。
  释放信号量
  void up(struct semaphore * sem);
  该函数释放信号量sem,唤醒等待者。
  与自旋锁相关的API主要有:
  定义自旋锁
  spinlock_tspin;
  初始化自旋锁
  spin_lock_init(lock)
  该宏用于动态初始化自旋锁lock
  获得自旋锁
  spin_lock(lock)
  该宏用于获得自旋锁lock,如果能够立即获得锁,它就马上返回,否则,它将自旋在那里,直到该自旋锁的保持者释放;
  spin_trylock(lock)
  该宏尝试获得自旋锁lock,如果能立即获得锁,它获得锁并返回真,否则立即返回假,实际上不再"在原地打转";
  释放自旋锁
  spin_unlock(lock)
  该宏释放自旋锁lock,它与spin_trylock或spin_lock配对使用;
  除此之外,还有一组自旋锁使用于中断情况下的API。
  下面进入对并发控制的实战。
首先,在globalvar的驱动程序中,
我们可以通过信号量来控制对int global_var的并发访问,
下面给出源代码:
    #include
    #include
    #include
    #include
    #include
    MODULE_LICENSE("GPL");
    #define MAJOR_NUM 254
    static ssize_t globalvar_read(struct file *, char *, size_t,loff_t*);
    static ssize_t globalvar_write(struct file *, const char *,size_t, loff_t*);
    struct file_operations globalvar_fops =
    {
      read: globalvar_read,
      write: globalvar_write,
    };
    static int global_var = 0;
    static struct semaphore sem;
    static int __init globalvar_init(void)
    {
     int ret;
     ret = register_chrdev(MAJOR_NUM, "globalvar",&globalvar_fops);
     if (ret)
     {
      printk("globalvar register failure");
     }
     else
     {
      printk("globalvar register success");
      init_MUTEX(&sem);
     }
     return ret;
    }

    static void __exitglobalvar_exit(void)
    {
     int ret;
     ret = unregister_chrdev(MAJOR_NUM, "globalvar");
     if (ret)
     {
      printk("globalvar unregister failure");
     }
     else
     {
      printk("globalvar unregister success");
     }
    }

    static ssize_t globalvar_read(struct file*filp, char *buf, size_t len, loff_t *off)
    {
     //获得信号量
     if (down_interruptible(&sem))
     {
      return - ERESTARTSYS;
     }
     //将global_var从内核空间复制到用户空间
     if (copy_to_user(buf, &global_var,sizeof(int)))
     {
      up(&sem);
      return - EFAULT;
     }
     //释放信号量
     up(&sem);
     return sizeof(int);
    }
    ssize_t globalvar_write(struct file *filp, const char *buf,size_t len, loff_t *off)
    {
     //获得信号量
     if(down_interruptible(&sem))//如果返回0,表示获得信号量正常返回
     {
      return - ERESTARTSYS;
     }
     //将用户空间的数据复制到内核空间的global_var
     if (copy_from_user(&global_var, buf,sizeof(int)))
     {
      up(&sem);
      return - EFAULT;
     }
     //释放信号量
     up(&sem);
     return sizeof(int);
    }
    module_init(globalvar_init);
    module_exit(globalvar_exit);

  接下来,我们给globalvar的驱动程序增加open()和release()函数,并在其中借助自旋锁来保护对全局变量intglobalvar_count(记录打开设备的进程数)的访问来实现设备只能被一个进程打开(必须确保globalvar_count最多只能为1):

    #include
    #include
    #include
    #include
    #include

    MODULE_LICENSE("GPL");

    #define MAJOR_NUM 254

    static ssize_t globalvar_read(struct file *,char *, size_t, loff_t*);
    static ssize_t globalvar_write(struct file *, const char *,size_t, loff_t*);
    static int globalvar_open(struct inode *inode, struct file*filp);
    static int globalvar_release(struct inode *inode, struct file*filp);

    struct file_operations globalvar_fops=
    {
     read : globalvar_read,
     write : globalvar_write,
     open : globalvar_open,
     release: globalvar_release,
    };

    static int global_var = 0;
    static int globalvar_count = 0;
    static struct semaphore sem;
    static spinlock_tspin = SPIN_LOCK_UNLOCKED;

    static int __init globalvar_init(void)
    {
     int ret;
     ret = register_chrdev(MAJOR_NUM, "globalvar",&globalvar_fops);
     if (ret)
     {
      printk("globalvar register failure");
     }
     else
     {
      printk("globalvar register success");
      init_MUTEX(&sem);
     }
     return ret;
    }

    static void __exitglobalvar_exit(void)
    {
     int ret;
     ret = unregister_chrdev(MAJOR_NUM, "globalvar");
     if (ret)
     {
      printk("globalvar unregister failure");
     }
     else
     {
      printk("globalvar unregister success");
     }
    }

    static int globalvar_open(struct inode*inode, struct file *filp)
    {
     //获得自选锁
     spin_lock(&spin);

     //临界资源访问
     if (globalvar_count)
     {
      spin_unlock(&spin);
      return - EBUSY;
     }
     globalvar_count++;

     //释放自选锁
     spin_unlock(&spin);
     return 0;
    }

    static int globalvar_release(struct inode*inode, struct file *filp)
    {
     globalvar_count--;
     return 0;
    }

    static ssize_t globalvar_read(struct file*filp, char *buf, size_t len, loff_t *off)
    {
     if (down_interruptible(&sem))
     {
      return - ERESTARTSYS;
     }
     if (copy_to_user(buf, &global_var,sizeof(int)))
     {
      up(&sem);
      return - EFAULT;
     }
     up(&sem);
     return sizeof(int);
    }

    static ssize_t globalvar_write(struct file*filp, const char *buf, size_t len,loff_t *off)
    {
     if (down_interruptible(&sem))
     {
      return - ERESTARTSYS;
     }
     if (copy_from_user(&global_var, buf,sizeof(int)))
     {
      up(&sem);
      return - EFAULT;
      }
      up(&sem);
      return sizeof(int);
    }

    module_init(globalvar_init);
    module_exit(globalvar_exit);

  为了上述驱动程序的效果,我们启动两个进程分别打开/dev/globalvar。在两个终端中调用./globalvartest.o测试程序,当一个进程打开/dev/globalvar后,另外一个进程将打开失败,输出"deviceopen failure"。

0 0