任务

一、实验目的
1)理解设备是文件的概念。
2)掌握Linux模块、驱动的概念和编程流程
3)Windows /Linux下掌握文件读写基本操作
二、实验内容
1)编写一个Linux内核模块,并完成安装/卸载等操作。
2)编写Linux驱动程序并编程应用程序测试。功能:write几个整数进去,read出其和或差或最大值。
3)编写Linux驱动程序并编程应用程序测试。功能:有序读写内核缓冲区,返回实际读写字节数。

任务1 编写一个Linux内核模块,并完成安装/卸载等操作。

1. 提示

提示1:安装时和退出时在内核缓冲区显示不同的字符串。
提示2:相关函数:module_init( )、 module_exit( )
提示3: MODULE_LICENSE( )、 MODULE_AUTHOR ( )等可选
提示4:安装命令:insmod XXXX.ko
提示5:扩展:编写带参数的模块程序
int mytest = 100;
module_param(mytest, int, 0644);

2. 任务代码

先安装一个必要的软件包(否则会有warning):

  1. sudo apt install libelf-dev

文件code1.c:

  1. #include<linux/init.h>
  2. #include<linux/kernel.h>
  3. #include<linux/module.h>
  4. #include<linux/moduleparam.h>
  5. #include<linux/sched.h>
  6. MODULE_LICENSE("GPL");
  7. static char* yourID="shandianchengzi";
  8. module_param(yourID,charp,0644);
  9. static int code1_init(void){
  10. //输出欢迎信息
  11. printk(KERN_ALERT"Hello, dear %s!\n", yourID);
  12. return 0;
  13. }
  14. static void code1_exit(void){
  15. printk(KERN_ALERT"Goodbye, %s!\n", yourID);
  16. }
  17. module_init(code1_init);
  18. module_exit(code1_exit);

Makefile:

  1. obj-m += code1.o
  2. all:
  3. make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
  4. clean:
  5. make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

然后运行:

  1. sudo make

3. 结果及说明

1)模块作用:实验文件编译生成的ko模块可接收charp型参数,默认为"shandianchengzi"。当用户加载模块时输出"Hello, dear 参数!\n",退出时输出"GoodBye, 参数!\n"。
2)如何向模块中传入参数:函数原型module_param(name, type, perm),参数name,type是自己定义的变量的类型,perm是权限。
其中常用的权限:①0755->用户具有读/写/执行权限,组用户和其它用户具有读/写权限;②0644->用户具有读/写权限,组用户和其它用户具有只读权限;
一般赋予目录0755权限,而文件赋予0644权限。
3)编译:

4)结果:

先用sudo dmesg -C清空缓冲区,然后使用sudo insmod code1.ko yourID='shandianchengzi'装入内核并修改参数值,再dmesg显示当前内容。再使用sudo rmmod code1.ko卸载内核,再dmesg显示当前内容。与预期相符。

参考:
主要:Linux中添加一个带参数的模块
传入字符串的设计:第四章Linux内核模块之五(模块参数)

任务2 编写Linux驱动程序并编程应用程序测试。

1. 提示

提示1:参考任务1
提示2:至少实现xx_open,xx_write,xx_read等函数
提示3:功能:
xx_write( )写进去2个整数
xx_read( )读回结果(和或差或最大值)
提示4: [可选的设备注册方式,其余方式参考baidu]
struct miscdevice mydemodrv_misc_device ;
ret = misc_register( &mydemodrv_misc_device )

2. 任务代码

