linux内核信号量和互斥锁使用

信号量概念

Linux 内核的信号量在概念和原理上与用户态的 System V 的 IPC 机制信号量是一样的,但是它绝不可能在内核之外使用,因此它与 System V 的 IPC 机制信号量毫不相干。
信号量在创建时需要设置一个初始值,表示同时可以有几个任务可以访问该信号量保护的共享资源,初始值为 1 就变成互斥锁(Mutex),即同时只能有一个任务可以访问信号量保护的共享资源。
一个任务要想访问共享资源,首先必须得到信号量,获取信号量的操作将把信号量的值减 1,若当前信号量的值为负数,表明无法获得信号量,该任务必须挂起在该信号量的等待队列等待该信号量可用;若当前信号量的值为非负数,表示可以获得信号量,因而可以立刻访问被该信号量保护的共享资源。
当任务访问完被信号量保护的共享资源后,必须释放信号量,释放信号量通过把信号量的值加 1 实现,如果信号量的值为非正数,表明有任务等待当前信号量,因此它也唤醒所有等待该信号量的任务。

内核信号量核心结构

内核中使用 struct semaphore 结构来描述一个信号量,结构定义如下:

struct semaphore {
raw_spinlock_t lock;
unsigned int count; //信号值,只要这个值为正数,则信号可用, 一般情况设置 0 或 1。
struct list_head wait_list;
};

信号量相关 API

DEFINE_SEMAPHORE (name)

宏原型#define DEFINE_SEMAPHORE(name) \
struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)
宏功能该宏声明一个信号量 name 并初始化它的值为 1,即声明一个互斥锁
宏参数name: 要定义的信号量变量名
所在头文件include\linux\semaphore.h
宏定义文件include\linux\semaphore.h
备注Linux3.5 内核出现 在 linux 2.6 中名字为 DECLARE_MUTEX(name)

void sema_init(struct semaphore *sem, int val)

宏(函数) 原型

void sema_init(struct semaphore *sem, int val)

宏功能该函数用于初始化一个互斥锁,即它把信号量 sem 的值设置为 1。
宏参数sem: 要初始化的信号量的指针
所在头文件include\linux\semaphore.h
宏定义文件include\linux\semaphore.h
备注 

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

宏原型void sema_init (struct semaphore *sem, int val);
宏功能初始化设置信号量的初值,它设置信号量 sem 的值为 val
宏参数sem: 要初始化的信号量的指针;
val: 信号量的值
所在头文件include\linux\semaphore.h
宏定义文件include\linux\semaphore.h

定义、初始化信号量相关 API:这是一个静态定义方式。 定义一个名字为 name,信号量值为 1 的信号量结构变量。
示例:
struct semaphore sem;
sema_init(&sem, 1);
以上两行等效:
DEFINE_SEMAPHORE(sem);
 

void down(struct semaphore *sem)

函数原型void down(struct semaphore * sem);
函数功能用于获得信号量 sem,它会导致睡眠,因此不能在中断上下文(包括 IRQ 上下文和 softirq 上
下文)使用该函数。该函数将把 sem 的值减 1,如果信号量 sem 的值非负,就直接返回,否
则调用者将被挂起,直到别的任务释放该信号量才能继续运行。
函数参数sem: 要初始化获取的信号量的指针;
函数返回值
所在头文件include\linux\semaphore.h
函数定义文件kernel\semaphore.c

int down_timeout(struct semaphore *sem, long jiffies);
sem: 信号量结构指针
jiffies: 要设置超时时间,单位是时钟节拍。
阻塞地请求一个信号量,如果信号量大于 0, 则可以马上返回, 否则休眠,直到有其他进程释放信号量
(把信号量的 count 修改为大于 0 的值) 或超时时间到。
这个函数效果和 down 函数很像,它也是不可中断的休眠。
 

int down_interruptible(struct semaphore * sem);

