常用的處理并發(fā)和競爭的機制有四種,原子操作、自旋鎖、信號量和互斥體。下邊就通過編寫驅動來實現(xiàn),展示一下相關效果。當前臺的應用一直運行,控制臺是不能輸入指令,測試并發(fā)與競爭最好是在后臺運行,而解決并非與競爭最直接的手段就是只允許一個應用去調(diào)用相關資源,這里為了好展示效果就通過任務運行來體現(xiàn)。下面的實驗使用了新字符驅動GPIO源碼,只需要復制一份即可使用。
|原子操作
驅動源碼
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*添加頭文件*/
#include
#include
#include
#include
#defineCHRDEVBASE_CNT1/*設備號個數(shù)*/
#defineCHRDEVBASE_NAME"chrdevbase"/*名字*/
#defineLEDOFF0/*關燈*/
#defineLEDON1/*開燈*/
/*chrdevbase設備結構體*/
structnewchr_dev{
dev_tdevid;/*設備號*/
structcdevcdev;/*cdev*/
structclass*class;/*類*/
structdevice*device;/*設備*/
intmajor;/*主設備號*/
intminor;/*次設備號*/
structdevice_node*nd;/*設備節(jié)點*/
intled_gpio;/*led所使用的GPIO編號*/
atomic_tlock;/*原子變量*/
};
structnewchr_devchrdevbase;/*自定義字符設備*/
/*
*@description:LED硬件初始化
*@param:無
*@return:無
*/
staticintled_hal_init(void)
{
intret=0;
/*設置LED所使用的GPIO*/
/* 1、獲取設備節(jié)點: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_gpio0){
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(ret0){
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;/*設置私有數(shù)據(jù)*/
return0;
}
/*
*@description:從設備讀取數(shù)據(jù)
*@param-filp:要打開的設備文件(文件描述符)
*@param-buf:返回給用戶空間的數(shù)據(jù)緩沖區(qū)
*@param-cnt:要讀取的數(shù)據(jù)長度
*@param-offt:相對于文件首地址的偏移
*@return:讀取的字節(jié)數(shù),如果為負值,表示讀取失敗
*/
staticssize_tchrdevbase_read(structfile*filp,char__user*buf,size_tcnt,loff_t*offt)
{
printk("chrdevbaseread!
");
return0;
}
/*
*@description:向設備寫數(shù)據(jù)
*@param-filp:設備文件,表示打開的文件描述符
*@param-buf:要寫給設備寫入的數(shù)據(jù)
*@param-cnt:要寫入的數(shù)據(jù)長度
*@param-offt:相對于文件首地址的偏移
*@return:寫入的字節(jié)數(shù),如果為負值,表示寫入失敗
*/
staticssize_tchrdevbase_write(structfile*filp,constchar__user*buf,size_tcnt,loff_t*offt)
{
intretvalue=0;
charwritebuf[1];
structnewchr_dev*dev=filp->private_data;
/*接收用戶空間傳遞給內(nèi)核的數(shù)據(jù)并且打印出來*/
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;
}
/*
*設備操作函數(shù)結構體
*/
staticstructfile_operationschrdevbase_fops={
.owner=THIS_MODULE,
.open=chrdevbase_open,
.read=chrdevbase_read,
.write=chrdevbase_write,
.release=chrdevbase_release,
};
/*
*@description:驅動入口函數(shù)
*@param:無
*@return:0成功;其他失敗
*/
staticint__initchrdevbase_init(void)
{
/*初始化原子變量*/
atomic_set(&chrdevbase.lock,1);/*原子變量初始值為1*/
/*初始化硬件*/
led_hal_init();
/*注冊字符設備驅動*/
/*1、創(chuàng)建設備號*/
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、創(chuàng)建類*/
chrdevbase.class=class_create(THIS_MODULE,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.class)){
returnPTR_ERR(chrdevbase.class);
}
/*5、創(chuàng)建設備*/
chrdevbase.device=device_create(chrdevbase.class,NULL,chrdevbase.devid,NULL,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.device)){
returnPTR_ERR(chrdevbase.device);
}
return0;
}
/*
*@description:驅動出口函數(shù)
*@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!
");
}
/*
*將上面兩個函數(shù)指定為驅動的入口和出口函數(shù)
*/
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數(shù)組元素個數(shù)
*@param-argv:具體參數(shù)
*@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(fd0){
printf("[APP]Can'topenfile%s
",filename);
return-1;
}
/*把第三個參數(shù)賦值給databuf*/
databuf[0]=atoi(argv[2]);
/*向設備驅動寫數(shù)據(jù)*/
memcpy(writebuf,databuf,sizeof(databuf));
retvalue=write(fd,writebuf,sizeof(databuf));
if(retvalue0){
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(retvalue0){
printf("[APP]Can'tclosefile%s
",filename);
return-1;
}
return0;
}
實驗現(xiàn)象
加了原子操作后,應用程序運行時,再次觸發(fā)是不能運行的,這就解決了在復雜環(huán)境下的并發(fā)和競爭的問題。注意不加“&”表示直接運行,控制臺不能輸入指令,加了“&”表示后臺運行,可以繼續(xù)輸入指令。
套路分析
1、先在結構體定義一個變量
/*chrdevbase設備結構體*/
structnewchr_dev{
......
atomic_tlock;/*原子變量*/
};
2、在驅動入口初始化原子變量
/*
*@description:驅動入口函數(shù)
*@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/*設備號個數(shù)*/
#defineCHRDEVBASE_NAME"chrdevbase"/*名字*/
#defineLEDOFF0/*關燈*/
#defineLEDON1/*開燈*/
/*chrdevbase設備結構體*/
structnewchr_dev{
dev_tdevid;/*設備號*/
structcdevcdev;/*cdev*/
structclass*class;/*類*/
structdevice*device;/*設備*/
intmajor;/*主設備號*/
intminor;/*次設備號*/
structdevice_node*nd;/*設備節(jié)點*/
intled_gpio;/*led所使用的GPIO編號*/
intdev_stats;/*設備狀態(tài),0,設備未使用;>0,設備已經(jīng)被使用*/
spinlock_tlock;/*自旋鎖*/
};
structnewchr_devchrdevbase;/*自定義字符設備*/
/*
*@description:LED硬件初始化
*@param:無
*@return:無
*/
staticintled_hal_init(void)
{
intret=0;
/*設置LED所使用的GPIO*/
/* 1、獲取設備節(jié)點: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_gpio0){
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(ret0){
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;/*設置私有數(shù)據(jù)*/
spin_lock_irqsave(&chrdevbase.lock,flags);/*上鎖*/
if(chrdevbase.dev_stats){/*如果設備被使用了*/
spin_unlock_irqrestore(&chrdevbase.lock,flags);/*解鎖*/
return-EBUSY;
}
chrdevbase.dev_stats++;/*如果設備沒有打開,那么就標記已經(jīng)打開了*/
spin_unlock_irqrestore(&chrdevbase.lock,flags);/*解鎖*/
printk("[BSP]chrdevbaseopen!
");
return0;
}
/*
*@description:從設備讀取數(shù)據(jù)
*@param-filp:要打開的設備文件(文件描述符)
*@param-buf:返回給用戶空間的數(shù)據(jù)緩沖區(qū)
*@param-cnt:要讀取的數(shù)據(jù)長度
*@param-offt:相對于文件首地址的偏移
*@return:讀取的字節(jié)數(shù),如果為負值,表示讀取失敗
*/
staticssize_tchrdevbase_read(structfile*filp,char__user*buf,size_tcnt,loff_t*offt)
{
printk("chrdevbaseread!
");
return0;
}
/*
*@description:向設備寫數(shù)據(jù)
*@param-filp:設備文件,表示打開的文件描述符
*@param-buf:要寫給設備寫入的數(shù)據(jù)
*@param-cnt:要寫入的數(shù)據(jù)長度
*@param-offt:相對于文件首地址的偏移
*@return:寫入的字節(jié)數(shù),如果為負值,表示寫入失敗
*/
staticssize_tchrdevbase_write(structfile*filp,constchar__user*buf,size_tcnt,loff_t*offt)
{
intretvalue=0;
charwritebuf[1];
structnewchr_dev*dev=filp->private_data;
/*接收用戶空間傳遞給內(nèi)核的數(shù)據(jù)并且打印出來*/
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;
}
/*
*設備操作函數(shù)結構體
*/
staticstructfile_operationschrdevbase_fops={
.owner=THIS_MODULE,
.open=chrdevbase_open,
.read=chrdevbase_read,
.write=chrdevbase_write,
.release=chrdevbase_release,
};
/*
*@description:驅動入口函數(shù)
*@param:無
*@return:0成功;其他失敗
*/
staticint__initchrdevbase_init(void)
{
/*初始化自旋鎖*/
spin_lock_init(&chrdevbase.lock);
/*初始化硬件*/
led_hal_init();
/*注冊字符設備驅動*/
/*1、創(chuàng)建設備號*/
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、創(chuàng)建類*/
chrdevbase.class=class_create(THIS_MODULE,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.class)){
returnPTR_ERR(chrdevbase.class);
}
/*5、創(chuàng)建設備*/
chrdevbase.device=device_create(chrdevbase.class,NULL,chrdevbase.devid,NULL,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.device)){
returnPTR_ERR(chrdevbase.device);
}
return0;
}
/*
*@description:驅動出口函數(shù)
*@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!
");
}
/*
*將上面兩個函數(shù)指定為驅動的入口和出口函數(shù)
*/
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
/*
*LICENSE和作者信息
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");
應用源碼
和原子操作應用源碼一樣,不需要修改!
實驗現(xiàn)象
套路分析
自旋鎖和RTOS中的臨界保護有點類似,套路分析如下:
1、在結構體加入自旋鎖變量和一個狀態(tài)變量
/*chrdevbase設備結構體*/
structnewchr_dev{
......
intdev_stats;/*設備狀態(tài),0,設備未使用;>0,設備已經(jīng)被使用*/
spinlock_tlock;/*自旋鎖*/
};
2、在驅動入口初始化自旋鎖
/*
*@description:驅動入口函數(shù)
*@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;/*設置私有數(shù)據(jù)*/
spin_lock_irqsave(&chrdevbase.lock,flags);/*上鎖*/
if(chrdevbase.dev_stats){/*如果設備被使用了*/
spin_unlock_irqrestore(&chrdevbase.lock,flags);/*解鎖*/
return-EBUSY;
}
chrdevbase.dev_stats++;/*如果設備沒有打開,那么就標記已經(jīng)打開了*/
spin_unlock_irqrestore(&chrdevbase.lock,flags);/*解鎖*/
......
return0;
}
4、在釋放設備時對狀態(tài)變量自減
/*
*@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/*設備號個數(shù)*/
#defineCHRDEVBASE_NAME"chrdevbase"/*名字*/
#defineLEDOFF0/*關燈*/
#defineLEDON1/*開燈*/
/*chrdevbase設備結構體*/
structnewchr_dev{
dev_tdevid;/*設備號*/
structcdevcdev;/*cdev*/
structclass*class;/*類*/
structdevice*device;/*設備*/
intmajor;/*主設備號*/
intminor;/*次設備號*/
structdevice_node*nd;/*設備節(jié)點*/
intled_gpio;/*led所使用的GPIO編號*/
structsemaphoresem;/*信號量*/
};
structnewchr_devchrdevbase;/*自定義字符設備*/
/*
*@description:LED硬件初始化
*@param:無
*@return:無
*/
staticintled_hal_init(void)
{
intret=0;
/*設置LED所使用的GPIO*/
/* 1、獲取設備節(jié)點: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_gpio0){
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(ret0){
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;/*設置私有數(shù)據(jù)*/
/*獲取信號量,進入休眠狀態(tài)的進程可以被信號打斷*/
if(down_interruptible(&chrdevbase.sem)){
return-ERESTARTSYS;
}
#if0
down(&chrdevbase.sem);/*不能被信號打斷*/
#endif
return0;
}
/*
*@description:從設備讀取數(shù)據(jù)
*@param-filp:要打開的設備文件(文件描述符)
*@param-buf:返回給用戶空間的數(shù)據(jù)緩沖區(qū)
*@param-cnt:要讀取的數(shù)據(jù)長度
*@param-offt:相對于文件首地址的偏移
*@return:讀取的字節(jié)數(shù),如果為負值,表示讀取失敗
*/
staticssize_tchrdevbase_read(structfile*filp,char__user*buf,size_tcnt,loff_t*offt)
{
printk("chrdevbaseread!
");
return0;
}
/*
*@description:向設備寫數(shù)據(jù)
*@param-filp:設備文件,表示打開的文件描述符
*@param-buf:要寫給設備寫入的數(shù)據(jù)
*@param-cnt:要寫入的數(shù)據(jù)長度
*@param-offt:相對于文件首地址的偏移
*@return:寫入的字節(jié)數(shù),如果為負值,表示寫入失敗
*/
staticssize_tchrdevbase_write(structfile*filp,constchar__user*buf,size_tcnt,loff_t*offt)
{
intretvalue=0;
charwritebuf[1];
structnewchr_dev*dev=filp->private_data;
/*接收用戶空間傳遞給內(nèi)核的數(shù)據(jù)并且打印出來*/
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;
}
/*
*設備操作函數(shù)結構體
*/
staticstructfile_operationschrdevbase_fops={
.owner=THIS_MODULE,
.open=chrdevbase_open,
.read=chrdevbase_read,
.write=chrdevbase_write,
.release=chrdevbase_release,
};
/*
*@description:驅動入口函數(shù)
*@param:無
*@return:0成功;其他失敗
*/
staticint__initchrdevbase_init(void)
{
/*初始化信號量*/
sema_init(&chrdevbase.sem,1);
/*初始化硬件*/
led_hal_init();
/*注冊字符設備驅動*/
/*1、創(chuàng)建設備號*/
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、創(chuàng)建類*/
chrdevbase.class=class_create(THIS_MODULE,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.class)){
returnPTR_ERR(chrdevbase.class);
}
/*5、創(chuàng)建設備*/
chrdevbase.device=device_create(chrdevbase.class,NULL,chrdevbase.devid,NULL,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.device)){
returnPTR_ERR(chrdevbase.device);
}
return0;
}
/*
*@description:驅動出口函數(shù)
*@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!
");
}
/*
*將上面兩個函數(shù)指定為驅動的入口和出口函數(shù)
*/
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
/*
*LICENSE和作者信息
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");
應用源碼
和原子操作應用源碼一樣,不需要修改!
實驗現(xiàn)象
使用信號量不會出現(xiàn)設備打不開的問題,它會在任務結束后再次執(zhí)行!
套路分析
1、在結構體加入信號量
/*chrdevbase設備結構體*/
structnewchr_dev{
......
structsemaphoresem;/*信號量*/
};
2、在驅動入口初始信號量
/*
*@description:驅動入口函數(shù)
*@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;/*設置私有數(shù)據(jù)*/
/*獲取信號量,進入休眠狀態(tài)的進程可以被信號打斷*/
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/*設備號個數(shù)*/
#defineCHRDEVBASE_NAME"chrdevbase"/*名字*/
#defineLEDOFF0/*關燈*/
#defineLEDON1/*開燈*/
/*chrdevbase設備結構體*/
structnewchr_dev{
dev_tdevid;/*設備號*/
structcdevcdev;/*cdev*/
structclass*class;/*類*/
structdevice*device;/*設備*/
intmajor;/*主設備號*/
intminor;/*次設備號*/
structdevice_node*nd;/*設備節(jié)點*/
intled_gpio;/*led所使用的GPIO編號*/
structmutexlock;/*互斥體*/
};
structnewchr_devchrdevbase;/*自定義字符設備*/
/*
*@description:LED硬件初始化
*@param:無
*@return:無
*/
staticintled_hal_init(void)
{
intret=0;
/*設置LED所使用的GPIO*/
/*1、獲取設備節(jié)點: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_gpio0){
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(ret0){
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;/*設置私有數(shù)據(jù)*/
/*獲取互斥體,可以被信號打斷*/
if(mutex_lock_interruptible(&chrdevbase.lock)){
return-ERESTARTSYS;
}
#if0
mutex_lock(&chrdevbase.lock);/*不能被信號打斷*/
#endif
return0;
}
/*
*@description:從設備讀取數(shù)據(jù)
*@param-filp:要打開的設備文件(文件描述符)
*@param-buf:返回給用戶空間的數(shù)據(jù)緩沖區(qū)
*@param-cnt:要讀取的數(shù)據(jù)長度
*@param-offt:相對于文件首地址的偏移
*@return:讀取的字節(jié)數(shù),如果為負值,表示讀取失敗
*/
staticssize_tchrdevbase_read(structfile*filp,char__user*buf,size_tcnt,loff_t*offt)
{
printk("chrdevbaseread!
");
return0;
}
/*
*@description:向設備寫數(shù)據(jù)
*@param-filp:設備文件,表示打開的文件描述符
*@param-buf:要寫給設備寫入的數(shù)據(jù)
*@param-cnt:要寫入的數(shù)據(jù)長度
*@param-offt:相對于文件首地址的偏移
*@return:寫入的字節(jié)數(shù),如果為負值,表示寫入失敗
*/
staticssize_tchrdevbase_write(structfile*filp,constchar__user*buf,size_tcnt,loff_t*offt)
{
intretvalue=0;
charwritebuf[1];
structnewchr_dev*dev=filp->private_data;
/*接收用戶空間傳遞給內(nèi)核的數(shù)據(jù)并且打印出來*/
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;
}
/*
*設備操作函數(shù)結構體
*/
staticstructfile_operationschrdevbase_fops={
.owner=THIS_MODULE,
.open=chrdevbase_open,
.read=chrdevbase_read,
.write=chrdevbase_write,
.release=chrdevbase_release,
};
/*
*@description:驅動入口函數(shù)
*@param:無
*@return:0成功;其他失敗
*/
staticint__initchrdevbase_init(void)
{
/*初始化互斥體*/
mutex_init(&chrdevbase.lock);
/*初始化硬件*/
led_hal_init();
/*注冊字符設備驅動*/
/*1、創(chuàng)建設備號*/
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、創(chuàng)建類*/
chrdevbase.class=class_create(THIS_MODULE,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.class)){
returnPTR_ERR(chrdevbase.class);
}
/*5、創(chuàng)建設備*/
chrdevbase.device=device_create(chrdevbase.class,NULL,chrdevbase.devid,NULL,CHRDEVBASE_NAME);
if(IS_ERR(chrdevbase.device)){
returnPTR_ERR(chrdevbase.device);
}
return0;
}
/*
*@description:驅動出口函數(shù)
*@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!
");
}
/*
*將上面兩個函數(shù)指定為驅動的入口和出口函數(shù)
*/
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
/*
*LICENSE和作者信息
*/
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");
應用源碼
和原子操作應用源碼一樣,不需要修改!
實驗現(xiàn)象
互斥體和信號量的效果類似,也會在任務完成后再運行。
套路分析
1、在結構體加入互斥體
/*chrdevbase設備結構體*/
structnewchr_dev{
....
structmutexlock;/*互斥體*/
};
2、在驅動入口初始化互斥體
/*
*@description:驅動入口函數(shù)
*@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;/*設置私有數(shù)據(jù)*/
/*獲取互斥體,可以被信號打斷*/
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文章
1840瀏覽量
85313 -
控制臺
+關注
關注
0文章
85瀏覽量
10370 -
源碼
+關注
關注
8文章
642瀏覽量
29229
原文標題:i.MX6ULL|并發(fā)與競爭實驗
文章出處:【微信號:玩轉單片機,微信公眾號:玩轉單片機】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論