做网站公司叫什么,智慧团建在线登录,wordpress不显示模板,网站运营软件【系列文章】Linux中的并发与竞争[04]-信号量
该文章为系列文章#xff1a;Linux中的并发与竞争中的第4篇 该系列的导航页连接#xff1a; 【系列文章】Linux中的并发与竞争-导航页 文章目录【系列文章】Linux中的并发与竞争[04]-信号量一、信号量二、实验程序的编写2.1驱动…【系列文章】Linux中的并发与竞争[04]-信号量
该文章为系列文章Linux中的并发与竞争中的第4篇 该系列的导航页连接 【系列文章】Linux中的并发与竞争-导航页 文章目录【系列文章】Linux中的并发与竞争[04]-信号量一、信号量二、实验程序的编写2.1驱动程序编写2.2编写测试 APP2.3运行测试在上面两篇文章对自旋锁和自旋锁死锁进行了学习自旋锁会让请求的任务原地“自旋”在等待的过程中会循环检测自旋锁的状态进而占用系统资源而本章节要讲解的信号量也是解决竞争的一种常用方法与自旋锁不同的是信号量会使等待的线程进入休眠状态适用于那些占用资源比较久的场合。下面对信号量相关知识的进行讲解。
一、信号量
信号量是操作系统中最典型的用于同步和互斥的手段本质上是一个全局变量信号量的值表示控制访问资源的线程数可以根据实际情况来自行设置如果在初始化的时候将信号量量值设置为大于 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 函数如下
函数描述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
二、实验程序的编写
2.1驱动程序编写
与之前章节设置标志位在同一时间内只允许一个任务对共享资源进行访问的方式所不同本小节将采用信号量的方式避免竞争的产生。本实验设置的信号量量值为 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_teststatic 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 类型字符串全局变量 kbufstatic 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()函数初始化 cdev_test 结构体并链接到fops_test 结构体cdev_init(dev1.cdev_test,fops_test);//将 owner 字段指向本模块可以避免在模块的操作正在被使用时卸载该模块dev1.cdev_test.owner THIS_MODULE;cdev_add(dev1.cdev_test,dev1.dev_num,1);//使用 cdev_add()函数进行字符设备的添加//使用 class_create 进行类的创建类名称为class_testdev1.class_test class_create(THIS_MODULE,class_test);//使用 device_create 进行设备的创建设备名称为 device_testdevice_create(dev1.class_test,0,dev1.dev_num,0,device_test);return 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);2.2编写测试 APP
#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;
}2.3运行测试
使用以下命令运行测试 app
./app /dev/device_test topeet可以看到传递的 buf 值为 topeet然后输入以下命令在后台运行两个 app来进行竞争测试运行结果如下图所示
./app /dev/device_test topeet
./app /dev/device_test itop上述打印信息正常证明数据被正确传递了没有发生共享资源的竞争第一个任务运行之后由于设置的信号量量值为 1所以第二个任务会进入休眠状态第一个任务执行完毕之后会唤醒第二个任务去执行所以避免了并发与竞争。