code2.c:

  1. #include <linux/module.h>
  2. #include <linux/fs.h>
  3. #include <linux/uaccess.h>
  4. #include <linux/init.h>
  5. #include <linux/miscdevice.h>
  6. #define NAME "code2"
  7. MODULE_LICENSE("GPL");
  8. static struct device *mydemodrv_device;
  9. static int a=0,b=0;
  10. static int demodrv_open(struct inode *inode, struct file *file)
  11. {
  12. int major = MAJOR(inode->i_rdev);
  13. int minor = MINOR(inode->i_rdev);
  14. printk("%s: major=%d, minor=%d\n", __func__, major, minor);
  15. return 0;
  16. }
  17. static int demodrv_release(struct inode *inode, struct file *file)
  18. {
  19. return 0;
  20. }
  21. static ssize_t
  22. demodrv_read(struct file *file, char __user *buf, size_t lbuf, loff_t *ppos)
  23. {
  24. printk("%s: a+b=%d\n", __func__,a+b);
  25. return 0;
  26. }
  27. static ssize_t
  28. demodrv_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
  29. {
  30. if(count>64)
  31. {
  32. count = 64;
  33. }
  34. char temp[64];
  35. if(copy_from_user(temp,buf,count))
  36. {
  37. return -EFAULT;
  38. }
  39. sscanf(temp, "%d%d",&a,&b);
  40. printk("%s: a=%d,b=%d\n", __func__,a,b);
  41. return count;
  42. }
  43. static const struct file_operations demodrv_fops = {
  44. .owner = THIS_MODULE,
  45. .open = demodrv_open,
  46. .release = demodrv_release,
  47. .read = demodrv_read,
  48. .write = demodrv_write
  49. };
  50. static struct miscdevice mydemodrv_misc_device = {
  51. .minor = MISC_DYNAMIC_MINOR,
  52. .name = NAME,
  53. .fops = &demodrv_fops,
  54. };
  55. static int __init code2_init(void)
  56. {
  57. int ret;
  58. ret = misc_register(&mydemodrv_misc_device);
  59. if (ret) {
  60. printk("failed register code2 misc device\n");
  61. return ret;
  62. }
  63. mydemodrv_device = mydemodrv_misc_device.this_device;
  64. printk("succeeded register char device: %s\n", NAME);
  65. return 0;
  66. }
  67. static void __exit code2_exit(void)
  68. {
  69. printk("removing device: %s\n", NAME);
  70. misc_deregister(&mydemodrv_misc_device);
  71. }
  72. module_init(code2_init);
  73. module_exit(code2_exit);

测试程序:
test.c:

  1. #include <stdio.h>
  2. #include <fcntl.h>
  3. #include <unistd.h>
  4. #define DEV_NAME "/dev/code2"
  5. int main()
  6. {
  7. char buffer[64];
  8. int fd,a,b;
  9. fd = open(DEV_NAME, O_RDWR | O_CREAT);
  10. if (fd < 0) {
  11. printf("open device %s failded\n", DEV_NAME);
  12. return -1;
  13. }
  14. printf("请输入两个整数:");
  15. scanf("%d%d",&a,&b);
  16. sprintf(buffer,"%d %d",a,b);
  17. write(fd,buffer,64);
  18. read(fd, buffer, 64);
  19. close(fd);
  20. return 0;
  21. }

3. 结果及说明

1)编译:

2)结果:完成的是求和功能。

3)遇到的问题:最开始,打开文件指针的时候用了O_RDONLY,只读。后来写write的时候死活读不进去。。。
怀疑是中间文件没删、模块代码返回值不能size_t强转ssize_t等问题,结果原来是因为这个。。。。。。。
改成O_RDWR | O_CREAT。

参考:《内核kernel:misc机制字符设备驱动模块编写》
O_RDWR | O_CREAT参考:3.8 write函数-文件数据写

4)用的方法是先读入缓冲区,再用sscanf读。我觉得传整型指针有点麻烦,要写两个write。
5)设备读不了写不了装不了用不了没输出:
甚至把模块卸载了,它还在!

这是因为在装入模块之前,不慎调用test程序,已经提前申请打开了设备,以致于装载的时候未装载成功。
如果出现这种情况,运行:

  1. sudo rm /dev/code2

任务3 编写Linux驱动程序并编程应用程序测试。

1. 提示

提示1:参考任务1
提示2:至少实现xx_open,xx_write,xx_read等函数
提示3:功能:
内核分配一定长度的缓冲区,比如64字节。

xx_write()写进去若干字符,注意维护写入位置。下次继续写的话,接着该位置往后写,直到缓冲区末尾。要返回实际写入字数。
xx_read()读出若干字符串,注意维护读出位置。下次继续读的话,接着该位置往后读,直到缓冲区末尾。要返回实际读回字数。
扩展:
▲缓冲区设置为循环缓冲区?
▲如何避免写覆盖,避免读重复?

2. 任务代码

