linux信号量驱动程序,LINUX设备驱动中的信号量机制

继续敲《LINUX设备驱动开发详解》中的信号量机制的中心内容,版权所有,我是抄他的~~~

信号量是用于保护临界区的一种常用方法,他的使用方法和自旋锁类似,只要获得了信号量的进程才能执行临界区代码,不同的是,当无法获得信号量时,进程不会原地打转,而是进入休眠等待状态。

1定义信号量

struct semaphore sem;

2初始化信号量

void sema_init(struct semaphore *sem,int val);

该函数初始化信号量,并设置信号量sem的值为val。尽管信号量可以被初始化为大于1的值从而成为一个计数信号量,但是它通常不被这样使用。

void init_MUTEX(struct semaphore *sem);

该函数用于初始化一个用于互斥的信号量,他把信号量sem的值设置为1,等于sema_init(struct semaphore *sem,1)

void init_MUTEX_LOCKED(struct semphore *sem);

该函数也用于初始化一个信号量,但它把信号量sem的值设置为0,等同于sema_init(struct semaphore *sem,0)

此外,还定义了两个宏,定义并初始化信号量的快捷方式:

DECLARE_MUTEX(name);

DECLARE_MUTEX_LOCKED(name);

前者定义一个名为name的信号量,并初始化为1,后者定义一个name的信号量,并初始化为0.

3获得信号量

void down(struct semaphore *sem)

该函数用于获得信号量sem,它会导致睡眠,因此不能在中断上下文使用。

int down_interruptible(struct semaphore *sem)

该函数功能与down类似,不同之处是,因为down()而进入睡眠状态的进程不能被信号打断,而因为down_interruptible()而进入睡眠状态的进程能被信号所打断,信号也会导致该函数返回,这时返回值为非0.

在使用down_interruptible()获得信号量时,对返回值一般会进行检查,如果,非0,通常立刻返回-ERESTARTSYS;

if(down_interruptible(&sem))

{

return -ERESTARTSYS;

}

int down_trylock(struct semaphore *sem)

该函数尝试获得信号量sem,如果能立刻获得,他就获得改信号量,并返回0,否则,返回非0值,它不会导致调用者睡眠,可以在中断上下文使用。

信号量的使用方法如下:

//定义信号量

DECLARE_MUTEX(sem);

down(&sem);  //获得信号量,保护临界区

...

...//临界区

...

up(&sem);    //释放信号量

使用信号量实现设备只能被一个进程打开

static DECLARE_MUTEX(xxx_lock);   //定义互斥锁

static int xxx_open(struct *inode,struct file *filp)

{

...   //试图给进程获得信号量,如果进程获得了信号量,则继续进行别的操作,

if(down_trylock(&xxx_lock))  //获得打开锁

return -EBUSY;  //但如果该共享资源已经被另外互斥锁锁定,则直接返回设备繁忙

...

return 0;

}

static int xxx_release(struct *inode,struct file *filp)

{                   //打开锁

up(&xxx_lock)  //释放打开锁

return 0;

}

信号量的同步

如果信号量被初始化为0,则它可以用于同步,同步意味着一个执行单元的继续执行等待另一个执行单元完成某事,保证执行的先后顺序,

执行单元A                         执行单元B

DECLARE_MUTEX(sem);                                            //该例子中,执行单元A执行代码区域b之前,必须等待执行单元B执行完代码单元c,

代码区域a

down(&sem);                     代码区域c                       //即执行完 代码区域a时,要先判断 代码区域c是否执行完,完了才能执行区域b

代码区域b                up(&sem);

完成量用于同步

linux系统中提供了一种比信号量更好的同步机制,即完成量(completion),它用于一个执行单元等待另一个执行单元完成某事。

1定义完成量

struct completion my_completion;

2初始化completion

init_completion(&my_completion);

定义并初始化的快捷方式

DECLARE_COMPLETION(my_completion);

3等待完成量

下面函数用于等待一个completion被唤醒

void wait_for_completion(struct completion *c);

4唤醒完成量

void complete(struct completion *c);

void complete_all(struct completion *c);

完成量的功能实例,

执行单元A                                       执行单元B

DECLARE_COMPLETION(my_completion)                                   //在执行单元A执行完代码区域a时,wait_for_completion()会等待执行单元B的

代码区域a                                       代码区域c              //completion()函数,此函数执行完了才能执行代码区域b

