一. 简介
前面文章学习了Linux内核中处理并发与竞争的一种方法:原子操作,并编写代码说明原子操作中对整型变量的操作,文章地址如下:
Linux内核中并发与竞争的处理方法:原子操作代码举例一-CSDN博客
本文学习原子操作中,针对整型变量的原子操作的另一种代码实现。
实现效果:实现对 LED 这个设备的互斥访问,也就是一次只允许一个应用程序可以使用 LED 灯。
二. 原子操作的另一种代码实现
1. 准备工程代码
这里在前面 8_atomic工程代码的基础上,进行更改。通过 vscode软件打开 8_atomic工程代码。
主要在 atomic.c文件中添加原子操作,实现对Led灯的互斥访问。
这里所使用的关键的原子操作函数为:
int atomic_dec_and_test(atomic_t *v)
该函数对原子类型的变量v原子的减少1,并判断其结果是否是0,如果为0则返回真,否则返回假。
2. 对整型变量进行原子操作的代码实现
atomic.c驱动文件加入原子操作代码后,如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/atomic.h>#define LED_OFF 0
#define LED_ON 1
#define GPIOLED_NAME "gpioled"
#define GPIOLED_CNT 1//Led设备结构体
struct gpio_led {dev_t devid;int major;int minor;struct cdev cdev;struct class* class;struct device* dev;struct device_node * dev_node;int gpio_number;atomic_t lock; //原子操作//这里定义:1表示设备未使用,0表示设备已使用};struct gpio_led gpioled;static int gpioled_open(struct inode *inode, struct file *filp)
{ /*通过判断原子变量的值来检查 LED 有没有被别的应用使用 */if(!atomic_dec_and_test(&gpioled.lock)) //{atomic_inc(&gpioled.lock); /*小于 0 的话就加 1,使其原子变量等于 0 */printk("gpioled.lock:%d\n", gpioled.lock.counter);return -EBUSY;}filp->private_data = &gpioled; /*设置私有数据 */return 0;
}static ssize_t gpioled_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{int ret = 0;char rx_buf[2] = {0};ret = copy_from_user(rx_buf, buf, count);if(ret != 0){printk("copy_from_user failed!\n");}if(rx_buf[0] == LED_ON){ //设置低电平,打开Ledgpio_set_value(gpioled.gpio_number, 0); }else if(rx_buf[0] == LED_OFF){ //设置高电平,关闭Ledgpio_set_value(gpioled.gpio_number, 1);}return 0;
}static int gpioled_close(struct inode *inode, struct file *filp)
{struct gpio_led * dev = (struct gpio_led*)(filp->private_data);/* 关闭驱动文件的时候释放原子变量 */atomic_inc(&dev->lock);return 0;
}const struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = gpioled_open,.release = gpioled_close,.write = gpioled_write,
};/*驱动模块入口函数*/
static int __init gpioled_init(void)
{int ret = 0;/*原子变量的初始化:向原子变量写1 */atomic_set(&gpioled.lock, 1); /*1.注册设备号*/gpioled.major = 0;if(gpioled.major) //给出主设备号{gpioled.devid = MKDEV(gpioled.major, 0);ret = register_chrdev_region(gpioled.devid, GPIOLED_CNT, GPIOLED_NAME); }else //未给设备号时,向Linux申请设备号{ret = alloc_chrdev_region(&gpioled.devid, 0, GPIOLED_CNT, GPIOLED_NAME);gpioled.major = MAJOR(gpioled.devid);gpioled.minor = MINOR(gpioled.devid);}printk("gpioled.major: %d\n", gpioled.major);printk("gpioled.minor: %d\n", gpioled.minor);if(ret < 0){printk("apply dev_number failed!\n");goto apply_devid_failed;}/*2.设备初始化 */gpioled.cdev.owner = THIS_MODULE;cdev_init(&gpioled.cdev, &gpioled_fops);ret = cdev_add(&gpioled.cdev, gpioled.devid, GPIOLED_CNT);if(ret < 0){printk("cdev_add failed\n");goto cdev_add_failed;}/*3.自动创建设备节点*/gpioled.class = class_create(THIS_MODULE, GPIOLED_NAME);if (IS_ERR(gpioled.class)) {ret = PTR_ERR(gpioled.class);goto class_create_failed;}gpioled.dev = device_create(gpioled.class, NULL, gpioled.devid, NULL, GPIOLED_NAME);if (IS_ERR(gpioled.dev)) {ret = PTR_ERR(gpioled.dev);goto dev_create_failed;} /*读取设备节点*/gpioled.dev_node = of_find_node_by_path("/gpioled");if(NULL == gpioled.dev_node){printk("find dev_node failed!\n");goto read_devnode_failed;}/*获取Led所对应的GPIO的编号*/gpioled.gpio_number = of_get_named_gpio(gpioled.dev_node, "led-gpio", 0);if(gpioled.gpio_number < 0){printk("get_named_gpio failed!\n");goto read_devnode_failed;}/*申请GPIO管脚 */ret = gpio_request(gpioled.gpio_number, GPIOLED_NAME);if(ret != 0){printk("gpio_request failed!\n");goto read_devnode_failed;}/*设置GPIO为输出,设置为高电平,关闭Led */ret = gpio_direction_output(gpioled.gpio_number, 1);if(ret != 0){printk("gpio_direction_input failed!\n");goto set_gpio_input_failed;}return 0;set_gpio_input_failed:gpio_free(gpioled.gpio_number);
read_devnode_failed:device_destroy(gpioled.class, gpioled.devid);
dev_create_failed:class_destroy(gpioled.class);
class_create_failed:cdev_del(&gpioled.cdev);
cdev_add_failed:unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);
apply_devid_failed:return ret;
}/*驱动模块出口函数*/
static void __exit gpioled_exit(void)
{//关闭Led灯gpio_set_value(gpioled.gpio_number, 1);/*1. 删除设备*/cdev_del(&gpioled.cdev);/*2. 注销设备号*/unregister_chrdev_region(gpioled.devid, GPIOLED_CNT);//3. 销毁设备device_destroy(gpioled.class, gpioled.devid);/*4. 销毁类*/class_destroy(gpioled.class);/*释放IO */gpio_free(gpioled.gpio_number);
}/*驱动入口与出口*/
module_init(gpioled_init);
module_exit(gpioled_exit);
MODULE_LICENSE("GPL"); //License
MODULE_AUTHOR("WeiWuXian"); //author
三. 编译驱动代码与测试
1. 编译驱动代码
通过 ubuntu终端进入 8_atomic工程目录下,对驱动代码进行编译:
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/8_atomic$ make
make -C /home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/wangtian/zhengdian_Linux/Linux_Drivers/8_atomic modules
make[1]: 进入目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”CC [M] /home/wangtian/zhengdian_Linux/Linux_Drivers/8_atomic/atomic.oBuilding modules, stage 2.MODPOST 1 modulesCC /home/wangtian/zhengdian_Linux/Linux_Drivers/8_atomic/atomic.mod.oLD [M] /home/wangtian/zhengdian_Linux/Linux_Drivers/8_atomic/atomic.ko
make[1]: 离开目录“/home/wangtian/zhengdian_Linux/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga”
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/8_atomic$ sudo cp atomic.ko /home/wangtian/linux/nfs_File/rootfs/lib/modules/4.1.15/ -f
wangtian@wangtian-virtual-machine:~/zhengdian_Linux/Linux_Drivers/8_atomic$
可以看出,驱动模块编译通过。
2. 测试驱动模块
接下来就是对该驱动模块进行测试,确定原子操作是否已经实现对 Led灯的互斥访问。在开发板上经过测试,这里的驱动模块所添加的原子操作,已经实现了对 Led设备的互斥访问。
测试方法与之前一样,可以参考如下文章进行测试:
Led灯驱动添加原子操作后驱动程序测试-CSDN博客
关于对原子操作的驱动讲到这里。