Linux 内核定义了叫做 atomic_t
的结构体来完成整型数据的原操作,在使用是使用原子变量来代替整型变量。此结构体定义在 include/linux/types.h
文件中,定义如下:
typedef struct {
int counter;
}atomic_t;
如果要使用原子操作 API 函数,需要首先定义一个 atomic_t 的变量,如下所示:
atomic_t value; /* 定义一个原子变量 value */
也可以在定义原子变量的时候给原子变量赋初值,如下所示
atomic_t value = ATOMIC_INIT(0) /* 定义原子变量 value 并赋初值为 0*/
Linux 内核提供的原子变量操作的 API 如下:
函数 | 描述 |
---|---|
ATOMIC_INIT(int i) | 定义原子变量的时候初始化 |
int atomic_read(atomic_t *v) | 读取原子变量 v 的值,并且返回 |
void atomic_set(atomic_t *v, int i) | 向 v 写入 i 值 |
void atomic_add(int i, atomic_t *v) | 给 v 加上 i 值 |
void atomic_sub(int i, atomic_t *v) | 给 v 减去 i 值 |
void atomic_inc(atomic_t *v) | 自增 |
void atomic_dec(atomic_t *v) | 自减 |
int atomic_dec_return(atomic_t *v) | 自减,并返回 v 的值 |
int atomic_inc_return(atomic_t *v) | 自增,并返回 v 的值 |
int atomic_sub_and_test(int i, atomic_t *v) | 从 v 减 i,如果结果为 0 就返回真,否则返回假 |
int atomic_dec_and_test(atomic_t *v) | 从 v 减 1,如果结果为 0 就返回真,否则返回假 |
int atomic_inc_and_test(atomic_t *v) | 给 v 加 1,如果结果为 0 就返回真,否则返回假 |
int atomic_inc_and_test(atomic_t *v) | 给 v 加 1,如果结果为 0 就返回真,否则返回假 |
int atomic_add_negative(int i, atomic_t *v) | 给 v 加 i,如果结果为负就返回真,否则返回假 |
如果使用 64 位的 SOC 的话,就要用到 64 位的原子变量,Linux 内核也定义了 64 位原子结构体,如下所示:
#ifdef CONFIG_64BIT
typedef struct {
s64 counter;
} atomic64_t;
#endif
typedef __s64 s64;
__extension__ typedef __signed__ long long __s64;
相应的也提供了 64 位原子变量的操作 API 函数,和上表用法一样,只是将 atomic_ 前缀换为 atomic64_ 将 int 换为 long long。如果使用的是 64 位的 SOC,那么就要使用 64 位的原子操作函数。
原子变量使用示例如下:
atomic_t v = ATOMIC_INIT(0); /* 定义并初始化原子变量 v = 0 */
atomic_set(&v, 10); /* 设置 v = 10 */
atomic_read(&v); /* 读取 v 的值,肯定是 10 */
atomic_inc(&v); /* v 的值加 1,v = 11 */
位操作也是很常用的操作,Linux 内核也提供了一系列的原子位操作 API 函数,只不过原子位操作不像原子整形变量那样有个 atomic_t 的数据结构,原子位操作是直接对内存进行操作,API 函数下所示:
函数 | 描述 |
---|---|
void set_bit(int nr, void *p) | 将 p 地址的第 nr 位置 1 |
void clear_bit(int nr,void *p) | 将 p 地址的第 nr 位清零 |
void change_bit(int nr, void *p) | 将 p 地址的第 nr 位进行翻转 |
int test_bit(int nr, void *p) | 获取 p 地址的第 nr 位的值 |
int test_and_set_bit(int nr, void *p) | 将 p 地址的第 nr 位置 1,并且返回 nr 位原来的值 |
int test_and_clear_bit(int nr, void *p) | 将 p 地址的第 nr 位清零,并且返回 nr 位原来的值 |
int test_and_change_bit(int nr, void *p) | 将 p 地址的第 nr 位翻转,并且返回 nr 位原来的值 |
#include "linux/device/class.h"
#include "linux/export.h"
#include "linux/uaccess.h"
#include
#include
#include
#include
#include
#include
#include
#define CHRDEVBASE_NAME "chrdev_atom" /* 设备名 */
#define CHRDEVBASE_NUM 1 /* 设备数目 */
static char write_buf[100];
static char read_buf[100];
static char *string_test = "kernel data this tyustli test";
typedef struct {
dev_t dev_id; /* 设备号 */
struct cdev c_dev; /* cdev */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
atomic_t lock; /* 原子锁 */
} new_chrdev_t;
new_chrdev_t new_chrdev;
static int chrdevbase_open(struct inode *inode, struct file *file)
{
/* 通过判断原子变量的值来检查当前驱动有没有被别的应用使用 */
if (!atomic_dec_and_test(&new_chrdev.lock)) {
atomic_inc(&new_chrdev.lock); /* 小于 0 的话就加 1,使其原子变量等于 0 */
return -EBUSY; /* 驱动被使用,返回忙 */
}
printk("k: chrdevbase open\r\n");
return 0;
}
static ssize_t chrdevbase_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long ret = 0;
printk("k: chrdevbase read\r\n");
memcpy(read_buf, string_test, strlen(string_test));
ret = copy_to_user(buf, read_buf, count);
if (ret == 0) {
printk("k: read data success\r\n");
} else {
printk("k: read data failed ret = %ld\r\n", ret);
}
return ret;
}
static ssize_t chrdevbase_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long ret = 0;
printk("k: chrdevbase write\r\n");
ret = copy_from_user(write_buf, buf, count);
if (ret == 0) {
printk("k: write data success write data is: %s\r\n", write_buf);
} else {
printk("k: write data failed ret = %ld\r\n", ret);
}
return count;
}
static int chrdevbase_release(struct inode *inode, struct file *file)
{
/* 关闭驱动文件的时候释放原子变量 */
atomic_inc(&new_chrdev.lock);
printk("k: chrdevbase release\r\n");
return 0;
}
static struct file_operations chrdevbase_fops = {
.owner = THIS_MODULE,
.open = chrdevbase_open,
.read = chrdevbase_read,
.write = chrdevbase_write,
.release = chrdevbase_release,
};
static int __init chrdevbase_init(void)
{
int err = 0;
atomic_set(&new_chrdev.lock, 1); /* 原子变量初始值为1 */
err = alloc_chrdev_region(&new_chrdev.dev_id, 0, CHRDEVBASE_NUM,
CHRDEVBASE_NAME);
if (err < 0) {
printk("k: alloc chrdev region failed err = %d\r\n", err);
return -1;
}
/* get major and minor */
new_chrdev.major = MAJOR(new_chrdev.dev_id);
new_chrdev.minor = MINOR(new_chrdev.dev_id);
printk("k: newcheled major=%d,minor=%d\r\n", new_chrdev.major,
new_chrdev.minor);
new_chrdev.c_dev.owner = THIS_MODULE;
cdev_init(&new_chrdev.c_dev, &chrdevbase_fops);
err = cdev_add(&new_chrdev.c_dev, new_chrdev.dev_id, CHRDEVBASE_NUM);
if (err < 0) {
printk("k: cdev add failed err = %d\r\n", err);
goto out;
}
new_chrdev.class = class_create(CHRDEVBASE_NAME);
if (IS_ERR(new_chrdev.class)) {
printk("k: class create failed\r\n");
goto out_cdev;
}
new_chrdev.device = device_create(new_chrdev.class, NULL, new_chrdev.dev_id,
NULL, CHRDEVBASE_NAME);
if (IS_ERR(new_chrdev.device)) {
printk("k: device create failed\r\n");
goto out_class;
}
printk("k: base module init\r\n");
return 0;
out_class:
class_destroy(new_chrdev.class);
out_cdev:
cdev_del(&new_chrdev.c_dev);
out:
unregister_chrdev_region(new_chrdev.dev_id, CHRDEVBASE_NUM);
return err;
}
static void __exit chrdevbase_exit(void)
{
device_destroy(new_chrdev.class, new_chrdev.dev_id);
class_destroy(new_chrdev.class);
cdev_del(&new_chrdev.c_dev);
unregister_chrdev_region(new_chrdev.dev_id, CHRDEVBASE_NUM);
printk("k: base module exit!\r\n");
}
module_init(chrdevbase_init);
module_exit(chrdevbase_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("tyustli");
MODULE_INFO(intree, "Y"); /* loading out-of-tree module taints kernel */
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
static char usrdata[] = { "user data!" };
int main(int argc, char *argv[])
{
int fd, retvalue;
char *filename;
unsigned char run_cnt = 0;
char readbuf[100], writebuf[100];
if (argc != 3) {
printf("u: error Usage!\r\n");
return -1;
}
filename = argv[1];
/* 打开驱动文件 */
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("u: can't open file %s\r\n", filename);
return -1;
}
/* 从驱动文件读取数据 */
if (atoi(argv[2]) == 1) {
retvalue = read(fd, readbuf, 50);
if (retvalue < 0) {
printf("u: read file %s failed!\r\n", filename);
} else {
/* 读取成功,打印出读取成功的数据 */
printf("u: read data:%s\r\n", readbuf);
}
}
/* 模拟占用驱动 25s,此时另一个线程去打开驱动 */
while (1) {
sleep(5);
run_cnt++;
if (run_cnt >= 5)
break;
}
/* 向设备驱动写数据 */
if (atoi(argv[2]) == 2) {
memcpy(writebuf, usrdata, sizeof(usrdata));
retvalue = write(fd, writebuf, 50);
if (retvalue < 0) {
printf("u: write file %s failed!\r\n", filename);
}
}
/* 关闭设备 */
retvalue = close(fd);
if (retvalue < 0) {
printf("u: can't close file %s\r\n", filename);
return -1;
}
return 0;
}
模块安装
modprobe my_module
模块运行
/lib/modules/6.5.7+/my_app /dev/chrdev_atom 1 &
/lib/modules/6.5.7+/my_app /dev/chrdev_atom 1 &
结果
~ # /lib/modules/6.5.7+/my_app /dev/chrdev_atom 1 &
~ # k: chrdevbase open
k: chrdevbase read
k: read data success
u: read data:kernel data this tyustli test
~ # /lib/modules/6.5.7+/my_app /dev/chrdev_atom 1 &
~ # u: can't open file /dev/chrdev_atom
[2]+ Done(255) /lib/modules/6.5.7+/my_app /dev/chrdev_atom 1
~ # k: chrdevbase release
[1]+ Done /lib/modules/6.5.7+/my_app /dev/chrdev_atom 1