code3.c

  1. #include <linux/module.h>
  2. #include <linux/fs.h>
  3. #include <linux/uaccess.h>
  4. #include <linux/init.h>
  5. #include <linux/miscdevice.h>
  6. #include <linux/slab.h> /*kmalloc*/
  7. #define NAME "code3"
  8. MODULE_LICENSE("GPL");
  9. static struct device *mydemodrv_device;
  10. static char *device_buffer;
  11. static size_t pos;
  12. #define MAX_DEVICE_BUFFER_SIZE 64
  13. static int demodrv_open(struct inode *inode, struct file *file)
  14. {
  15. device_buffer = kmalloc(MAX_DEVICE_BUFFER_SIZE, GFP_KERNEL);
  16. pos=0;
  17. return 0;
  18. }
  19. static ssize_t
  20. demodrv_read(struct file *file, char __user *buf, size_t lbuf, loff_t *ppos)
  21. {
  22. if(pos < lbuf)
  23. {
  24. lbuf = pos;
  25. }
  26. if(copy_to_user(buf,device_buffer+pos-lbuf,lbuf))
  27. {
  28. return -EFAULT;
  29. }
  30. pos=pos-lbuf;
  31. printk("%s: 读出%ld字节,读出后指针位置为%ld\n", __func__, lbuf, pos);
  32. return lbuf;
  33. }
  34. static ssize_t
  35. demodrv_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
  36. {
  37. if(pos + count > 64)
  38. {
  39. count = 64-pos;
  40. if(count < 0)
  41. return count;
  42. }
  43. if(copy_from_user(device_buffer+pos,buf,count))
  44. {
  45. return -EFAULT;
  46. }
  47. pos=pos+count;
  48. printk("%s: 写入%ld字节,写完后缓冲区为%s\n", __func__, count, device_buffer);
  49. return count;
  50. }
  51. static const struct file_operations demodrv_fops = {
  52. .owner = THIS_MODULE,
  53. .open = demodrv_open,
  54. .read = demodrv_read,
  55. .write = demodrv_write
  56. };
  57. static struct miscdevice mydemodrv_misc_device = {
  58. .minor = MISC_DYNAMIC_MINOR,
  59. .name = NAME,
  60. .fops = &demodrv_fops,
  61. };
  62. static int __init code3_init(void)
  63. {
  64. int ret;
  65. ret = misc_register(&mydemodrv_misc_device);
  66. if (ret) {
  67. printk("failed register code2 misc device\n");
  68. return ret;
  69. }
  70. mydemodrv_device = mydemodrv_misc_device.this_device;
  71. printk("succeeded register char device: %s\n", NAME);
  72. return 0;
  73. }
  74. static void __exit code3_exit(void)
  75. {
  76. kfree(device_buffer);
  77. printk("removing device: %s\n", NAME);
  78. misc_deregister(&mydemodrv_misc_device);
  79. }
  80. module_init(code3_init);
  81. module_exit(code3_exit);

测试程序:
test.c:

  1. #include <stdio.h>
  2. #include <fcntl.h>
  3. #include <unistd.h>
  4. #include <string.h>
  5. #define DEV_NAME "/dev/code3"
  6. int main()
  7. {
  8. char buffer[64];
  9. int fd,a,b;
  10. fd = open(DEV_NAME, O_RDWR | O_CREAT);
  11. if (fd < 0) {
  12. printf("open device %s failded\n", DEV_NAME);
  13. return -1;
  14. }
  15. printf("请输入向缓冲区写的内容:");
  16. scanf("%s",buffer);
  17. write(fd,buffer,strlen(buffer));
  18. read(fd, buffer, 64);
  19. printf("从缓冲区读:%s\n", buffer);
  20. close(fd);
  21. return 0;
  22. }

3. 结果及说明

1)kmalloc头文件是#include <linux/slab.h>。
2)编译:

3)运行:

4)遇到的问题2:copy_to_user和copy_from_user的to和from参数是反过来的,一开始没注意。
5)遇到的问题3:
下面这个不行:

  1. if(pos - lbuf < 0)
  2. {
  3. lbuf = pos;
  4. }

改成这个才行:

  1. if(pos < lbuf)
  2. {
  3. lbuf = pos;
  4. }

因为size_t是unsigned类型的,所以不能直接跟0比较。

