0. 需要的头文件、宏及变量
1 #include <linux/module.h>
2 #include <linux/init.h>
3 #include <linux/kernel.h>
4 #include <linux/fs.h>
5 #include <linux/miscdevice.h>
6 #include <linux/uaccess.h>
7
8 // 定义一些宏和变量
9 #define DEVICE_NAME "wordcount" // 定义设备文件名
10 static unsigned char mem[10000]; // 保存向设备文件写入的数据
11 // static char read_flag = 'y'; // y:已从设备文件读取数据 n:未读取数据
12 static int word_count = 0; // 单词数
13 #define TRUE -1
14 #define FALSE 0
1. 建立Linux驱动骨架(装载和卸载Linux驱动)
装载:
// 初始化Linux驱动
static int word_count_init(void)
{
int ret;
// 建立设备文件
ret = misc_register(&misc);
// 输出日志信息
printk("word_count_init_success\n");
return ret;
}
卸载:
// 退出Linux驱动
static void word_count_exit(void)
{
// 移除设备文件
misc_deregister(&misc);
// 输出日志信息
printk("word_count_exit_success\n");
}
注册Linux驱动加载和卸载函数:
module_init(word_count_init);
module_exit(word_count_exit);
2.注册和注销设备文件
指定设备文件信息:
// 描述与设备文件触发的事件对应的回调函数指针
// owner:设备事件回调函数应用于哪些驱动模块,THIS_MODULE表示应用于当前驱动模块
// 需要设置read和write成员变量,系统才能调用处理读写设备文件动作的函数
static struct file_operations dev_fops =
{ .owner = THIS_MODULE, .read = word_count_read, .write = word_count_write };
// 描述设备文件的信息
// minor: 次设备号 MISC_DYNAMIC_MINOR:动态生成次设备号 name: 设备文件名称
// fops: file_operations结构体变量指针
static struct miscdevice misc =
{ .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_fops };
注册和注销动作已于步骤1中通过misc_register和 misc_deregister函数完成。
3. 指定与驱动相关的信息
// 驱动相关信息
MODULE_AUTHOR("LAZIJI");
MODULE_DESCRIPTION("statistics of word count");
MODULE_ALIAS("word count module");
MODULE_LICENSE("GPL");
4. 编写回调函数
读取设备文件数据的函数:
// file:指向设备文件 buf:保存可读取的数据 count:可读取的字节数 ppos:读取数据的偏移量
static ssize_t word_count_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
unsigned char temp[4];
// 将单词数(int类型)分解成4个字节存储在buf中
temp[0] = word_count >> 24;
temp[1] = word_count >> 16;
temp[2] = word_count >> 8;
temp[3] = word_count;
copy_to_user(buf, (void*) temp, 4);
printk("read:word count:%d", (int) count);
return count;
}
向设备文件写数据的函数:
// file:指向设备文件 buf:保存写入的数据 count:写入数据的字节数 ppos:写入数据的偏移量
static ssize_t word_count_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
ssize_t written = count;
copy_from_user(mem, buf, count);
mem[count] = '\0';
// 统计单词数
word_count = get_word_count(mem);
printk("write:word count:%d", (int) word_count);
return written;
}
5.编写业务逻辑
判断字符是否为空格(包括空格符、制表符、回车符和换行符)
static char is_spacewhite(char c)
{
if(c == ' ' || c == 9 || c == 13 || c == 10)
return TRUE;
else
return FALSE;
}
统计单词数
static int get_word_count(const char *buf)
{
int n = 1;
int i = 0;
char c = ' ';
// 处理多个空格分隔的情况,0:正常情况,1:已遇到一个空格
if(*buf == '\0')
return 0;
// 第1个字符是空格,从0开始计数
if(is_spacewhite(*buf) == TRUE)
n--;
// 扫描字符串中的每一个字符
for (; (c = *(buf + i)) != '\0'; i++)
{
// 只由一个空格分割单词的情况
if(flag == 1 && is_spacewhite(c) == FALSE)
{
flag = 0;
}
// 由多个空格分隔单词的情况,忽略多余的空格
else if(flag == 1 && is_spacewhite(c) == TRUE)
{
continue;
}
// 当前字符为空格时单词数加1
if(is_spacewhite(c) == TRUE)
{
n++;
flag = 1;
}
}
if(is_spacewhite(*(buf + i - 1)) == TRUE)
n--;
return n;
}
6.编写Makefile文件
1 obj-m := word_count1.o
2
3 PWD := $(shell pwd)
4 KernelDir :=/lib/modules/$(shell uname -r)/build/
5
6 all:
7 $(MAKE) -C $(KernelDir) M=$(PWD) modules
8
9 all_install:
10 $(MAKE) -C $(KernelDir) M=$(PWD) modules_install
11
12 clean:
13 rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers *.order *.mod
14 .PHOHY: modules modules_install clean
7.编译Linux驱动程序
编译为模块的步骤:将驱动程序和Makefile文件放到同一目录下,将命令行切换到该目录下,输入make完成编译,产生.ko文件。
8.安装和卸载Linux驱动
分别输入insmod + .ko文件和rmmod + .ko文件完成安装和卸载
9. Linux驱动测试
测试程序test.c:
1 #include <stdio.h>
2 #include <fcntl.h>
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <string.h>
6 int main(int argc, char *argv[])
7 {
8 int testdev; // 打开设备文件(/dev/wordcount)的句柄
9 unsigned char buf[4]; // 表示单词数的4个字节
10 // 打开设备文件
11 testdev = open("/dev/wordcount", O_RDWR);
12 // 如果open函数返回-1,表示打开设备文件失败
13 if (testdev == -1)
14 {
15 printf("Cann't open file \n");
16 return 0;
17 }
18 // 如果test_word_count后面跟有命令行参数,程序会将第1个参数值当做待统计的字符串
19 // 如果没有命令行参数,则只读取设备文件中的值
20 if (argc > 1)
21 {
22 // 向设备文件写入待统计的字符串
23 write(testdev, argv[1], strlen(argv[1]));
24 // 输出待统计的字符串
25 printf("string:%s\n", argv[1]);
26 }
27 // 读取设备文件中的单词数(4个字节)
28 read(testdev, buf, 4);
29
30 int n = 0; // 单词数
31
32 // 将4个字节还原成int类型的值
33 n = ((int) buf[0]) << 24 |((int) buf[1]) << 16 | ((int) buf[2]) << 8 | ((int) buf[3]);
34 // 分别输出从设备文件获取的4个字节的值
35 printf("word byte display:%d,%d,%d,%d\n", buf[0], buf[1], buf[2], buf[3]);
36 // 输出统计出的单词数
37 printf("word count:%d\n", n);
38 // 关闭设备文件
39 close(testdev);
40
41 return 0;
42 }
编译测试代码:在终端输入 gcc test.c -o test
执行测试代码:在终端输入 ./test "la zi ji zhen de la"
终端显示的结果:
string:la zi ji zhen de la
word byte display:0,0,0,6
word count:6