广州大学Linux实验3文件操作
实验内容:编写一个内核模块,在/proc文件系统中增加一个目录hello,并在这个目录中增加一个文件world,文件的内容为hello world。
/proc文件系统
/proc 文件系统是Linux上的一种虚拟文件系统,存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以更改其中某些文件来改变内核的运行状态。最初开发 /proc 文件系统是为了提供有关系统中进程的信息。但是由于这个文件系统非常有用,因此内核中的很多元素也开始使用它来报告信息或启用动态运行时配置。图 1 是对 /proc 中部分元素进行一次交互查询的结果。它显示的是 /proc 文件系统的根目录中的内容。注意,在左边是一系列数字编号的文件。每个实际上都是一个目录,表示系统中的一个进程。由于在 GNU/Linux 中创建的第一个进程是 init 进程,因此它的 process-id 为 1。然后对这个目录执行一个 ls 命令,这会显示很多文件。每个文件都提供了有关这个特殊进程的详细信息。/proc 中另外一些有趣的文件有:cpuinfo,它标识了处理器的类型和速度;pci,显示在 PCI 总线上找到的设备;modules,标识了当前加载到内核中的模块。
cmdline:系统启动时输入给内核命令行参数
cpuinfo:CPU的硬件信息 (型号, 家族, 缓存大小等)
devices:主设备号及设备组的列表,当前加载的各种设备(块设备/字符设备)
dma:使用的DMA通道
filesystems:当前内核支持的文件系统,当没有给 mount(1) 指明哪个文件系统的时候, mount(1) 就依靠该文件遍历不同的文件系统
interrupts :中断的使用及触发次数,调试中断时很有用
ioports I/O:当前在用的已注册 I/O 端口范围
kcore:该伪文件以 core 文件格式给出了系统的物理内存映象(比较有用),可以用 GDB 查探当前内核的任意数据结构。该文件的总长度是物理内存 (RAM) 的大小再加上 4KB
kmsg:可以用该文件取代系统调用 syslog(2) 来记录内核日志信息,对应dmesg命令
kallsym:内核符号表,该文件保存了内核输出的符号定义, modules(X)使用该文件动态地连接和捆绑可装载的模块
loadavg:负载均衡,平均负载数给出了在过去的 1、 5,、15 分钟里在运行队列里的任务数、总作业数以及正在运行的作业总数。
locks:内核锁 。
meminfo物理内存、交换空间等的信息,系统内存占用情况,对应df命令。
misc:杂项 。
modules:已经加载的模块列表,对应lsmod命令 。
mounts:已加载的文件系统的列表,对应mount命令,无参数。
partitions:系统识别的分区表 。
slabinfo:sla池信息。
stat:全面统计状态表,CPU内存的利用率等都是从这里提取数据。对应ps命令。
swaps:对换空间的利用情况。
version:指明了当前正在运行的内核版本。
可加载内核模块(LKM)是用来展示 /proc 文件系统的一种简单方法,这是因为这是一种用来动态地向 Linux 内核添加或删除代码的新方法。LKM 也是 Linux 内核中为设备驱动程序和文件系统使用的一种流行机制。如果你曾经重新编译过 Linux 内核,就可能会发现在内核的配置过程中,有很多设备驱动程序和其他内核元素都被编译成了模块。如果一个驱动程序被直接编译到了内核中,那么即使这个驱动程序没有运行,它的代码和静态数据也会占据一部分空间。但是如果这个驱动程序被编译成一个模块,就只有在需要内存并将其加载到内核时才会真正占用内存空间。
实验开始
实验环境
Linux version 5.15.0-generic (Ubuntu 9.4.0-1Ubuntu1~20.04.2)
实验步骤
打开Liunx虚拟机进行文件系统
进入computer,想快速进入/proc目录下,就进行搜索,注意要选择正确目录下的文件
1、创建目录
在Linux内核网址https://www.kernel.org/doc/html/latest/查阅相关文档可知,proc中创建目录的函数为proc_mkdir,该函数原型为:
struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent);
该函数接受两个参数,分别为要创建的目录的名称以及一个指向该目录的父目录的指针,并返回指向当前目录的指针结构体。
2、创建文件
考虑到在/proc目录下有version和softirqs等文件,且源码中有以上文件的实现,分别为version.c和softirqs.c,则可参考这两个文件的实现进一步实现创建world文件。显然,创建version文件的函数为proc_create,查阅文档可知,该函数原型为:static inline struct proc_dir_entry *proc_create(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops);该函数接收4个参数,第一个参数为文件名,第二个参数为文件的读写权限,第三个参数为其父目录的结构体指针,第四个参数为文件的读写操作结构体。
world文件的父目录指针即为先前创建hello目录所返回的指针,proc_fops定义了该文件所能执行的操作,由于本实验只要求读取文件内容,因此只实现了read属性
3.定义模块的初始化和清理函数
4.编写Makefile
实验代码
proc_hello.c文件
#include <linux/module.h>
#include <linux/kernel.h>
#include<linux/sched.h>
#include <asm/uaccess.h>
#include <linux/proc_fs.h>
static struct proc_dir_entry *proc_parent;
int len,temp;
char *msg;
static ssize_t read_proc(struct file *filp,char __user *buf,size_t count,loff_t *offp )
{
int a; //used to recept the value return from the function
if(count>temp)
count=temp;
temp=temp-count;
a=raw_copy_to_user(buf,msg, count);
if(count==0)
temp=len;
return count;
}
static struct proc_ops proc_fops={
.proc_read = read_proc
};
void create_new_proc_entry(void)
{
/*create a new directory named hello, and return a pointer point to this dir*/
proc_parent = proc_mkdir("hello",NULL);
if(!proc_parent)
{
printk(KERN_INFO "Error creating proc entry");
}
/*create a file named world, add read attribute to this file using proc_fops*/
proc_create("world",0,proc_parent,&proc_fops);
msg="hello world\n"; /*file content*/
len=strlen(msg);
temp=len;
printk(KERN_INFO "1.len=%d",len);
printk(KERN_INFO "proc initialized");
}
static int __init proc_init(void)
{
create_new_proc_entry();
return 0;
}
static void __exit proc_cleanup(void)
{
printk(KERN_INFO " Inside cleanup_module\n");
remove_proc_entry("hello",proc_parent);
remove_proc_entry("world",NULL);
}
MODULE_LICENSE("GPL");
module_init(proc_init);
module_exit(proc_cleanup);
Makefile
ifneq ($(KERNELRELEASE),)
obj-m := proc_hello.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
进行make编译
如果出现了以下错误
参考这篇文章并进行修改Linux关于proc_create函数的修改-优快云博客
再次进行make,这样子就是正确的了,编译成功
用insmod proc_hello.ko将文件编译进内核
lsmod进行查看