linux驱动程序中的并发控制-5(信号量(semaphore))-47

Posted 杨斌并

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux驱动程序中的并发控制-5(信号量(semaphore))-47相关的知识,希望对你有一定的参考价值。

信号量(semaphore)

  • 信号量是用于保护临界区的一种常用方法,它的使用方式与自旋锁类似。
  • 与自旋锁相同,只有得到信号量的进程才能执行临界区代码。
  • 但与自旋锁不同的是,在未获取信号量时,进程不会像自旋锁-样原地打转,而是进入休眠等待状态。因此当信号量阻塞时消耗的系统资源(主要是CPU资源)并不多,也不会出现死机的现象。

信号量的使用

  1. 定义信号量(#include <linux/semaphore.h>)
struct semaphore  sem;
  1. 初始化信号量
sema_init(&sem, 1);

函数原型

static inline void sema_init(struct semaphore *sem, int val)

	static struct lock_class_key __key;
	*sem = (struct semaphore) __SEMAPHORE_INITIALIZER(*sem, val);
	lockdep_init_map(&sem->lock.dep_map, "semaphore->lock", &__key, 0);

  • sem semaphore 的结构体指针
  • val 信号量的初始值,表示同时只能有val个进程访问共享资源

如果val等于1,表示同时只能有一个进程访问共享资源,如果有多于一个进程视图同时获取信号量,除了当前正在访问共享资源的进程外,其他的进程都将进入休眠状态,直到当前访问共享资源的进程释放信号量。如果让信号量和自旋锁起到同样的效果(共享数据同时只能由一个进程访问),val 参数的值应为1。如果val参数的值大于1,信号量就相当于一一个计数器。直到信号量为0,试图获取信号量才会被阻塞。.


  1. 获取信号量
extern void down(struct semaphore *sem);
extern int __must_check down_interruptible(struct semaphore *sem);
extern int __must_check down_trylock(struct semaphore *sem);
  • down函数用于获取信号量sem,如果未能获取信号量,会导致当前进程休眠,中断仍然无法唤醒,因此不能在中断上下文使用。
  • down_interruptible 函数与down函数类似,在未获得信号量时仍然可导致休眠,但在睡眠过程中可以被中断信号打断。打断时该函数返回非0值,正常结束(成功获取了信号量)返回0。一般在使用down_interruptible 函数时都会对其返回值进行判读,代码如下:
   if (down_interruptible(&sem)) 
        return  -ERESTARTSYS;
    
  • 使用down trylock 函数获取信号量时,不管是否成功获取信号量,该函数都会立即返回。如果成功获取信号量,返回0,否则返回非0值。down_trylock 函数不会休眠,可以用在中断上下文。
  1. 释放信号量
extern void up(struct semaphore *sem);

实例代码

信号量的初始值为1,当读取设备文件时,执行down函数成功获取信号量,并延长10秒来模拟执行临界区代码的过程,最后会执行up函数释放信号量。在10秒之内会向设备文件写入数据,这时会再执行down函数获取信号量,但down函数将会眠。直到5秒后,读设备文件时执行up函数释放信号量。写设备文件的write函数中的down函数才能被激活。示例的代码如下:

  • semaphore_test.c
//
//  rcu_test.c
//  seqlock_demo
//
//  Created by lianfei on 2021/7/13.
//

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/semaphore.h>
#include <linux/delay.h>

#define DEVICE_RCU_NAME "semaphore"

struct semaphore  sem;

static ssize_t  demo_read(struct file *file, char __user * buf, size_t count, loff_t *ppos)
    struct timeval tv;
    
    do_gettimeofday(&tv);
    //输出单前的秒数
    printk("semaphore read: start %ld\\n", tv.tv_sec);
    //获取信号量
    down(&sem);
    //延迟10秒
    mdelay(10000);
    //释放信号量
    up(&sem);
    
    do_gettimeofday(&tv);
    
    printk("semphore read: end %ld\\n", tv.tv_sec);
    
    
    return 0;



static ssize_t demo_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
    struct timeval tv;
    
    do_gettimeofday(&tv);
    printk("semphore write: start %ld \\n", tv.tv_sec);
    
    down(&sem);
    up(&sem);
    do_gettimeofday(&tv);
    printk("semphore write: end %ld\\n", tv.tv_sec);
    
    return count;


static int demo_release(struct inode *node, struct file *file)
    return 0;


static int demo_open(struct inode *node, struct file *file)
    return 0;


static struct file_operations dev_fops=
    .owner = THIS_MODULE,
    .open = demo_open,
    .release = demo_release,
    .read = demo_read,
    .write = demo_write
;

static struct miscdevice misc=
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_RCU_NAME,
    .fops = &dev_fops
;



static int demo_init(void)

    int ret=misc_register(&misc);
    if(ret < 0 )
        printk("atomic_init is error\\n");
        return -1;
    
    printk("demo_init_success\\n");
    sema_init(&sem, 1);
    return ret;


static void demo_exit(void)
    printk("ademo_exit_success\\n");
    misc_deregister(&misc);


module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("binbing.Yang");
  • semaphore_test.sh
#!/system/bin/sh

cat /dev/semaphore
sleep 5
echo data > /dev/semaphore

输出 read end 和 write end 相差5秒,说明demo_write 函数中通过down函数休眠了5秒

以上是关于linux驱动程序中的并发控制-5(信号量(semaphore))-47的主要内容,如果未能解决你的问题,请参考以下文章

linux驱动程序中的并发控制-5(信号量(semaphore))-47

在 Linux 中控制信号量队列中的出队顺序

Linux驱动设备中的并发控制

linux线程-使用信号量同步

linux驱动程序中的并发控制-6(读写信号量)-48

linux驱动程序中的并发控制-6(读写信号量)-48