函数原型int down_interruptible(struct semaphore * sem);
函数功能该函数功能与 down 类似,不同之处为, down 不会被信号(signal)打断,但 down_interruptible
能被信号打断,因此该函数有返回值来区分是正常返回还是被信号中断,如果返回 0,表示
获得信号量正常返回,如果被信号打断,返回-EINTR。
函数参数sem: 要获取的信号量的指针;
函数返回值0:得到信号量正常返回 ; 负数:被信号中断返回
所在头文件include\linux\semaphore.h
函数定义文件kernel\semaphore.c

int down_trylock(struct semaphore * sem);

函数原型int down_trylock(struct semaphore * sem);
函数功能该函数试着获得信号量 sem,如果能够立刻获得,它就获得该信号量并返回 0,否则,表示
不能获得信号量 sem,返回值为非 0 值。因此,它不会导致调用者睡眠,可以在中断上下文
使用。
函数参数sem: 要获取的信号量的指针;
函数返回值0:得到信号量正常返回 ; 非 0:得不到信号量
所在头文件include\linux\semaphore.h
函数定义文件kernel\semaphore.c

void up(struct semaphore * sem);

函数原型void up(struct semaphore * sem);
函数功能该函数释放信号量 sem,即把 sem 的值加 1,如果 sem 的值为非正数,表明有任务等待该信
号量,因此唤醒这些等待者。
函数参数sem: 要初释放的信号量的指针;
函数返回值
头文件include\linux\semaphore.h
函数定义文件kernel\semaphore.c

虽然信号量可以设置为大于 1 的值,但是信号量在绝大部分情况下作为互斥锁使用。

 


简单信号量使用例子实现设备只能被一个进程打开:

#include <linux/module.h> /* Needed by all modules */
#include <linux/init.h> /* Needed for the module-macros */
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <asm/atomic.h>
#define LEDS_MAJOR 255
#define DEVICE_NAME "miscdev_semaphore"

DECLARE_MUTEX(lock); //定义一个互斥信号量,并初始化 1;

static int first_miscdev_open(struct inode *pinode, struct file *pfile)
{
printk (KERN_EMERG "Linux miscdevice:%s is call\r\n",__FUNCTION__);

/* 获取信号量 */
if (!down_trylock(&lock))
return 0;
else
return -EBUSY;
}

