盛泽做网站,网站的要素是什么,wordpress 底部导航,黄山旅游攻略景点必去在上面两个章节对自旋锁和自旋锁死锁进行了学习#xff0c;自旋锁会让请求的任务原地“自旋”#xff0c;在等待的过程中会循环检测自旋锁的状态#xff0c;进而占用系统资源#xff0c;而本章节要讲解的信号量也是解决竞争的一种常用方法#xff0c;与自旋锁不同的是自旋锁会让请求的任务原地“自旋”在等待的过程中会循环检测自旋锁的状态进而占用系统资源而本章节要讲解的信号量也是解决竞争的一种常用方法与自旋锁不同的是信号量会使等待的线程进入休眠状态适用于那些占用资源比较久的场合。下面对信号量相关知识的进行讲解。
23.1 信号量
信号量是操作系统中最典型的用于同步和互斥的手段本质上是一个全局变量信号量的值表示控制访问资源的线程数可以根据实际情况来自行设置如果在初始化的时候将信号量量值设置为大于1那么这个信号量就是计数型信号量允许多个线程同时访问共享资源。如果将信号量量值设置为1那么这个信号量就是二值信号量同一时间内只允许一个线程访问共享资源注意信号量的值不能小于0。当信号量的值为0时想访问共享资源的线程必须等待直到信号量大于0时等待的线程才可以访问。当访问共享资源时信号量执行“减一”操作访问完成后再执行“加一”操作。
相比于自旋锁信号量具有休眠特性因此适用长时间占用资源的场合但由于信号量会引起休眠所以不能用在中断函数中最后如果共享资源的持有时间比较短使用信号量的话会造成频繁的休眠反而带来更多资源的消耗使用自旋锁反而效果更好。再同时使用信号量和自旋锁的时候要先获取信号量再使用自旋锁因为信号量会导致睡眠。
以现实生活中的银行办理业务为例银行的业务办理窗口就是共享资源业务办理窗口的数量就是信号量量值进入银行之后客户需要领取相应的排序码然后在休息区进行等待可以看作线程的睡眠阶段当前面的客户办理完业务之后相应的窗口会空闲出来可以看作信号量的释放之后银行会通过广播提醒下一位客户到指定的窗口进行业务的办理可以看作线程的唤醒并获取到信号量访问共享资源的过程。
Linux 内核使用semaphore结构体来表示信号量该结构体定义在“内核源码/include/linux/semaphore.h”文件内所以在下一章节的信号量实验中需要加入该头文件结构体内容如下所示 struct semaphore {raw_spinlock_t lock;unsigned int count;struct list_head wait_list;};与信号量相关的 API 函数同样定义在semaphore.h文件内部分常用API函数如下表23-1所示
函数描述DEFINE_SEAMPHORE(name)定义信号量并且设置信号量的值为 1。void sema_init(struct semaphore *sem, int val)初始化信号量 sem设置信号量值为 val。void down(struct semaphore *sem)获取信号量不能被中断打断如ctrlcint down_interruptible(struct semaphore *sem)获取信号量可以被中断打断如ctrlcvoid up(struct semaphore *sem)释放信号量int down_trylock(struct semaphore *sem);尝试获取信号量如果能获取到信号量就获取并且返回 0。如果不能就返回非 0
表 23-1
至此关于信号量相关的知识就讲解完成了上述API函数会在下一小节的实验中用到。
23.2 实验程序的编写
23.2.1 驱动程序编写
本实验对应的网盘路径为iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\18\module。
与之前章节设置标志位在同一时间内只允许一个任务对共享资源进行访问的方式所不同本小节将采用信号量的方式避免竞争的产生。本实验设置的信号量量值为1所以需要在open()函数中加入信号量获取函数在release()函数中加入信号量释放函数即可。
编写完成的semaphore.c代码如下所示
#include linux/init.h
#include linux/module.h
#include linux/fs.h
#include linux/cdev.h
#include linux/kdev_t.h
#include linux/uaccess.h
#include linux/delay.h
#include linux/semaphore.hstruct semaphore semaphore_test;//定义一个semaphore类型的结构体变量semaphore_test
static int open_test(struct inode *inode,struct file *file)
{printk(\nthis is open_test \n);
down(semaphore_test);//信号量数量减1return 0;
}static ssize_t read_test(struct file *file,char __user *ubuf,size_t len,loff_t *off)
{int ret;char kbuf[10] topeet;//定义char类型字符串变量kbufprintk(\nthis is read_test \n);ret copy_to_user(ubuf,kbuf,strlen(kbuf));//使用copy_to_user接收用户空间传递的数据if (ret ! 0){printk(copy_to_user is error \n);}printk(copy_to_user is ok \n);return 0;
}
static char kbuf[10] {0};//定义char类型字符串全局变量kbuf
static ssize_t write_test(struct file *file,const char __user *ubuf,size_t len,loff_t *off)
{int ret;ret copy_from_user(kbuf,ubuf,len);//使用copy_from_user接收用户空间传递的数据if (ret ! 0){printk(copy_from_user is error\n);
}if(strcmp(kbuf,topeet) 0 ){//如果传递的kbuf是topeet就睡眠四秒钟ssleep(4);}else if(strcmp(kbuf,itop) 0){//如果传递的kbuf是itop就睡眠两秒钟ssleep(2);}printk(copy_from_user buf is %s \n,kbuf);return 0;
}
static int release_test(struct inode *inode,struct file *file)
{
up(semaphore_test);//信号量数量加1printk(\nthis is release_test \n);return 0;
}struct chrdev_test {dev_t dev_num;//定义dev_t类型变量dev_num来表示设备号int major,minor;//定义int类型的主设备号major和次设备号minorstruct cdev cdev_test;//定义struct cdev 类型结构体变量cdev_test表示要注册的字符设备struct class *class_test;//定于struct class *类型结构体变量class_test表示要创建的类
};
struct chrdev_test dev1;//创建chrdev_test类型的
struct file_operations fops_test {.owner THIS_MODULE,//将owner字段指向本模块可以避免在模块的操作正在被使用时卸载该模块.open open_test,//将open字段指向open_test(...)函数.read read_test,//将read字段指向read_test(...)函数.write write_test,//将write字段指向write_test(...)函数.release release_test,//将release字段指向release_test(...)函数
};static int __init atomic_init(void)
{sema_init(semaphore_test,1);//初始化信号量结构体semaphore_test并设置信号量的数量为1if(alloc_chrdev_region(dev1.dev_num,0,1,chrdev_name) 0 ){//自动获取设备号设备名chrdev_nameprintk(alloc_chrdev_region is error \n);}printk(alloc_chrdev_region is ok \n);dev1.major MAJOR(dev1.dev_num);//使用MAJOR()函数获取主设备号dev1.minor MINOR(dev1.dev_num);//使用MINOR()函数获取次设备号printk(major is %d,minor is %d\n,dev1.major,dev1.minor);cdev_init(dev1.cdev_test,fops_test);//使用cdev_init()函数初始化cdev_test结构体并链接到fops_test结构体dev1.cdev_test.owner THIS_MODULE;//将owner字段指向本模块可以避免在模块的操作正在被使用时卸载该模块cdev_add(dev1.cdev_test,dev1.dev_num,1);//使用cdev_add()函数进行字符设备的添加dev1.class_test class_create(THIS_MODULE,class_test);//使用class_create进行类的创建类名称为class_testdevice_create(dev1.class_test,0,dev1.dev_num,0,device_test);//使用device_create进行设备的创建设备名称为device_testreturn 0;
}static void __exit atomic_exit(void)
{device_destroy(dev1.class_test,dev1.dev_num);//删除创建的设备class_destroy(dev1.class_test);//删除创建的类cdev_del(dev1.cdev_test);//删除添加的字符设备cdev_testunregister_chrdev_region(dev1.dev_num,1);//释放字符设备所申请的设备号printk(module exit \n);
}
module_init(atomic_init);
module_exit(atomic_exit)
MODULE_LICENSE(GPL v2);
MODULE_AUTHOR(topeet);23.2.2 编写测试 APP
本实验应用程序对应的网盘路径为iTOP-RK3568开发板【底板V1.7版本】\03_【iTOP-RK3568开发板】指南教程\02_Linux驱动配套资料\04_Linux驱动例程\18\app。
本测试app代码和上一章节相同需要输入两个参数第一个参数为对应的设备节点第二个参数为“topeet”或者“itop”分别代表向设备写入的数据编写完成的应用程序app.c内容如下所示
#include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include string.h#include unistd.h
int main(int argc, char *argv[])
{int fd;//定义int类型的文件描述符char str1[10] {0};//定义读取缓冲区str1fd open(argv[1],O_RDWR);//调用open函数打开输入的第一个参数文件权限为可读可写if(fd 0 ){printf(file open failed \n);return -1;}/*如果第二个参数为topeet条件成立调用write函数写入topeet*/ if (strcmp(argv[2],topeet) 0 ){write(fd,topeet,10);}/*如果第二个参数为itop条件成立调用write函数写入itop*/ else if (strcmp(argv[2],itop) 0 ){write(fd,itop,10);}close(fd); return 0;
}23.3 运行测试
23.3.1 编译驱动程序
在上一小节中的semaphore.c代码同一目录下创建 Makefile 文件Makefile 文件内容如下所示
export ARCHarm64#设置平台架构
export CROSS_COMPILEaarch64-linux-gnu-#交叉编译器前缀
obj-m semaphore.o #此处要和你的驱动源文件同名
KDIR :/home/topeet/Linux/linux_sdk/kernel #这里是你的内核目录
PWD ? $(shell pwd)
all:make -C $(KDIR) M$(PWD) modules #make操作
clean:make -C $(KDIR) M$(PWD) clean #make clean操作对于Makefile的内容注释已在上图添加保存退出之后来到存放semaphore.c和Makefile文件目录下如下图图23-2所示 图 23-2
然后使用命令“make”进行驱动的编译编译完成如下图图23-3所示 图 23-3
编译完生成semaphore.ko目标文件如下图图23-4所示 图 23-4
至此驱动模块就编译成功了下面进行应用程序的安装。
23.3.2 编译应用程序
来到应用程序app.c文件的存放路径如下图图 23-5所示 图 23-5
然后使用以下命令对app.c进行交叉编译编译完成如下图图23-6所示
aarch64-linux-gnu-gcc -o app app.c -static图23-6
生成的app文件就是之后放在开发板上运行的可执行文件至此应用程序的编译就完成了。
23.3.3 运行测试
开发板启动之后使用以下命令进行驱动模块的加载如下图图 23-7所示
insmod semaphore.ko图 23-7
可以看到申请的主设备号和次设备号就被打印了出来然后使用以下代码对自动生成的设备节点device_test进行查看如下图图23-8所示 ls /dev/device_test图 23-8
可以看到device_test节点已经被自动创建了然后使用以下命令运行测试app运行结果如下图图23-9所示
./app /dev/device_test topeet图 23-9
可以看到传递的buf值为topeet然后输入以下命令在后台运行两个app来进行竞争测试运行结果如下图图23-10所示
./app /dev/device_test topeet ./app /dev/device_test itop 图 23-10
上述打印信息正常证明数据被正确传递了没有发生共享资源的竞争第一个任务运行之后由于设置的信号量量值为1所以第二个任务会进入休眠状态第一个任务执行完毕之后会唤醒第二个任务去执行所以避免了并发与竞争。
最后可以使用以下命令进行驱动的卸载如下图图 23-11所示
rmmod semaphore.ko图 23-11
至此信号量实验就完成了。
【最新驱动资料文档例程】
链接 https://pan.baidu.com/s/1M4smUG2vw_hnn0Hye-tkog
提取码hbh6
【B 站配套视频】
https://b23.tv/XqYa6Hm
【RK3568 购买链接】
https://item.taobao.com/item.htm?spma1z10.5-c-s.w4002-2245
rmmod semaphore.ko[外链图片转存中…(img-nql99Xd2-1694222692051)]
图 23-11
至此信号量实验就完成了。
【最新驱动资料文档例程】
链接 https://pan.baidu.com/s/1M4smUG2vw_hnn0Hye-tkog
提取码hbh6
【B 站配套视频】
https://b23.tv/XqYa6Hm
【RK3568 购买链接】
https://item.taobao.com/item.htm?spma1z10.5-c-s.w4002-2245
2452613.11.2fec74a6elWNeAid669939423234