【HUST】网安|操作系统实验|实验四 设备管理、文件管理的更多相关文章

  1. 201671030113 李星宇 实验十四 团队项目评审&课程学习总结

    项目 内容 所属课程 [所属课程(https://www.cnblogs.com/nwnu-daizh/) 作业要求 作业要求 课程学习目标 (1)掌握软件项目评审会流程:(2)反思总结课程学习内容 ...

  2. 20145221 《Java程序设计》实验报告四:Android开发基础

    20145221 <Java程序设计>实验报告四:Android开发基础 实验要求 基于Android Studio开发简单的Android应用并部署测试; 了解Android组件.布局管 ...

  3. 201671010426 孙锦喆 实验十四 团队项目评审&课程学习总结

    徐明锦 徐明锦 2 95 2019-06-30T14:54:00Z 2019-06-30T14:54:00Z 9 608 3472 28 8 4072 14.00 Clean Clean false ...

  4. 201671010446姚良实验十四团队项目评审&课程总结

    实验十四 团队项目评审&课程学习总结 项目 内容 这个作业属于哪个课程 http://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://www.cn ...

  5. 201671030114 马秀丽 实验十四 团队项目评审&课程学习总结

    项目 内容 作业所属课程 所属课程 作业要求 作业要求 课程学习目标 (1)掌握软件项目评审会流程:(2)反思总结课程学习内容 任务一:团队项目审核已完成.项目验收过程意见表已上交. 任务二:课程学习 ...

  6. 201671030106 何启芝 实验十四 团队项目评审&课程学习总结

    项目 内容 这个作业属于哪个课程 >>2016级计算机科学与工程学院软件工程(西北师范大学) 这个作业的要求在哪里 >>实验十四 团队项目评审&课程学习总结 课程学习目 ...

  7. 201671030111 李蓉 实验十四 团队项目评审&课程学习总结

    项目 内容 这个作业属于哪个课程 软件工程 这个作业的要求在哪里 实验十四 团队项目评审&课程学习总结 作业学习目标 掌握软件项目评审会流程,反思总结课程学习内容. 任务一:结合本学期课程学习 ...

  8. 实验十四 第九组 张燕~杨蓉庆~杨玲 Swing图形界面组件

    实验十四  Swing图形界面组件 8-11-29 理论知识 Swing和MVC设计模式 (1)设计模式(Design pattern)是设计者一种流行的 思考设计问题的方法,是一套被反复使用,多数人 ...

  9. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十四:储存模块

    实验十四比起动手笔者更加注重原理,因为实验十四要讨论的东西,不是其它而是低级建模II之一的模块类,即储存模块.接触顺序语言之际,“储存”不禁让人联想到变量或者数组,结果它们好比数据的暂存空间. . i ...

  10. 20145203Java实验报告四:Android开发基础

    Java实验报告四:Android开发基础 实验要求: 1.安装Android Studio 2.运行安卓AVD模拟器 3.使用安卓运行出虚拟手机并显示HelloWorld以及自己的学号 实验过程 ( ...

随机推荐

  1. BurpSuite重放发包的一些区别

    目录 Send Group in parallel Single connection 和 Separate connections 的差别 实际应用场景 Reference 2022年之后,Burp ...

  2. 2分钟学会 DeepSeek API,竟然比官方更好用!

    大家好,我是程序员鱼皮.最近 DeepSeek AI 太火了,效果也很强,但致命问题是 不稳定, 经常给我返回 服务器繁忙,请稍后再试,甚至让我怀疑自己被杀熟了. 也有网友说,第一次使用成功率很高,第 ...

  3. C++最基本调用动态链接库dll方法的小结

    针对当时初学动态链接.静态链接,有些文档整理一下发出来算是给自己和读者一个小结. 首先创建DLL 编辑头文件 dllmain.h 头文件: #pragma once #if defined(_DLL_ ...

  4. FastAPI极速入门:15分钟搭建你的首个智能API(附自动文档生成)🚀

    title: FastAPI极速入门:15分钟搭建你的首个智能API(附自动文档生成) date: 2025/3/1 updated: 2025/3/1 author: cmdragon excerp ...

  5. 基于正则化的图自编码器在推荐算法中的应用 Application of graph auto-encoders based on regularization in recommendation algorithms

    引言 看过的每一篇文章,都是对自己的提高.不积跬步无以至千里,不积小流无以成江海,积少成多,做更好的自己. 本文基于2023年4月6日发表于SCIPEERJ COMPUTER SCIENCE(PEER ...

  6. MySQL错误码大全

    B.1. 服务器错误代码和消息服务器错误信息来自下述源文件:· 错误消息信息列在share/errmsg.txt文件中."%d"和"%s"分别代表编号和字符串, ...

  7. 1、从DeepSeek API调用到Semantic Kernel集成:深度解析聊天机器人开发全链路

    引言:AI时代下的聊天机器人开发范式演进 在生成式AI技术爆发的当下,基于大语言模型(LLM)的聊天机器人开发已形成标准化技术链路.本文将结合DeepSeek API与微软Semantic Kerne ...

  8. 工作面试必备:SQL 中的各种连接 JOIN 的区别总结!

    前言 尽管大多数开发者在日常工作中经常用到Join操作,如Inner Join.Left Join.Right Join等,但在面对特定查询需求时,选择哪种Join类型以及如何使用On和Where子句 ...

  9. linux重启后,启动docker和docker对应的服务

    我的项目部署在docker上,linux关闭之后,项目要重启,在此做一个记录1.启动linux之后,执行docker images或者docker ps,如果出现下面的错误Cannot connect ...

  10. Redis 原理 - Hash

    Hash 数据结构 使用 ziplist 当同时满足下面两个条件时,使用 ziplist 存储数据 元素个数少于512个 (hash-max-ziplist-entries: 512) 每个元素长度小 ...