globalmem驱动

globalmem为“全局内存”的意思,在globalmem字符设备中会分配一片大小为GLOBALMEM_SIZE的内存空间,并在驱动中提供对这片内存的读写、控制和定位函数,供用户空间的进程能通过Linux系统调用获取和设置这片内存。

头文件、宏定义以及结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

#define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x1
#define GLOBALMEM_MAJOR 230

static int globalmem_major = GLOBALMEM_MAJOR;
module_param(globalmem_major, int, S_IRUGO);//模块传递参数,在加载模块的时候可以自己传递

struct globalmem_dev {
struct cdev cdev;
unsigned char mem[GLOBALMEM_SIZE];//使用内存
};

struct globalmem_dev *globalmem_devp;

内核模块传入参数

内核模块中没有main函数,所以向模块内部传入参数可以通过module_param这个宏定义来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#define S_IRWXU 00700    // 用户读写可执行权限
#define S_IRUSR 00400 // 用户读权限
#define S_IWUSR 00200 // 用户写权限
#define S_IXUSR 00100 // 用户可执行权限

#define S_IRWXG 00070 // 用户组读写可执行权限
#define S_IRGRP 00040 // 用户组读权限
#define S_IWGRP 00020 // 用户组写权限
#define S_IXGRP 00010 // 用户组可执行权限

#define S_IRWXO 00007 // 其他人可读写执行权限
#define S_IROTH 00004 // 其他人可读权限
#define S_IWOTH 00002 // 其他人可写权限
#define S_IXOTH 00001 // 其他人可执行权限

kzalloc函数与kfree函数

用kzalloc申请内存的时候, 效果等同于先是用 kmalloc() 申请空间 , 然后用 memset() 来初始化 *,*所有申请的元素都被初始化为 0.

1
2
#include <linux/slab.h>
void *kmalloc(size_t size, int flags)

给 kmalloc 的第一个参数是要分配的块的大小. 第 2 个参数, 分配标志, 非常有趣, 因为它以几个方式控制 kmalloc 的行为.

最一般使用的标志, GFP_KERNEL, 代表运行在内核空间的进程而进行的. 这意味着调用函数是代表一个进程在执行一个系统调用. 使用 GFP_KENRL 意味着 kmalloc 能够使当前进程在少内存的情况下睡眠来等待一页. 一个使用 GFP_KERNEL 来分配内存的函数必须, 因此, 是可重入的并且不能在原子上下文中运行. 当当前进程睡眠, 内核采取正确的动作来定位一些空闲内存, 或者通过刷新缓存到磁盘或者交换出去一个用户进程的内存。

GFP_KERNEL 不一直是使用的正确分配标志; 有时 kmalloc 从一个进程的上下文的外部调用. 例如, 这类的调用可能发生在中断处理, tasklet, 和内核定时器中. 在这个情况下, 当前进程不应当被置为睡眠, 并且驱动应当使用一个GFP_ATOMIC 标志来代替. 内核正常地试图保持一些空闲页以便来满足原子的分配. 当使用 GFP_ATOMIC 时,kmalloc 能够使用甚至最后一个空闲页. 如果这最后一个空闲页不存在, 但是, 分配失败。

其他用来代替或者增添 GFP_KERNEL 和 GFP_ATOMIC 的标志, 尽管它们 2 个涵盖大部分设备驱动的需要. 所有的标志定义在 <linux/gfp.h>, 并且每个标志用一个双下划线做前缀, 例如 __GFP_DMA. 另外, 有符号代表常常使用的标志组合; 这些缺乏前缀并且有时被称为分配优先级。

1
2
3
#include <linux/slab.h>
void kfree(const void *objp)
//objp:内存地址,通常是kmalloc( )函数的返回值,即是指向分配的内存块起始地址的地址指针。

seek函数

