Linux 内核中的并发--信号量与互斥体

来源:互联网 发布:巨人网络总部地址 编辑:程序博客网 时间:2024/06/06 03:34

信号量(down操作->临界区->up操作)

信号量的使用方式和自旋锁类似,进程只有得到信号量才能执行临界区代码
但与自旋锁不同的是,当进程获取不到信号量时并不是原地打转而是睡眠等待

中断服务函数不能进行睡眠,因此信号量不能用于中断当中,如果中断函数一定要用信号量可以使用尝试上锁(down_trylock)进行操作,不能获取锁就立刻返回,以避免阻塞,通过返回值判读可执行与否

信号量相关函数介绍

#include <linux/semaphore.h>//信号量相关函数的头文件

定义一个信号量

struct semaphore my_sem;

初始化信号量

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

参数1:信号量变量

参数2:信号量的计数值

获取信号量(减操作,不能被系统消息打断,导致调用者睡眠)

void down(struct semaphore *sem);


获取信号量(减操作,可以被系统消息打断,导致调用者睡眠)

int down_interruptible(struct semaphore *sem);

尝试获得信号量,成功返回0,失败返回非0,不会导致调用者睡眠)

int down_trylock(struct semaphore *sem);

释放信号量,即使信号量加1(如果线程睡眠,将其唤醒)

void up(struct semaphore *sem);

信号量小结


互斥体

互斥体,省去了信号量up和down的复杂操作,使互斥变得更简单,其本质等同于信号量,相关操作:


实例代码:

驱动端:

#include <linux/device.h>#include <linux/interrupt.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/irq.h>#include <asm/uaccess.h>#include <asm/irq.h>#include <asm/io.h>#include <mach/gpio.h>#include <linux/delay.h>#include <mach/regs-gpio.h>  /*S5PV210_GPH3_BASE*/#include <linux/semaphore.h>#define EINT_DEVICE_ID1#define DRIVER_NAME"key_eint_race"#define err(msg) printk(KERN_ERR "%s: " msg "\n", DRIVER_NAME)#define __debug(fmt, arg...)printk(KERN_DEBUG fmt, ##arg)#define GPH3CON(unsigned long)(S5PV210_GPH3_BASE+ 0x00)#define GPH3DAT(unsigned long)(S5PV210_GPH3_BASE + 0x04)#define GPH2UP(unsigned long)(S5PV210_GPH2_BASE + 0x08)static int major = 0;/* Driver Major Number */static int minor = 0;/* Driver Minor Number */struct class *key_class;static struct device *key_device;static unsigned int key;static struct semaphore my_sem;static unsigned int deal_key_value(unsigned int data){key = data;mdelay(1000);return key;}irqreturn_t buttons_interrupt(int irq, void *dev_id){if(down_trylock(&my_sem) == 0){deal_key_value((unsigned int)dev_id);up(&my_sem);}//__debug("in eint function...\n");return IRQ_HANDLED;}static void key_io_port_init(void){unsigned long reg_val;reg_val = readl(GPH3CON);reg_val &= ~((0x0f<<0) | (0x0f<<4));reg_val |= ((0x01<<0) | (0x01<<4));writel(reg_val, GPH3CON);reg_val = readl(GPH3DAT);reg_val &= ~((0x01<<0) | (0x01<<1));writel(reg_val, GPH3DAT);reg_val = readl(GPH2UP);reg_val &= ~(0x03<<8);reg_val |= 0x02<<8;writel(reg_val, GPH2UP);}static ssize_t key_read(struct file *filp, char *buf, size_t count, loff_t *f_pos){int key_num;int cpy_len;int retval;/**信号量是在进程调度的层次实现互斥,它实现了进程间抢占导致的并发操作,*   但不能解决中断产生的并发操作,我们的解决办法是尽量不在中断函数中访问*   临界区,或者在中断函数中采用尝试上锁的方式*/down(&my_sem);//信号量减1key_num =deal_key_value(current->pid);up(&my_sem);//信号量加1cpy_len = min(sizeof(key_num), count);retval = copy_to_user(buf, &key_num, cpy_len);return (cpy_len - retval);}/* Driver Operation structure */static struct file_operations key_fops = {.owner = THIS_MODULE,.read = key_read,};static int __init key_eint_init(void){int retval;key_io_port_init();sema_init(&my_sem, 1);//__debug("in key_eint_init\n");retval = set_irq_type(IRQ_EINT(20),IRQ_TYPE_EDGE_FALLING);if(retval){err("IRQ_EINT20 set irq type failed");goto error;}retval = request_irq(IRQ_EINT(20), buttons_interrupt, IRQF_DISABLED, "KEY1", (void *)EINT_DEVICE_ID);if(retval){err("request eint20 failed");goto error;}/* Driver register */major = register_chrdev(major, DRIVER_NAME, &key_fops);if(major < 0){err("register char device fail");retval = major;goto error_register;}key_class=class_create(THIS_MODULE,DRIVER_NAME);if(IS_ERR(key_class)){err("class create failed!");retval =  PTR_ERR(key_class);goto error_class;}key_device=device_create(key_class,NULL, MKDEV(major, minor), NULL,DRIVER_NAME);if(IS_ERR(key_device)){err("device create failed!");retval = PTR_ERR(key_device);goto error_device;}__debug("register myDriver OK! Major = %d\n", major);return 0;error_device:class_destroy(key_class);error_class:unregister_chrdev(major, DRIVER_NAME);error_register:free_irq(IRQ_EINT(20), (void *)EINT_DEVICE_ID);error:return retval;}static void __exit key_eint_exit(void){//__debug("in key_eint_exit\n");free_irq(IRQ_EINT(20), (void *)EINT_DEVICE_ID);unregister_chrdev(major, DRIVER_NAME);device_destroy(key_class,MKDEV(major, minor));class_destroy(key_class);return;}module_init(key_eint_init);module_exit(key_eint_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Eric");


应用层:

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <fcntl.h>/*Linux内核是抢占式内核,在一个系统调用未结束前,另一个系统调用也可以进入进程上下文,访问同一缓冲区*/int main(void){int status;pid_t pid;//打开文件raceStationint fd_driver;if((fd_driver = open("/dev/key_eint_race", O_RDWR)) < 0){printf("file open error\n");exit(1);}//创建子进程 if((pid = fork()) < 0){perror("fork:");exit(1);}else if(pid == 0){//判断如果是子进程int num;while(1){read(fd_driver,&num,sizeof(num));printf("the num value is <son-%d>: %d\n",getpid(), num);//usleep(50*1000);}close(fd_driver);}else{//判断如果是父进程int num;while(1){read(fd_driver,&num,sizeof(num));printf("the num value is <father-%d>: %d\n",getpid(), num);//usleep(50*1000);}pid = wait(&status);close(fd_driver);}}


原创粉丝点击