1)资料下载:点击资料即可下载
2)对正点原子Linux感兴趣的同学可以加群讨论:935446741
3)关注正点原子公众号,获取最新资料更新
![45380d2803211217e8a5e86f2b61af1d.png](https://img-blog.csdnimg.cn/img_convert/45380d2803211217e8a5e86f2b61af1d.png)
第四十八章Linux并发与竞争实验
在上一章中我们学习了Linux下的并发与竞争,并且学习了四种常用的处理并发和竞争的机制:原子操作、自旋锁、信号量和互斥体。本章我们就通过四个实验来学习如何在驱动中使用这四种机制。
48.1 原子操作实验
本实验对应的例程路径为:开发板光盘->2、Linux驱动例程->7_atomic。
本例程我们在第四十五章的gpioled.c文件基础上完成。在本节使用中我们使用原子操作来实现对LED这个设备的互斥访问,也就是一次只允许一个应用程序可以使用LED灯。
48.1.1 实验程序编写
1、修改设备树文件
因为本章实验是在第四十五章实验的基础上完成的,因此不需要对设备树做任何的修改。
2、LED驱动修改
本节实验在第四十五章实验驱动文件gpioled.c的基础上修改而来。新建名为“7_atomic”的文件夹,然后在7_atomic文件夹里面创建vscode工程,工作区命名为“atomic”。将5_gpioled实验中的gpioled.c复制到7_atomic文件夹中,并且重命名为atomic.c。本节实验重点就是使用atomic来实现一次只能允许一个应用访问LED,所以我们只需要在atomic.c文件源码的基础上加上添加atomic相关代码即可,完成以后的atomic.c文件内容如下所示:
示例代码48.1.1.1 atomic.c文件代码段
1 #include <linux/types.h>
2 #include <linux/kernel.h>
3 #include <linux/delay.h>
4 #include <linux/ide.h>
5 #include <linux/init.h>
6 #include <linux/module.h>
7 #include <linux/errno.h>
8 #include <linux/gpio.h>
9 #include <linux/cdev.h>
10 #include <linux/device.h>
11 #include <linux/of.h>
12 #include <linux/of_address.h>
13 #include <linux/of_gpio.h>
14 #include <asm/mach/map.h>
15 #include <asm/uaccess.h>
16 #include <asm/io.h>
17/***************************************************************
18 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
19文件名 : atomic.c
20作者 : 左忠凯
21版本 : V1.0
22描述 : 原子操作实验,使用原子变量来实现对实现设备的互斥访问
23其他 : 无
24论坛 : http://www.openedv.com
25日志 : 初版V1.0 2019/7/18 左忠凯创建
26 ***************************************************************/
27 #define GPIOLED_CNT 1 /* 设备号个数 */
28 #define GPIOLED_NAME "gpioled" /* 名字 */
29 #define LEDOFF 0 /* 关灯 */
30 #define LEDON 1 /* 开灯 */
31
32/* gpioled设备结构体 */
33struct gpioled_dev{
34 dev_t devid; /* 设备号 */
35struct cdev cdev; /* cdev */
36struct class *class; /* 类 */
37struct device *device; /* 设备 */
38int major; /* 主设备号 */
39int minor; /* 次设备号 */
40struct device_node *nd; /* 设备节点 */
41int led_gpio; /* led所使用的GPIO编号 */
42 atomic_t lock; /* 原子变量 */
43};
44
45struct gpioled_dev gpioled;/* led设备 */
46
47/*
48 * @description : 打开设备
49 * @param – inode : 传递给驱动的inode
50 * @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
51 * 一般在open的时候将private_data指向设备结构体。
52 * @return : 0 成功;其他失败
53 */
54staticint led_open(struct inode *inode,struct file *filp)
55{
56/* 通过判断原子变量的值来检查LED有没有被别的应用使用 */
57if(!atomic_dec_and_test(&gpioled.lock)){
58 atomic_inc(&gpioled.lock);/* 小于0的话就加1,使其原子变量等于0 */
59return-EBUSY;/* LED被使用,返回忙 */
60}
61
62 filp->private_data =&gpioled;/* 设置私有数据 */
63return0;
64}
65
66/*
67 * @description : 从设备读取数据
68 * @param - filp : 要打开的设备文件(文件描述符)
69 * @param - buf : 返回给用户空间的数据缓冲区
70 * @param – cnt : 要读取的数据长度
71 * @param - offt : 相对于文件首地址的偏移
72 * @return : 读取的字节数,如果为负值,表示读取失败
73 */
74static ssize_t led_read(struct file *filp,char __user *buf,
size_t cnt, loff_t *offt)
75{
76return0;
77}
78
79/*
80 * @description : 向设备写数据
81 * @param – filp : 设备文件,表示打开的文件描述符
82 * @param - buf : 要写给设备写入的数据
83 * @param - cnt : 要写入的数据长度
84 * @param – offt : 相对于文件首地址的偏移
85 * @return : 写入的字节数,如果为负值,表示写入失败
86 */
87static ssize_t led_write(struct file *filp,constchar __user *buf,
size_t cnt, loff_t *offt)
88{
89int retvalue;
90unsignedchar databuf[1];
91unsignedchar ledstat;
92struct gpioled_dev *dev = filp->private_data;
93
94 retvalue = copy_from_user(databuf, buf, cnt);
95if(retvalue <0){
96 printk("kernel write failed!rn");
97return-EFAULT;
98}
99
100 ledstat = databuf[0]; /* 获取状态值 */
101
102if(ledstat == LEDON){
103 gpio_set_value(dev->led_gpio,0);/* 打开LED灯 */
104}elseif(ledstat == LEDOFF){
105 gpio_set_value(dev->led_gpio,1);/* 关闭LED灯 */
106}
107return0;
108}
109
110/*
111 * @description : 关闭/释放设备
112 * @param – filp : 要关闭的设备文件(文件描述符)
113 * @return : 0 成功;其他失败
114 */
115staticint led_release(struct inode *inode,struct file *filp)
116{
117struct gpioled_dev *dev = filp->private_data;
118
119/* 关闭驱动文件的时候释放原子变量 */
120 atomic_inc(&dev->lock);
121return0;
122}
123
124/* 设备操作函数 */
125staticstruct file_operations gpioled_fops ={
126.owner = THIS_MODULE,
127.open = led_open,
128.read = led_read,
129.write = led_write,
130.release = led_release,
131};
132
133/*
134 * @description : 驱动入口函数
135 * @param : 无
136 * @return : 无
137 */
138staticint __init led_init(void)
139{
140int ret =0;
141
142/* 初始化原子变量 */
143 atomic_set(&gpioled.lock,1);/* 原子变量初始值为1 */
144
145/* 设置LED所使用的GPIO */
146/* 1、获取设备节点:gpioled */
147 gpioled.nd = of_find_node_by_path("/gpioled");
148if(gpioled.nd ==NULL){
149 printk("gpioled node not find!rn");
150return-EINVAL;
151}else{
152 printk("gpioled node find!rn");
153}
154
155/* 2