int first_miscdev_release (struct inode *inode, struct file *file)
{
printk (KERN_EMERG "Linux miscdevice:%s is call\r\n",__FUNCTION__);

up(&lock); //释放信号量
return 0;
}
static struct file_operations dev_fops =
{
.owner = THIS_MODULE,
.open = first_miscdev_open,
.release= first_miscdev_release,
};
static struct miscdevice misc =
{
.minor = LEDS_MAJOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
/* 模块装载执行函数 */
static int __init first_miscdev_init(void)
{
int ret;
ret = misc_register(&misc); //注册混杂设备
if(ret <0)
printk (KERN_EMERG DEVICE_NAME"\t err\r\n");
printk (KERN_EMERG DEVICE_NAME"\tinitialized\n");
return ret;
}

/* 模块卸载执行函数 */
static void __exit first_miscdev_exit(void)
{
misc_deregister(&misc);
printk(KERN_EMERG "Goodbye,cruel world!, priority = 0\n");
}
module_init(first_miscdev_init);
module_exit(first_miscdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("XYD");
MODULE_DESCRIPTION("This the samlpe drv_test");

互斥信号量

互斥信号量概念:

互斥锁主要用于实现内核中的互斥访问功能。内核互斥锁是在原子 API 之上实现的,但这对于内核用户是不可见的。 在大部分场合中,对共享资源的访问,都是使用独占方式, 在这种情况下, 提供专门的互斥接口给编程者使用。 对它的访问必须遵循一些规则:同一时间只能有一个任务持有互斥锁,而且只有这个任务可以对互斥锁进行解锁。互斥锁不能进行递归锁定或解锁。一个互斥锁对象必须通过其 API 初始化,而不能使用 memset 或复制初始化。一个任务在持有互斥锁的时候是不能结束的。互斥锁所使用的内存区域是不能被释放的。使用中的互斥锁是不能被重新初始化的。并且互斥锁不能用于中断上下文。但是互斥锁比当前的内核信号量选项更快,并且更加紧凑,因此如果它们满足您的需求,那么它们将是您明智的选择。

内核信号量核心结构

内核中使用 struct mutex 结构来描述一个信号量,结构定义如下:
 

struct mutex {
/* 1: unlocked, 0: locked, negative: locked, possible waiters */
atomic_t count;
spinlock_t wait_lock;
struct list_head wait_list;
#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
struct task_struct *owner;
#endif
#ifdef CONFIG_DEBUG_MUTEXES
const char *name;
void *magic;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
};

关键成员说明:
atomic_t count:
指示互斥锁的状态:
1--没有上锁,可以获得
0--被锁定,不能获得
负数--被锁定,且可能在该锁上有等待进程
初始化时为没有上锁状态。


spinlock_t wait_lock:
等待获取互斥锁中使用的自旋锁。在获取互斥锁的过程中,操作会在自旋锁的保护中进行。初始化为为
锁定, 用户一般无需关心。

struct list_head wait_list:
等待互斥锁的进程队列, 用户无需关心。

互斥信号量相关 API

DEFINE_MUTEX(mutexname)

宏原型#define DEFINE_MUTEX(mutexname) \
struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
宏功能该宏声明一个互斥信号量 name 并初始化它
宏参数name: 要定义的互斥信号量变量名
所在头文件include\linux\mutex.h
备注linux3.5出现

mutex_init(mutex)

宏原型# define mutex_init(mutex) \
do { \
static struct lock_class_key __key; \
__mutex_init((mutex), #mutex, &__key); \
} while (0)
宏功能该宏初始化一个已经定义的互斥信号量, metex 传递的是指针
宏参数mutex: 初始化的的互斥信号量指针
所在头文件include\linux\mutex.h
宏定义文件include\linux\mutex.h

int mutex_is_locked(struct mutex *lock)

宏原型int mutex_is_locked(struct mutex *lock)
宏功能该函数检测互斥锁是否已经被锁定,
宏参数lock: 互斥锁变量是指针
返回值1 : 已经锁定 0: 没有锁定
所在头文件include\linux\mutex.h

mutex_lock(lock) 或 void mutex_lock(struct mutex *lock);

宏(函数) 原型mutex_lock(lock)

void mutex_lock(struct mutex *lock);
宏功能该宏(函数) 获取互斥锁是否, 如没有得到会休眠,直接得到为止
宏参数lock: 互斥锁变量是指针
返回值
所在头文件include\linux\mutex.h
宏定义文件include\linux\mutex.h
备注根据内核配置不同,该功能有有宏版本和函数版本

mutex_lock_interruptible(lock) 或 int mutex_lock_interruptible(struct mutex *lock);

宏原型#define mutex_lock_interruptible(lock) mutex_lock_interruptible_nested(lock, 0)

int __must_check mutex_lock_interruptible(struct mutex *lock);
宏功能该宏(函数)获取互斥锁是否, 如没有得到会休眠,直接得到为止, 但是可以被信号中断
宏参数lock: 互斥锁变量是指针
返回值成功获得锁返回 0, 被信号中断返回-EINIR
所在头文件include\linux\mutex.h
宏定义文件include\linux\mutex.h
备注根据内核配置不同,该功能有有宏版本和函数版本

int mutex_trylock(struct mutex *lock);

宏原型int mutex_trylock(struct mutex *lock);
宏功能该函数是获取互斥锁, 如没有得到不会休眠,马上返回
宏参数lock: 互斥锁变量是指针
返回值
所在头文件include\linux\mutex.h
宏定义文件include\linux\mutex.c
备注根据内核配置不同,该功能有有宏版本和函数版本

void mutex_unlock(struct mutex *lock);

宏原型void mutex_unlock(struct mutex *lock);
宏功能该函数是释放互斥锁
宏参数lock: 互斥锁变量是指针
返回值
所在头文件include\linux\mutex.h
宏定义文件include\linux\mutex.c

互斥锁应用例子:

/* chardev.c */
#include <linux/module.h>       /* Needed by all modules */
#include <linux/init.h>              /* Needed for the module-macros */
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>       /* 自动创建设备文件 */
#include <linux/miscdevice.h>
#include <asm/io.h>                /*ioremap*/
#include <asm/uaccess.h>       /*copy_from_user ,copy_to_user*/
#include <linux/atomic.h>
#include <linux/mutex.h>

#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>  /* s3c_gpio_cfgpin  S3C_GPIO_OUTPUT */
static struct cdev *pcdev;
static struct device *this_device = NULL;
static struct class *leds_class = NULL;

/* 是否开启 gpio_request 函数功能 */
#define GPIO_REQUEST          1     //0 :关,非0 :开 

static int led_gpios[] = {
  EXYNOS4X12_GPM4(0),
  EXYNOS4X12_GPM4(1),
  EXYNOS4X12_GPM4(2),
  EXYNOS4X12_GPM4(3),
};

#define LED_NUM     ARRAY_SIZE(led_gpios)
#define   DEV_NAME   "myleds"
unsigned int major  = 0;                    //主设备号
unsigned int minor = 0;                   //次设备号

unsigned int devnr = 0;                    //设备号
char *chrdev_name = DEV_NAME;  //设备名
module_param(major, int, S_IRUGO | S_IWUSR);
module_param(minor, int, S_IRUGO | S_IWUSR);
module_param(chrdev_name, charp, S_IRUGO | S_IWUSR);


struct mutex lock;

//打开设备时候执行的程序
static int  chrdev_open(struct inode *pinode, struct file *pfile)
{
   mutex_lock(&lock);

  printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);
  return 0;
}

//关闭设备时执行的程序
static int chrdev_release(struct inode *pinode, struct file *pfile)
{
   mutex_unlock(&lock);
  printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);
  return 0;
}



//读接口函数
static ssize_t chrdev_read ( struct file *file,
                             char __user *buf,
                             size_t count,
                             loff_t * f_pos )
{
  char led_buffer[4] = {2, 2, 2, 2};
  int i = 0;
  //  printk ( KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__ );

  /* count ==0 ,则返回0,不是错误 */
  if ( !count ) {
    return 0;
  }

  /* 当前已经到文件末尾,不能再写,返回0,*/
  if ( count > LED_NUM ) {
    count =  LED_NUM;
  }


  /* 准备数据,0表示灭,1表示亮 */
  for (i = 0; i < LED_NUM; i++) {
    led_buffer[i] = !gpio_get_value(led_gpios[i]);
  }

  if ( copy_to_user ( buf, &led_buffer[0], count ) ) {
    return -EFAULT;
  }

  return count;
}



//写接口函数
static ssize_t chrdev_write(struct file *pfile,
                            const char __user *user_buf,
                            size_t count,
                            loff_t *off)
{
  int ret = 0;
  char buf[LED_NUM] = {0};
  int i = 0;
  // printk(KERN_EMERG "line:%d,%s is call\n", __LINE__, __FUNCTION__);
  //应用程序传递count=0下来,并不是错误,应该返回0
  if(count == 0) {
    return 0;
  }

  //因为板子只有4个灯,所以防止用户程序恶意破坏系统。
  if(count > LED_NUM) {
    count = LED_NUM;
  }

  //把用户空间传递下来的数据复制到内核空间的buf数组中
  ret = copy_from_user(buf, user_buf, count); //返回,成功是0,其他是失败
  if(ret) { //如果复制失败返回-1;
    return -EFAULT;
  }

  for(i = 0; i < count; i++) {
    if(buf[i] == 1)
      //                  GPM4DAT &= ~(1 << (0 + i));/* 亮 */

    {
      gpio_set_value(led_gpios[i], 0);
    }

    else if(buf[i] == 0)
      //                  rGPM4DAT |=  (1 << (0 + i));/* 灭 */
    {
      gpio_set_value(led_gpios[i], 1);
    }

  }

  count  = 1;


  return count;
}



//文件操作方法:
static const  struct file_operations chrdev_fops = {
  .read      = chrdev_read,
  .write     = chrdev_write,
  .release  = chrdev_release,
  .open     = chrdev_open,
};



static int __init chrdev_init(void)
{

  int ret = 0;
  int i;

  /* IO口配置代码 */
  for (i = 0; i < LED_NUM; i++) {
#if GPIO_REQUEST
    ret = gpio_request(led_gpios[i], "LED");
    if (ret) {
      printk("%s: request GPIO %d for LED failed, ret = %d\n",
             chrdev_name, led_gpios[i], ret);
      goto gpio_request_err;
    }
#endif

    //s3c_gpio_cfgpin(led_gpios[i], S3C_GPIO_OUTPUT);
    //gpio_set_value(led_gpios[i], 1);
    //gpio_direction_output等效于上面两条语句,
    gpio_direction_output(led_gpios[i], 1);

  }

  //分配cdev核心结构;
  pcdev = cdev_alloc();
  if (pcdev == NULL) {
    ret = -ENOMEM;
    printk(KERN_EMERG"cdev_alloc_err error\n");
    goto cdev_alloc_err;
  }

  //如果主设备号是0,则动态分配设备号
  if(!major) {
    ret  = alloc_chrdev_region(&devnr, minor, 1, chrdev_name);
    if(ret < 0) {
      printk(KERN_EMERG"alloc_chrdev_region error\n");
      goto devnr_requst_err;
    }

  } else {

    //合成设备号
    devnr =  MKDEV(major, minor);

    //静态注册设备号
    ret = register_chrdev_region(devnr, 1, chrdev_name);
    if(ret != 0) {
      printk(KERN_EMERG"register_chrdev_region error\n");
      goto devnr_requst_err;
    }
  }

  //cdev结构体初始化
  cdev_init(pcdev, &chrdev_fops);

  //注册字符设备
  ret = cdev_add(pcdev, devnr, 1);
  if ( ret < 0) {
    printk(KERN_EMERG"cdev_add error\n");
    goto cdev_add_err;
  }

  //输出字符设备主设备号和次设备号
  printk(KERN_EMERG "name:%s,major:%d,minor:%d\n",
         chrdev_name, MAJOR(devnr), MINOR(devnr));

  //创建一个类
  leds_class =   class_create(THIS_MODULE, "leds_class");
  if ( IS_ERR(leds_class) ) {
    ret = PTR_ERR(leds_class);
    printk(KERN_EMERG"class_create error\n");
    goto class_create_err;
  }

  //创建一个设备
  this_device = device_create(leds_class, NULL, devnr, NULL, "%s", chrdev_name);
  if ( IS_ERR(this_device) ) {
    ret = PTR_ERR(this_device);
    printk(KERN_EMERG"this_device error\n");
    goto device_create_err;
  }

  mutex_init(&lock);
  return 0;

device_create_err:
  class_destroy(leds_class);
class_create_err:
  unregister_chrdev(major, chrdev_name);
cdev_add_err:
devnr_requst_err:
  if (pcdev) {
    cdev_del(pcdev);
  }
  unregister_chrdev_region(devnr, 1);
cdev_alloc_err:

#if GPIO_REQUEST
gpio_request_err:
  /* gpio 反向释放 */
  for ( --i ; i >= 0 ; i-- ) {
    gpio_free(led_gpios[i]);
  }
#endif

  return ret;
}

static void  __exit chrdev_exit(void)
{
  int i = 0;
  device_destroy(leds_class, devnr);
  class_destroy(leds_class);

  /* gpio 反向释放 */
  for (   ; i < 4 ; i++) {
    gpio_free(led_gpios[i]);
  }

  cdev_del(pcdev);
  unregister_chrdev_region(devnr, 1);
  printk(KERN_EMERG "Goodbye,chrdev\n");
}
module_init(chrdev_init);
module_exit(chrdev_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("learn");                //optional
MODULE_DESCRIPTION("STUDY_MODULE"); //optional


 

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值