wait_for_completion(&my_completion)           completion(&com)

代码区域b

自旋锁 和 信号量 的区别

自旋锁和信号量都是解决互斥问题的基本手段,面对特定的情况,应该进行不同的选择。

严格意义上说,信号量和自旋锁属于不同层次的互斥手段,前者的实现依赖于后者,在信号量本身的实现上,为了保证信号量结构存取的原子性,在多CPU中需要自旋锁来互斥。

信号量是进程级的,用于多个进程之间对资源的互斥,虽然也是在内核中,但是该内核执行路径是以进程的身份,代表进程来争夺资源的,。如果竞争失败,会发生进程上下文切换,当前进程进入睡眠状态,,CPU将运行其他进程,鉴于进程上下文切换的开销很大,因此,只有当进程占用资源时间较长时,用信号量才是较好的选择。

当所要保护的临界区访问时间比较短时,用自旋锁是比较方便的,因为它节省了上下文切换的时间,但是CPU得不到自旋锁,会在那里空转直到其他执行单元解锁为止,所以要求锁不能在临界区理长时间停留,否则会降低系统的效率。

因此,自旋锁和信号量的选用有3个规则:

1:当锁不能被获取时,使用信号量的开销是进程上下文的切换时间tsw,使用自旋锁的开销是等待获取自旋锁tcs,若tcs比较小,应使用自旋锁,若tcs很大,应使用信号量。

2:信号量所保护的临界区可包含引起阻塞的代码,而自旋锁则绝对要避免用来保护包含这样代码的临界区,因为阻塞意味着要进行进程的切换,如果进程被切换出去后,另一个进程企图获取本自旋锁,死锁就会发生。

3:信号量存在于进程的上下文中,因此,如果被保护的共享资源需要在中断和软中断情况下使用,则在信号量和自旋锁之间只能选择自旋锁。当然,如果一定要使用信号量,则只能通过down_trylock()方式进行,不能获取就立刻返回以避免阻塞。

读写信号量

读写信号量类似于自旋锁中的读写自旋锁,读写信号量可能引起进程阻塞,但它可允许N个读执行单元同时访问共享资源,而最多只能有一个写执行单元,因此,读写信号量是一种相对放宽条件的粒度稍大于信号量的互斥机制。

1定义和初始化读写信号量

struct rw_semaphore my_rws;    /*定义读写信号量*/

void init_rwsem(struct rw_semaphore *my_rws); /*初始化读写信号量*/

2读信号量的获取

void down_read(struct rw_semaphore *sem);

void down_read_trylock(struct rw_semaphore *sem);

读信号量的释放

void up_read(struct rw_semaphore *sem);

3写信号量的获取

void down_write(struct rw_semaphoe *sem);

void down_write_trylock(struct rw_semaphore *sem);

写信号量的释放

void up_write(struct rw_semaphore *sem);

读写信号量的一般用法:

rw_semaphore rw_sem;    //定义读写信号量

init_rwsem(&rw_sem);    //初始化读写信号量

down_read(&rw_sem);//读时获取信号量

...  //读时临界区                                      //此时允许多个读执行单位进行对共享资源的读操作,但不允许对资源的写操作

up_read(&rw_sem);//释放读信号量

down_write(&rw_sem);    //写时获得信号量

...  //写时临界区

up_write(&rw_sem);      //释放写时信号量

互斥体

在Linux中互斥体mutex是真实存在的。

1定义并初始化互斥体

struct mutex my_mutex;

mutex_init(&my_mutex);

2获得互斥体

void fastcall mutex_lock(struct mutex *lock);

int fastcall mutex_lock_interruptible(struct mutex *lock);

int fastcall mutex_lock_trylock(struct mutex *lock);

mutex_lock()和mutex_lock_interruptible()的区别和down()和down_trylock()的区别完全一致,前者引起的睡眠不能被信号打断,而后者可以。mutex_trylock()用于尝试获得Mutex,获取不到mutex时不会引起进程睡眠。

3释放互斥体

void fastcall mutex_unlcok(struct mutex *lock);

mutex使用的方法和信号量使用的场合完全一致,使用方法为

struct mutex my_mutex;        //定义一个互斥体

init_mutex(&my_mutex);        //初始化一个互斥体

mutex_lock(&my_mutex);      //获取mutex

...  //临界区

mutex_unlock(&my_mutex);//释放mutex

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值