文件重定位函数,改变文件的读写偏移量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{
loff_t ret = 0;
switch (orig) {
case 0:
if (offset < 0) {
ret = -EINVAL;
break;
}
if ((unsigned int)offset > GLOBALMEM_SIZE) {
ret = -EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1:
if ((filp->f_pos + offset) > GLOBALMEM_SIZE) {
ret = -EINVAL;
break;
}
if ((filp->f_pos + offset) < 0) {
ret = -EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}

文件重定位的起始地址有三种情况:同时该函数具有检测定位的合法性
①0:从文件开头进行定位
②1:从文件当前位置进行定位
③2:定位到文件末尾

ioctl函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static long globalmem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct globalmem_dev *dev = filp->private_data;

switch (cmd) {
case MEM_CLEAR:
memset(dev->mem, 0, GLOBALMEM_SIZE);
printk(KERN_INFO "globalmem is set to zero\n");
break;
default:
return -EINVAL;
}

return 0;
}

ioctl()函数可以接收用户空间的MEM_CLEAR命令实现对内存的数据清0.

驱动代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linxu/init.h>

#define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x01
#define GLOBALMEM_MAJOR 230

struct globalmemdev{
dev_t devid;
struct cdev cdev;
struct device *device;
struct class *class;
int major;
int minor;
struct device_node *nd;
unsigned char mem[GLOBALMEM_SIZE];
};
struct globalmemdev *globalmem_devp;
module_param(globalmem_devp->major, int , S_IRUGO);

static int globalmem_open(struct inode *inode, struct file *filp)
{
filp->private_data = globalmem_devp;
return 0;
}
static int globalmem_release(struct inode *inode, struct file *filp)
{
return 0;
}
static long globalmem_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct globalmemdev *dev = filp -> private_data;
switch(cmd)
{
case MEM_CLEAR:
memset(dev->mem, 0, GLOBALMEM_SIZE);
printk(KERN_INFO "globalmem is set to zero \n");//KERN_INFO提示信息
default :
return -EINVAL;
}
}
static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
unsigned long p = *offt;
unsigned int count = cnt;
int ret = 0;

struct globalmemdev *dev = filp -> private_data;
if(p >= GLOBALMEM_SIZE)
{
return 0;
}
if(count > GLOBALMEM_SIZE - p)
count = GLOBALMEM_SIZE-p;
if(copy_to_user(buf, dev->mem + p, count))
ret = -EFAULT;
else{
*ppos += count;
ret = count;
printk(KERN_INFO "read %u bytes(s) from %lu\n", count , p);
}
return ret;
}
static ssize_t globalmem_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
unsigned long p = *offt;
unsigned int count = cnt ;
int ret = 0;

struct globalmemdev *dev = filp -> private_data;
if(p >= GLOBALMEM_SIZE)
return 0;
if(count > GLOBALMEM_SIZE - p)
count = GLOBALMEM_SIZE - p;
if(copy_from_user(dev->mem + p, buf, count))
ret = -EFAULT;
else{
*offt += count;
ret = count;
printk(KERN_INFO "written %u bytes(s) from %lu\n", count, p);
}
return ret;
}
static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
{
loff_t ret = 0;
switch (orig) {
case 0:
if (offset < 0) {
ret = -EINVAL;
break;
}
if ((unsigned int)offset > GLOBALMEM_SIZE) {
ret = -EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1:
if ((filp->f_pos + offset) > GLOBALMEM_SIZE) {
ret = -EINVAL;
break;
}
if ((filp->f_pos + offset) < 0) {
ret = -EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static const struct file_operations globalmem_ops = {
.owner = THIS_MODULE,
.llseek = globalmem_llseek,
.read = globalmem_read,
.write = globalmem_write,
.unlocked_ioctl = globalmem_ioctl,
.open = globalmem_open,
.release = globalmem_release,
};
static int __init globalmem_init(void)
{
if(globalmem_devp.major)
{
globalmem_devp.devid = MKDEV(globalmem_devp.major, 0);
register_chrdev_region(globalmem_devp.devid, 1, "globalmem");
}else{
alloc_chrdev_region(&globalmem_devp.devid, 0, 1, "globalmem");
globalmem_devp.major = MAJOR(globalmem_devp.devid);
globalmem_devp.minor = MINOR(globalmem_devp.devid);
}
cdev_init(&globalmem_devp.cdev, &globalmem_ops);
dev->cdev.owner = THIS_MODULE;
cdev_add(&globalmem_dep.cdev, globalmem_devp.devid, 1);

globalmem_devp = kzalloc(sizeof(struct globalmem_dev), GFP_KERNEL);
if (!globalmem_devp) {
ret = -ENOMEM;
goto fail_malloc;
}

fail_malloc:
unregister_chrdev_region(devno, 1);
return ret;

}
static void __exit globalmem_exit(void)
{
cdev_del(&globalmem_devp->cdev);
kfree(globalmem_devp)
unregister_chrdev_region(globalmem_devp.devid, 1);

}
module_init(globalmem_init);
module_exit(globalmem_exit);
MODULE_LICENSE("GPL);