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

📅 2026/7/28 18:48:13
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; };信号量相关 APIDEFINE_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.cint 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.cint down_trylock(struct semaphore * sem);函数原型int down_trylock(struct semaphore * sem);函数功能该函数试着获得信号量 sem如果能够立刻获得它就获得该信号量并返回 0否则表示不能获得信号量 sem返回值为非 0 值。因此它不会导致调用者睡眠可以在中断上下文使用。函数参数sem 要获取的信号量的指针函数返回值0得到信号量正常返回 非 0得不到信号量所在头文件include\linux\semaphore.h函数定义文件kernel\semaphore.cvoid 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等待互斥锁的进程队列 用户无需关心。互斥信号量相关 APIDEFINE_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.hint mutex_is_locked(struct mutex *lock)宏原型int mutex_is_locked(struct mutex *lock)宏功能该函数检测互斥锁是否已经被锁定宏参数lock 互斥锁变量是指针返回值1 已经锁定 0 没有锁定所在头文件include\linux\mutex.hmutex_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__); //应用程序传递count0下来并不是错误应该返回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_EMERGcdev_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_EMERGalloc_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_EMERGregister_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_EMERGcdev_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_EMERGclass_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_EMERGthis_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