常用的处理并发和竞争的机制有四种,原子操作、自旋锁、信号量和互斥体。下边就通过编写驱动来实现,展示一下相关效果。当前台的应用一直运行,控制台是不能输入指令,测试并发与竞争最好是在后台运行,而解决并非与竞争最直接的手段就是只允许一个应用去调用相关资源,这里为了好展示效果就通过任务运行来体现。下面的实验使用了新字符驱动GPIO源码,只需要复制一份即可使用。
|原子操作
驱动源码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*添加头文件*/
#include
#include
#include
#include
#defineCHRDEVBASE_CNT1/*设备号个数*/
#defineCHRDEVBASE_NAME"chrdevbase"/*名字*/
#defineLEDOFF0/*关灯*/
#defineLEDON1/*开灯*/
/*chrdevbase设备结构体*/
structnewchr_dev{
dev_tdevid;/*设备号*/
structcdevcdev;/*cdev*/
structclass*class;/*类*/
structdevice*device;/*设备*/
intmajor;/*主设备号*/
intminor;/*次设备号*/
structdevice_node*nd;/*设备节点*/
intled_gpio;/*led所使用的GPIO编号*/
atomic_tlock;/*原子变量*/
};
structnewchr_devchrdevbase;/*自定义字符设备*/
/*
*@description:LED硬件初始化
*@param:无
*@return:无
*/
staticintled_hal_init(void)
{
intret=0;
/*设置LED所使用的GPIO*/
/* 1、获取设备节点:gpioled */
chrdevbase.nd=of_find_node_by_path("/gpioled");
if(chrdevbase.nd==NULL){
printk("chrdevbasenodecantnotfound!
");
return-EINVAL;
}else{
printk("chrdevbasenodehasbeenfound!
");
}
/*2、获取设备树中的gpio属性,得到LED所使用的LED编号*/
chrdevbase.led_gpio=of_get_named_gpio(chrdevbase.nd,"led-gpio",0);
if(chrdevbase.led_gpio< 0){
printk("can'tgetled-gpio");
return-EINVAL;
}
printk("led-gpionum=%d
",chrdevbase.led_gpio);
/*3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯*/
ret=gpio_direction_output(chrdevbase.led_gpio,1);
if(ret< 0){
printk("can'tsetgpio!
");
}
return0;
}
/*
*@description:打开设备
*@param-inode:传递给驱动的inode
*@param-filp:设备文件,file结构体有个叫做private_data的成员变量
*一般在open的时候将private_data指向设备结构体。
*@return:0成功;其他失败
*/
staticintchrdevbase_open(structinode*inode,structfile*filp)
{
/*通过判断原子变量的值来检查LED有没有被别的应用使用*/
if(!atomic_dec_and_test(&chrdevbase.lock))
{
atomic_inc(&chrdevbase.lock);/*小于0的话就加1,使其原子变量等于0*/
return-EBUSY;/*LED被使用,返回忙*/
}
printk("[BSP]chrdevbaseopen!
");
filp->private_data=&chrdevbase;/*设置私有数据*/
return0;
}
/*
*@description:从设备读取数据
*@param-filp:要打开的设备文件(文件描述符)
*@param-buf:返回给用户空间的数据缓冲区
*@param-cnt:要读取的数据长度
*@param-offt:相对于文件首地址的偏移
*@return:读取的字节数,如果为负值,表示读取失败
*/
staticssize_tchrdevbase_read(structfile*filp,char__user*buf,size_tcnt,loff_t*offt)
{
printk("chrdevbaseread!
");
return0;
}
/*
*@description:向设备写数据
*@param-filp:设备文件,表示打开的文件描述符
*@param-buf:要写给设备写入的数据
*@param-cnt:要写入的数据长度
*@param-offt:相对于文件首地址的偏移
*@return:写入的字节数,如果为负值,表示写入失败
*/
staticssize_tchrdevbase_write(structfile*filp,constchar__user*buf,size_tcnt,loff_t*offt)
{
intretvalue=0;
charwritebuf[1];
structnewchr_dev*dev=filp->private_data;
/*接收用户空间传递给内核的数据并且打印出来*/
retvalue=copy_from_user(writebuf,buf,cnt);
printk("[BSP]kernelrecevdatadata:%d!
",writebuf[0]);
if(writebuf[0]==LEDON){
gpio_set_value(dev->led_gpio,0);/*打开LED灯*/
}elseif(writebuf[0]==LEDOFF){
gpio_set_value(dev->led_gpio,1);/*关闭LED灯*/
}
//printk("chrdevbasewrite!
");
return0;
}
/*
*@description:关闭/释放设备
*@param-filp:要关闭的设备文件(文件描述符)
*@return:0成功;其他失败
*/
staticintchrdevbase_release(structinode*inode,structfile*filp)
{
structnewchr_dev*dev=filp->private_data;
/*关闭驱动文件的时候释放原子变量*/
atomic_inc(&dev->lock);
printk("[BSP]release!
");
return0;
}
/*
*设备操作函数结构体
*/
staticstructfile_operationschrdevbase_fops={
.owner=THIS_MODULE,
.open=chrdevbase_open,
.read=chrdevbase_read,
.write=chrdevbase_write,
.release=chrdevbase_release,
};
/*
*@description:驱动入口函数
*@param:无
*@return:0成功;其他失败
*/
staticint__initchrdevbase_init(void)
{
/*初始化原子变量*/
atomic_set(&chrdevbase.lock,1);/*原子变量初始值为1*/
/*初始化硬件*/
led_hal_init();
/*注册字符设备驱动*/
/*1、创建设备号*/
if(chrdevbase.major){/*定义了设备号*/
chrdevbase.devid=MKDEV(chrdevbase.major,0);
register_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT,CHRDEVBASE_NAME);
}else{/*没有定义设备号*/
alloc_chrdev_region(&chrdevbase.devid,0,CHRDEVBASE_CNT,CHRDEVBASE_NAME);/*申请设备号*/
chrdevbase.major=MAJOR(chrdevbase.devid);/*获取主设备号*/
chrdevbase.minor=MINOR(chrdevbase.devid);/*获取次设备号*/
}
printk("newcheledmajor=%d,minor=%d
",chrdevbase.major,chrdevbase.minor);
/*2、初始化cdev*/
chrdevbase.cdev.owner=THIS_MODULE;
cdev_init(&chrdevbase.cdev,&chrdevbase_fops);
/*3、添加一个cdev*/
cdev_add(&chrdevbase.cdev,chrdevbase.devid,CHRDEVBASE_CNT);
/*4、创建类*/
chrdevbase.class=class_create(THIS_MODULE,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.class)){
returnPTR_ERR(chrdevbase.class);
}
/*5、创建设备*/
chrdevbase.device=device_create(chrdevbase.class,NULL,chrdevbase.devid,NULL,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.device)){
returnPTR_ERR(chrdevbase.device);
}
return0;
}
/*
*@description:驱动出口函数
*@param:无
*@return:无
*/
staticvoid__exitchrdevbase_exit(void)
{
/*注销字符设备*/
cdev_del(&chrdevbase.cdev);/*删除cdev*/
unregister_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT);/*注销设备号*/
device_destroy(chrdevbase.class,chrdevbase.devid);/*销毁设备*/
class_destroy(chrdevbase.class);/*销毁类*/
printk("[BSP]chrdevbaseexit!
");
}
/*
*将上面两个函数指定为驱动的入口和出口函数
*/
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
/*
*LICENSE和作者信息
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");
应用源码
#include"stdio.h"
#include"unistd.h"
#include"sys/types.h"
#include"sys/stat.h"
#include"fcntl.h"
#include"stdlib.h"
#include"string.h"
/*
*@description:main主程序
*@param-argc:argv数组元素个数
*@param-argv:具体参数
*@return:0成功;其他失败
*/
intmain(intargc,char*argv[])
{
intfd,retvalue;
char*filename;
charwritebuf[100];
unsignedchardatabuf[1];
unsignedcharcnt=0;
if(argc!=3){
printf("[APP]ErrorUsage!
");
return-1;
}
filename=argv[1];
/*打开驱动文件*/
fd=open(filename,O_RDWR);
if(fd< 0){
printf("[APP]Can'topenfile%s
",filename);
return-1;
}
/*把第三个参数赋值给databuf*/
databuf[0]=atoi(argv[2]);
/*向设备驱动写数据*/
memcpy(writebuf,databuf,sizeof(databuf));
retvalue=write(fd,writebuf,sizeof(databuf));
if(retvalue< 0){
printf("[APP]writefile%sfailed!
",filename);
}
/*模拟占用25SLED*/
while(1)
{
sleep(5);
cnt++;
printf("Apprunningtimes:%d
",cnt);
if(cnt>=5)break;
}
printf("Apprunningfinished!");
/*关闭设备*/
retvalue=close(fd);
if(retvalue< 0){
printf("[APP]Can'tclosefile%s
",filename);
return-1;
}
return0;
}
实验现象
加了原子操作后,应用程序运行时,再次触发是不能运行的,这就解决了在复杂环境下的并发和竞争的问题。注意不加“&”表示直接运行,控制台不能输入指令,加了“&”表示后台运行,可以继续输入指令。
套路分析
1、先在结构体定义一个变量
/*chrdevbase设备结构体*/
structnewchr_dev{
......
atomic_tlock;/*原子变量*/
};
2、在驱动入口初始化原子变量
/*
*@description:驱动入口函数
*@param:无
*@return:0成功;其他失败
*/
staticint__initchrdevbase_init(void)
{
/*初始化原子变量*/
atomic_set(&chrdevbase.lock,1);/*原子变量初始值为1*/
......
return0;
}
3、在打开设备时判断原子变量
/*
*@description:打开设备
*@param-inode:传递给驱动的inode
*@param-filp:设备文件,file结构体有个叫做private_data的成员变量
*一般在open的时候将private_data指向设备结构体。
*@return:0成功;其他失败
*/
staticintchrdevbase_open(structinode*inode,structfile*filp)
{
/*通过判断原子变量的值来检查LED有没有被别的应用使用*/
if(!atomic_dec_and_test(&chrdevbase.lock))
{
atomic_inc(&chrdevbase.lock);/*小于0的话就加1,使其原子变量等于0*/
return-EBUSY;/*LED被使用,返回忙*/
}
......
return0;
}
4、在释放设备时释放原子变量
/*
*@description:关闭/释放设备
*@param-filp:要关闭的设备文件(文件描述符)
*@return:0成功;其他失败
*/
staticintchrdevbase_release(structinode*inode,structfile*filp)
{
structnewchr_dev*dev=filp->private_data;
/*关闭驱动文件的时候释放原子变量*/
atomic_inc(&dev->lock);
......
return0;
}
| 自旋锁
驱动源码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*添加头文件*/
#include
#include
#include
#include
#defineCHRDEVBASE_CNT1/*设备号个数*/
#defineCHRDEVBASE_NAME"chrdevbase"/*名字*/
#defineLEDOFF0/*关灯*/
#defineLEDON1/*开灯*/
/*chrdevbase设备结构体*/
structnewchr_dev{
dev_tdevid;/*设备号*/
structcdevcdev;/*cdev*/
structclass*class;/*类*/
structdevice*device;/*设备*/
intmajor;/*主设备号*/
intminor;/*次设备号*/
structdevice_node*nd;/*设备节点*/
intled_gpio;/*led所使用的GPIO编号*/
intdev_stats;/*设备状态,0,设备未使用;>0,设备已经被使用*/
spinlock_tlock;/*自旋锁*/
};
structnewchr_devchrdevbase;/*自定义字符设备*/
/*
*@description:LED硬件初始化
*@param:无
*@return:无
*/
staticintled_hal_init(void)
{
intret=0;
/*设置LED所使用的GPIO*/
/* 1、获取设备节点:gpioled */
chrdevbase.nd=of_find_node_by_path("/gpioled");
if(chrdevbase.nd==NULL){
printk("chrdevbasenodecantnotfound!
");
return-EINVAL;
}else{
printk("chrdevbasenodehasbeenfound!
");
}
/*2、获取设备树中的gpio属性,得到LED所使用的LED编号*/
chrdevbase.led_gpio=of_get_named_gpio(chrdevbase.nd,"led-gpio",0);
if(chrdevbase.led_gpio< 0){
printk("can'tgetled-gpio");
return-EINVAL;
}
printk("led-gpionum=%d
",chrdevbase.led_gpio);
/*3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯*/
ret=gpio_direction_output(chrdevbase.led_gpio,1);
if(ret< 0){
printk("can'tsetgpio!
");
}
return0;
}
/*
*@description:打开设备
*@param-inode:传递给驱动的inode
*@param-filp:设备文件,file结构体有个叫做private_data的成员变量
*一般在open的时候将private_data指向设备结构体。
*@return:0成功;其他失败
*/
staticintchrdevbase_open(structinode*inode,structfile*filp)
{
unsignedlongflags;
filp->private_data=&chrdevbase;/*设置私有数据*/
spin_lock_irqsave(&chrdevbase.lock,flags);/*上锁*/
if(chrdevbase.dev_stats){/*如果设备被使用了*/
spin_unlock_irqrestore(&chrdevbase.lock,flags);/*解锁*/
return-EBUSY;
}
chrdevbase.dev_stats++;/*如果设备没有打开,那么就标记已经打开了*/
spin_unlock_irqrestore(&chrdevbase.lock,flags);/*解锁*/
printk("[BSP]chrdevbaseopen!
");
return0;
}
/*
*@description:从设备读取数据
*@param-filp:要打开的设备文件(文件描述符)
*@param-buf:返回给用户空间的数据缓冲区
*@param-cnt:要读取的数据长度
*@param-offt:相对于文件首地址的偏移
*@return:读取的字节数,如果为负值,表示读取失败
*/
staticssize_tchrdevbase_read(structfile*filp,char__user*buf,size_tcnt,loff_t*offt)
{
printk("chrdevbaseread!
");
return0;
}
/*
*@description:向设备写数据
*@param-filp:设备文件,表示打开的文件描述符
*@param-buf:要写给设备写入的数据
*@param-cnt:要写入的数据长度
*@param-offt:相对于文件首地址的偏移
*@return:写入的字节数,如果为负值,表示写入失败
*/
staticssize_tchrdevbase_write(structfile*filp,constchar__user*buf,size_tcnt,loff_t*offt)
{
intretvalue=0;
charwritebuf[1];
structnewchr_dev*dev=filp->private_data;
/*接收用户空间传递给内核的数据并且打印出来*/
retvalue=copy_from_user(writebuf,buf,cnt);
printk("[BSP]kernelrecevdatadata:%d!
",writebuf[0]);
if(writebuf[0]==LEDON){
gpio_set_value(dev->led_gpio,0);/*打开LED灯*/
}elseif(writebuf[0]==LEDOFF){
gpio_set_value(dev->led_gpio,1);/*关闭LED灯*/
}
//printk("chrdevbasewrite!
");
return0;
}
/*
*@description:关闭/释放设备
*@param-filp:要关闭的设备文件(文件描述符)
*@return:0成功;其他失败
*/
staticintchrdevbase_release(structinode*inode,structfile*filp)
{
unsignedlongflags;
structnewchr_dev*dev=filp->private_data;
/*关闭驱动文件的时候将dev_stats减1*/
spin_lock_irqsave(&dev->lock,flags);/*上锁*/
if(dev->dev_stats){
dev->dev_stats--;
}
spin_unlock_irqrestore(&dev->lock,flags);/*解锁*/
printk("[BSP]release!
");
return0;
}
/*
*设备操作函数结构体
*/
staticstructfile_operationschrdevbase_fops={
.owner=THIS_MODULE,
.open=chrdevbase_open,
.read=chrdevbase_read,
.write=chrdevbase_write,
.release=chrdevbase_release,
};
/*
*@description:驱动入口函数
*@param:无
*@return:0成功;其他失败
*/
staticint__initchrdevbase_init(void)
{
/*初始化自旋锁*/
spin_lock_init(&chrdevbase.lock);
/*初始化硬件*/
led_hal_init();
/*注册字符设备驱动*/
/*1、创建设备号*/
if(chrdevbase.major){/*定义了设备号*/
chrdevbase.devid=MKDEV(chrdevbase.major,0);
register_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT,CHRDEVBASE_NAME);
}else{/*没有定义设备号*/
alloc_chrdev_region(&chrdevbase.devid,0,CHRDEVBASE_CNT,CHRDEVBASE_NAME);/*申请设备号*/
chrdevbase.major=MAJOR(chrdevbase.devid);/*获取主设备号*/
chrdevbase.minor=MINOR(chrdevbase.devid);/*获取次设备号*/
}
printk("newcheledmajor=%d,minor=%d
",chrdevbase.major,chrdevbase.minor);
/*2、初始化cdev*/
chrdevbase.cdev.owner=THIS_MODULE;
cdev_init(&chrdevbase.cdev,&chrdevbase_fops);
/*3、添加一个cdev*/
cdev_add(&chrdevbase.cdev,chrdevbase.devid,CHRDEVBASE_CNT);
/*4、创建类*/
chrdevbase.class=class_create(THIS_MODULE,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.class)){
returnPTR_ERR(chrdevbase.class);
}
/*5、创建设备*/
chrdevbase.device=device_create(chrdevbase.class,NULL,chrdevbase.devid,NULL,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.device)){
returnPTR_ERR(chrdevbase.device);
}
return0;
}
/*
*@description:驱动出口函数
*@param:无
*@return:无
*/
staticvoid__exitchrdevbase_exit(void)
{
/*注销字符设备*/
cdev_del(&chrdevbase.cdev);/*删除cdev*/
unregister_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT);/*注销设备号*/
device_destroy(chrdevbase.class,chrdevbase.devid);/*销毁设备*/
class_destroy(chrdevbase.class);/*销毁类*/
printk("[BSP]chrdevbaseexit!
");
}
/*
*将上面两个函数指定为驱动的入口和出口函数
*/
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
/*
*LICENSE和作者信息
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");
应用源码
和原子操作应用源码一样,不需要修改!
实验现象
套路分析
自旋锁和RTOS中的临界保护有点类似,套路分析如下:
1、在结构体加入自旋锁变量和一个状态变量
/*chrdevbase设备结构体*/
structnewchr_dev{
......
intdev_stats;/*设备状态,0,设备未使用;>0,设备已经被使用*/
spinlock_tlock;/*自旋锁*/
};
2、在驱动入口初始化自旋锁
/*
*@description:驱动入口函数
*@param:无
*@return:0成功;其他失败
*/
staticint__initchrdevbase_init(void)
{
/*初始化自旋锁*/
spin_lock_init(&chrdevbase.lock);
......
return0;
}
3、在打开设备时判断设备是否被使用
/*
*@description:打开设备
*@param-inode:传递给驱动的inode
*@param-filp:设备文件,file结构体有个叫做private_data的成员变量
*一般在open的时候将private_data指向设备结构体。
*@return:0成功;其他失败
*/
staticintchrdevbase_open(structinode*inode,structfile*filp)
{
unsignedlongflags;
filp->private_data=&chrdevbase;/*设置私有数据*/
spin_lock_irqsave(&chrdevbase.lock,flags);/*上锁*/
if(chrdevbase.dev_stats){/*如果设备被使用了*/
spin_unlock_irqrestore(&chrdevbase.lock,flags);/*解锁*/
return-EBUSY;
}
chrdevbase.dev_stats++;/*如果设备没有打开,那么就标记已经打开了*/
spin_unlock_irqrestore(&chrdevbase.lock,flags);/*解锁*/
......
return0;
}
4、在释放设备时对状态变量自减
/*
*@description:关闭/释放设备
*@param-filp:要关闭的设备文件(文件描述符)
*@return:0成功;其他失败
*/
staticintchrdevbase_release(structinode*inode,structfile*filp)
{
unsignedlongflags;
structnewchr_dev*dev=filp->private_data;
/*关闭驱动文件的时候将dev_stats减1*/
spin_lock_irqsave(&dev->lock,flags);/*上锁*/
if(dev->dev_stats){
dev->dev_stats--;
}
spin_unlock_irqrestore(&dev->lock,flags);/*解锁*/
printk("[BSP]release!
");
return0;
}
| 信号量
驱动源码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*添加头文件*/
#include
#include
#include
#include
#defineCHRDEVBASE_CNT1/*设备号个数*/
#defineCHRDEVBASE_NAME"chrdevbase"/*名字*/
#defineLEDOFF0/*关灯*/
#defineLEDON1/*开灯*/
/*chrdevbase设备结构体*/
structnewchr_dev{
dev_tdevid;/*设备号*/
structcdevcdev;/*cdev*/
structclass*class;/*类*/
structdevice*device;/*设备*/
intmajor;/*主设备号*/
intminor;/*次设备号*/
structdevice_node*nd;/*设备节点*/
intled_gpio;/*led所使用的GPIO编号*/
structsemaphoresem;/*信号量*/
};
structnewchr_devchrdevbase;/*自定义字符设备*/
/*
*@description:LED硬件初始化
*@param:无
*@return:无
*/
staticintled_hal_init(void)
{
intret=0;
/*设置LED所使用的GPIO*/
/* 1、获取设备节点:gpioled */
chrdevbase.nd=of_find_node_by_path("/gpioled");
if(chrdevbase.nd==NULL){
printk("chrdevbasenodecantnotfound!
");
return-EINVAL;
}else{
printk("chrdevbasenodehasbeenfound!
");
}
/*2、获取设备树中的gpio属性,得到LED所使用的LED编号*/
chrdevbase.led_gpio=of_get_named_gpio(chrdevbase.nd,"led-gpio",0);
if(chrdevbase.led_gpio< 0){
printk("can'tgetled-gpio");
return-EINVAL;
}
printk("led-gpionum=%d
",chrdevbase.led_gpio);
/*3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯*/
ret=gpio_direction_output(chrdevbase.led_gpio,1);
if(ret< 0){
printk("can'tsetgpio!
");
}
return0;
}
/*
*@description:打开设备
*@param-inode:传递给驱动的inode
*@param-filp:设备文件,file结构体有个叫做private_data的成员变量
*一般在open的时候将private_data指向设备结构体。
*@return:0成功;其他失败
*/
staticintchrdevbase_open(structinode*inode,structfile*filp)
{
printk("[BSP]chrdevbaseopen!
");
filp->private_data=&chrdevbase;/*设置私有数据*/
/*获取信号量,进入休眠状态的进程可以被信号打断*/
if(down_interruptible(&chrdevbase.sem)){
return-ERESTARTSYS;
}
#if0
down(&chrdevbase.sem);/*不能被信号打断*/
#endif
return0;
}
/*
*@description:从设备读取数据
*@param-filp:要打开的设备文件(文件描述符)
*@param-buf:返回给用户空间的数据缓冲区
*@param-cnt:要读取的数据长度
*@param-offt:相对于文件首地址的偏移
*@return:读取的字节数,如果为负值,表示读取失败
*/
staticssize_tchrdevbase_read(structfile*filp,char__user*buf,size_tcnt,loff_t*offt)
{
printk("chrdevbaseread!
");
return0;
}
/*
*@description:向设备写数据
*@param-filp:设备文件,表示打开的文件描述符
*@param-buf:要写给设备写入的数据
*@param-cnt:要写入的数据长度
*@param-offt:相对于文件首地址的偏移
*@return:写入的字节数,如果为负值,表示写入失败
*/
staticssize_tchrdevbase_write(structfile*filp,constchar__user*buf,size_tcnt,loff_t*offt)
{
intretvalue=0;
charwritebuf[1];
structnewchr_dev*dev=filp->private_data;
/*接收用户空间传递给内核的数据并且打印出来*/
retvalue=copy_from_user(writebuf,buf,cnt);
printk("[BSP]kernelrecevdatadata:%d!
",writebuf[0]);
if(writebuf[0]==LEDON){
gpio_set_value(dev->led_gpio,0);/*打开LED灯*/
}elseif(writebuf[0]==LEDOFF){
gpio_set_value(dev->led_gpio,1);/*关闭LED灯*/
}
//printk("chrdevbasewrite!
");
return0;
}
/*
*@description:关闭/释放设备
*@param-filp:要关闭的设备文件(文件描述符)
*@return:0成功;其他失败
*/
staticintchrdevbase_release(structinode*inode,structfile*filp)
{
structnewchr_dev*dev=filp->private_data;
up(&dev->sem);/*释放信号量,信号量值加1*/
printk("[BSP]release!
");
return0;
}
/*
*设备操作函数结构体
*/
staticstructfile_operationschrdevbase_fops={
.owner=THIS_MODULE,
.open=chrdevbase_open,
.read=chrdevbase_read,
.write=chrdevbase_write,
.release=chrdevbase_release,
};
/*
*@description:驱动入口函数
*@param:无
*@return:0成功;其他失败
*/
staticint__initchrdevbase_init(void)
{
/*初始化信号量*/
sema_init(&chrdevbase.sem,1);
/*初始化硬件*/
led_hal_init();
/*注册字符设备驱动*/
/*1、创建设备号*/
if(chrdevbase.major){/*定义了设备号*/
chrdevbase.devid=MKDEV(chrdevbase.major,0);
register_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT,CHRDEVBASE_NAME);
}else{/*没有定义设备号*/
alloc_chrdev_region(&chrdevbase.devid,0,CHRDEVBASE_CNT,CHRDEVBASE_NAME);/*申请设备号*/
chrdevbase.major=MAJOR(chrdevbase.devid);/*获取主设备号*/
chrdevbase.minor=MINOR(chrdevbase.devid);/*获取次设备号*/
}
printk("newcheledmajor=%d,minor=%d
",chrdevbase.major,chrdevbase.minor);
/*2、初始化cdev*/
chrdevbase.cdev.owner=THIS_MODULE;
cdev_init(&chrdevbase.cdev,&chrdevbase_fops);
/*3、添加一个cdev*/
cdev_add(&chrdevbase.cdev,chrdevbase.devid,CHRDEVBASE_CNT);
/*4、创建类*/
chrdevbase.class=class_create(THIS_MODULE,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.class)){
returnPTR_ERR(chrdevbase.class);
}
/*5、创建设备*/
chrdevbase.device=device_create(chrdevbase.class,NULL,chrdevbase.devid,NULL,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.device)){
returnPTR_ERR(chrdevbase.device);
}
return0;
}
/*
*@description:驱动出口函数
*@param:无
*@return:无
*/
staticvoid__exitchrdevbase_exit(void)
{
/*注销字符设备*/
cdev_del(&chrdevbase.cdev);/*删除cdev*/
unregister_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT);/*注销设备号*/
device_destroy(chrdevbase.class,chrdevbase.devid);/*销毁设备*/
class_destroy(chrdevbase.class);/*销毁类*/
printk("[BSP]chrdevbaseexit!
");
}
/*
*将上面两个函数指定为驱动的入口和出口函数
*/
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
/*
*LICENSE和作者信息
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");
应用源码
和原子操作应用源码一样,不需要修改!
实验现象
使用信号量不会出现设备打不开的问题,它会在任务结束后再次执行!
套路分析
1、在结构体加入信号量
/*chrdevbase设备结构体*/
structnewchr_dev{
......
structsemaphoresem;/*信号量*/
};
2、在驱动入口初始信号量
/*
*@description:驱动入口函数
*@param:无
*@return:0成功;其他失败
*/
staticint__initchrdevbase_init(void)
{
/*初始化信号量*/
sema_init(&chrdevbase.sem,1);
......
return0;
3、在打开设备时获取信号量
/*
*@description:打开设备
*@param-inode:传递给驱动的inode
*@param-filp:设备文件,file结构体有个叫做private_data的成员变量
*一般在open的时候将private_data指向设备结构体。
*@return:0成功;其他失败
*/
staticintchrdevbase_open(structinode*inode,structfile*filp)
{
printk("[BSP]chrdevbaseopen!
");
filp->private_data=&chrdevbase;/*设置私有数据*/
/*获取信号量,进入休眠状态的进程可以被信号打断*/
if(down_interruptible(&chrdevbase.sem)){
return-ERESTARTSYS;
}
#if0
down(&chrdevbase.sem);/*不能被信号打断*/
#endif
return0;
}
4、在释放设备时释放信号量
/*
*@description:关闭/释放设备
*@param-filp:要关闭的设备文件(文件描述符)
*@return:0成功;其他失败
*/
staticintchrdevbase_release(structinode*inode,structfile*filp)
{
structnewchr_dev*dev=filp->private_data;
up(&dev->sem);/*释放信号量,信号量值加1*/
printk("[BSP]release!
");
return0;
}
| 互斥体
驱动源码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*添加头文件*/
#include
#include
#include
#include
#defineCHRDEVBASE_CNT1/*设备号个数*/
#defineCHRDEVBASE_NAME"chrdevbase"/*名字*/
#defineLEDOFF0/*关灯*/
#defineLEDON1/*开灯*/
/*chrdevbase设备结构体*/
structnewchr_dev{
dev_tdevid;/*设备号*/
structcdevcdev;/*cdev*/
structclass*class;/*类*/
structdevice*device;/*设备*/
intmajor;/*主设备号*/
intminor;/*次设备号*/
structdevice_node*nd;/*设备节点*/
intled_gpio;/*led所使用的GPIO编号*/
structmutexlock;/*互斥体*/
};
structnewchr_devchrdevbase;/*自定义字符设备*/
/*
*@description:LED硬件初始化
*@param:无
*@return:无
*/
staticintled_hal_init(void)
{
intret=0;
/*设置LED所使用的GPIO*/
/*1、获取设备节点:gpioled*/
chrdevbase.nd=of_find_node_by_path("/gpioled");
if(chrdevbase.nd==NULL){
printk("chrdevbasenodecantnotfound!
");
return-EINVAL;
}else{
printk("chrdevbasenodehasbeenfound!
");
}
/*2、获取设备树中的gpio属性,得到LED所使用的LED编号*/
chrdevbase.led_gpio=of_get_named_gpio(chrdevbase.nd,"led-gpio",0);
if(chrdevbase.led_gpio< 0){
printk("can'tgetled-gpio");
return-EINVAL;
}
printk("led-gpionum=%d
",chrdevbase.led_gpio);
/*3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯*/
ret=gpio_direction_output(chrdevbase.led_gpio,1);
if(ret< 0){
printk("can'tsetgpio!
");
}
return0;
}
/*
*@description:打开设备
*@param-inode:传递给驱动的inode
*@param-filp:设备文件,file结构体有个叫做private_data的成员变量
*一般在open的时候将private_data指向设备结构体。
*@return:0成功;其他失败
*/
staticintchrdevbase_open(structinode*inode,structfile*filp)
{
printk("[BSP]chrdevbaseopen!
");
filp->private_data=&chrdevbase;/*设置私有数据*/
/*获取互斥体,可以被信号打断*/
if(mutex_lock_interruptible(&chrdevbase.lock)){
return-ERESTARTSYS;
}
#if0
mutex_lock(&chrdevbase.lock);/*不能被信号打断*/
#endif
return0;
}
/*
*@description:从设备读取数据
*@param-filp:要打开的设备文件(文件描述符)
*@param-buf:返回给用户空间的数据缓冲区
*@param-cnt:要读取的数据长度
*@param-offt:相对于文件首地址的偏移
*@return:读取的字节数,如果为负值,表示读取失败
*/
staticssize_tchrdevbase_read(structfile*filp,char__user*buf,size_tcnt,loff_t*offt)
{
printk("chrdevbaseread!
");
return0;
}
/*
*@description:向设备写数据
*@param-filp:设备文件,表示打开的文件描述符
*@param-buf:要写给设备写入的数据
*@param-cnt:要写入的数据长度
*@param-offt:相对于文件首地址的偏移
*@return:写入的字节数,如果为负值,表示写入失败
*/
staticssize_tchrdevbase_write(structfile*filp,constchar__user*buf,size_tcnt,loff_t*offt)
{
intretvalue=0;
charwritebuf[1];
structnewchr_dev*dev=filp->private_data;
/*接收用户空间传递给内核的数据并且打印出来*/
retvalue=copy_from_user(writebuf,buf,cnt);
printk("[BSP]kernelrecevdatadata:%d!
",writebuf[0]);
if(writebuf[0]==LEDON){
gpio_set_value(dev->led_gpio,0);/*打开LED灯*/
}elseif(writebuf[0]==LEDOFF){
gpio_set_value(dev->led_gpio,1);/*关闭LED灯*/
}
//printk("chrdevbasewrite!
");
return0;
}
/*
*@description:关闭/释放设备
*@param-filp:要关闭的设备文件(文件描述符)
*@return:0成功;其他失败
*/
staticintchrdevbase_release(structinode*inode,structfile*filp)
{
structnewchr_dev*dev=filp->private_data;
/*释放互斥锁*/
mutex_unlock(&dev->lock);
printk("[BSP]release!
");
return0;
}
/*
*设备操作函数结构体
*/
staticstructfile_operationschrdevbase_fops={
.owner=THIS_MODULE,
.open=chrdevbase_open,
.read=chrdevbase_read,
.write=chrdevbase_write,
.release=chrdevbase_release,
};
/*
*@description:驱动入口函数
*@param:无
*@return:0成功;其他失败
*/
staticint__initchrdevbase_init(void)
{
/*初始化互斥体*/
mutex_init(&chrdevbase.lock);
/*初始化硬件*/
led_hal_init();
/*注册字符设备驱动*/
/*1、创建设备号*/
if(chrdevbase.major){/*定义了设备号*/
chrdevbase.devid=MKDEV(chrdevbase.major,0);
register_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT,CHRDEVBASE_NAME);
}else{/*没有定义设备号*/
alloc_chrdev_region(&chrdevbase.devid,0,CHRDEVBASE_CNT,CHRDEVBASE_NAME);/*申请设备号*/
chrdevbase.major=MAJOR(chrdevbase.devid);/*获取主设备号*/
chrdevbase.minor=MINOR(chrdevbase.devid);/*获取次设备号*/
}
printk("newcheledmajor=%d,minor=%d
",chrdevbase.major,chrdevbase.minor);
/*2、初始化cdev*/
chrdevbase.cdev.owner=THIS_MODULE;
cdev_init(&chrdevbase.cdev,&chrdevbase_fops);
/*3、添加一个cdev*/
cdev_add(&chrdevbase.cdev,chrdevbase.devid,CHRDEVBASE_CNT);
/*4、创建类*/
chrdevbase.class=class_create(THIS_MODULE,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.class)){
returnPTR_ERR(chrdevbase.class);
}
/*5、创建设备*/
chrdevbase.device=device_create(chrdevbase.class,NULL,chrdevbase.devid,NULL,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.device)){
returnPTR_ERR(chrdevbase.device);
}
return0;
}
/*
*@description:驱动出口函数
*@param:无
*@return:无
*/
staticvoid__exitchrdevbase_exit(void)
{
/*注销字符设备*/
cdev_del(&chrdevbase.cdev);/*删除cdev*/
unregister_chrdev_region(chrdevbase.devid,CHRDEVBASE_CNT);/*注销设备号*/
device_destroy(chrdevbase.class,chrdevbase.devid);/*销毁设备*/
class_destroy(chrdevbase.class);/*销毁类*/
printk("[BSP]chrdevbaseexit!
");
}
/*
*将上面两个函数指定为驱动的入口和出口函数
*/
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
/*
*LICENSE和作者信息
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");
应用源码
和原子操作应用源码一样,不需要修改!
实验现象
互斥体和信号量的效果类似,也会在任务完成后再运行。
套路分析
1、在结构体加入互斥体
/*chrdevbase设备结构体*/
structnewchr_dev{
....
structmutexlock;/*互斥体*/
};
2、在驱动入口初始化互斥体
/*
*@description:驱动入口函数
*@param:无
*@return:0成功;其他失败
*/
staticint__initchrdevbase_init(void)
{
/*初始化互斥体*/
mutex_init(&chrdevbase.lock);
......
return0;
}
3、在打开设备时获取互斥体
/*
*@description:打开设备
*@param-inode:传递给驱动的inode
*@param-filp:设备文件,file结构体有个叫做private_data的成员变量
*一般在open的时候将private_data指向设备结构体。
*@return:0成功;其他失败
*/
staticintchrdevbase_open(structinode*inode,structfile*filp)
{
printk("[BSP]chrdevbaseopen!
");
filp->private_data=&chrdevbase;/*设置私有数据*/
/*获取互斥体,可以被信号打断*/
if(mutex_lock_interruptible(&chrdevbase.lock)){
return-ERESTARTSYS;
}
#if0
mutex_lock(&chrdevbase.lock);/*不能被信号打断*/
#endif
return0;
}
4、在释放设备时释放互斥体
/*
*@description:关闭/释放设备
*@param-filp:要关闭的设备文件(文件描述符)
*@return:0成功;其他失败
*/
staticintchrdevbase_release(structinode*inode,structfile*filp)
{
structnewchr_dev*dev=filp->private_data;
/*释放互斥锁*/
mutex_unlock(&dev->lock);
printk("[BSP]release!
");
return0;
}
-
驱动
+关注
关注
12文章
1824浏览量
85169 -
控制台
+关注
关注
0文章
85浏览量
10336 -
源码
+关注
关注
8文章
633浏览量
29134
原文标题:i.MX6ULL|并发与竞争实验
文章出处:【微信号:玩转单片机,微信公众号:玩转单片机】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论