| 首页 | 技术文章 | 软件下载 | 博客 | 论坛 | 精品教程 | 黑客动画 | 视频资源 | 在线服务 | 黑客游戏 | 

您现在的位置: 中国X黑客小组 >> 技术文章 >> 操作系统 >> Linux >> 文章正文 用户登录 新用户注册
  深入浅出Linux设备驱动之并发控制          【字体:
深入浅出Linux设备驱动之并发控制
作者:dfgdfgd    文章来源:网络    点击数:    更新时间:2006-11-5    

在驱动程序中,当多个线程同时访问相同的资源时(驱动程序中的全局变量是一种典型的共享资源),可能会引发"竞态",因此我们必须对共享资源进行并发控制。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_t spin;

初始化自旋锁

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 <linux/module.h>

#include <linux/init.h>

#include <linux/fs.h>

#include <asm/uaccess.h>

#include <asm/semaphore.h> 

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 __exit globalvar_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))

 {

  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()函数,并在其中借助自旋锁来保护对全局变量int globalvar_count(记录打开设备的进程数)的访问来实现设备只能被一个进程打开(必须确保globalvar_count最多只能为1):

#include <linux/module.h>

#include <linux/init.h>

#include <linux/fs.h>

#include <asm/uaccess.h>

#include <asm/semaphore.h>



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_t spin = 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 __exit globalvar_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后,另外一个进程将打开失败,输出"device open failure"

文章录入:IceRiver    责任编辑:IceRiver 
  • 上一篇文章:

  • 下一篇文章:
  • 发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
    最新热点 最新推荐 相关文章
    Sniffit常见问题及防范策略深
    深入了解DDOS与DDOS追踪
    谷歌搜寻深入内心 大众隐私权
    由症状到原理 深入了解ARP及
    深入了解 DDOS与 DDOS追踪
    管理工具变入侵利器 深入分
    深入剖析 MY123流氓软件四种
    有关VPN目录服务管理方式的深
    发展迅速的网络防火墙功夫深
    深入防火墙记录
      网友评论:(只显示最新5条。评论内容只代表网友观点,与本站立场无关!)
    Powered by ICE RIVER - STUDIO
    » CnXHacker.CoM   © CopyRight 2002-2006, CnXHacker.CoM™, Inc. All Rights Reserved.