内容一:编写一个内核模块,在/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:指明了当前正在运行的内核版本。
内容二:添加新的系统调用。
1、系统调用号在unistd.h文件中定义。内核中每个系统调用号都以__NR_mysyscall开头,例如,fork的系统调用号为__NR_fork。于是,我们的系统调用号为__NR _mysyscall。具体在arch/x86/include/asm/ unistd_32.h和/usr/include/asm/unistd_32.h文件中添加如下:
...
#define __NR_dup3 330
#define __NR_pipe2 331
#define __NR_inotify init1 332
#define __NR_mysyscall 333/* mysyscall系统调用号添加在这里*/
添加系统调用号后,系统才能把这个号作为索引去查找系统调用表sys_call_table中的相应表项。
Include. uapi/asm-generic/unistd.h
2、在系统调用表中添加相应表项
如前所述,系统调用处理程序system_call会根据EAX中的号到系统调用表sys_call_tabe中查找相应的系统调用服务例程,因此,必须把服务例程sys_mysyscall派加到系统调用表中。系统调用表位于汇编语言arch/x86/kernel/ syscall_table_32.S中:找不到了。
内容三:Linux操作系统原理与应用教材上的第八章 例8-1
查看超级块super_block数据结构中的数据,并分析超级块与分区、文件系统、索引节点的关系。
内容一:编写一个内核模块,在/proc文件系统中增加一个目录hello,并在这个目录中增加一个文件world,文件的内容为hello world的步骤:
(1)首先获取内核源码:
浏览器输入网址:http://mirrors.aliyun.com/linux-kernel/v4.x/linux-4.2.6.tar.xz——下载内核源码
(2)终端控制台输入以下命令:
xz –d *.tar.xz——将内核源码解压为tar格式
tar –xvf *.tar——将内核源码解压至当前目录
(3)通过函数proc_mkdir创建Hello目录;
(4)通过函数proc_create创建World文件;
(5)定义模块proc_hello的初始化和清理函数;
(6)编写Makefile;
(7)程序的运行与验证。
内容二:
添加系统调用的步骤:
1、实验前的准备操作
sudo apt-get update //更新系统源码
sudo apt-get install libncurses5-dev libssl-dev //下载依赖包
sudo apt-get install build-essential openssl
sudo apt-get install zlibc minizip
sudo apt-get install libidn11-dev libidn11
sudo apt-get install flex
sudo apt-get install bison
2、添加系统调用号。系统调用号在 unistd.h文件中定义,以“__NR_”开头;
3、在系统调用表(在 entry.S中)中添加自己的服务例程 sys_coutname;
在/linux-4.2.6/arch/x86/entry/syscalls/syscall_64.tbl中条加服务例程:
4、实现系统调用服务例程:把 sys_coutname加在 kernel目录下的系统调用文件sys.c中;
在/linux-4.2.6/arch/x86/include/asm/syscalls.h中添加如下函数声明:
在/linux-4.2.6/kernel中添加如下函数:
5、重新编译内核;
在终端命令行分别输入下面的命令:
sudo make mrproper
sudo make clean
sudo make menuconfig
sudo make -j12(j后面的数字表示核数,核数越大,编译越快)
sudo make modules_install
sudo make install
完成后,reboot即可。
6、编写用户态程序。
内容三:
Linux操作系统原理与应用教材上的第八章 例8-1
1、查看变量的地址;
sudo cat /proc/kallsyms | grep super_blocks
sudo cat /proc/kallsyms | grep sb_lock
2、编译模块;
make
3、将模块插入内核中;
sudo insmod printValue.ko
4、查看系统消息;
sudo dmesg
5、从内核中移除模块。
sudo rmmod printValue
实验结果:
内容一:
内容二:
内容三:查看超级块super_block数据结构中的数据
实验分析:
(1)超级块与分区是一对一还是一对多的关系?超级块与文件系统是什么关系?
答:超级块与分区是一对一的关系;超级块与文件系统是一对一关系,一个超级块对应一个文件系统。
(2)超级块与索引结点是什么关系?
答:超级块与索引结点是一对多的关系。超级块是对一个文件系统的描述,索引结点是对一个文件物理属性的描述。超级块是我们寻找索引结点的唯一源头。操作文件必然需要获得其对应的索引结点,而获取索引结点是通过超级块操作表提供的read_inode()函数完成的。操作索引结点的低层次任务,如创建一个索引结点、释放一个索引结点,也是通过超级块操作表提供的有关函数完成的。
通过这次实验,我理解Linux虚拟文件系统的内容,学会如何在虚拟文件系统/proc中实现文件操作;理解什么是超级块、文件系统、分区和索引节点及它们之间的关系;学会如何添加新的系统调用,如何重新编译内核,如何进行系统调用的测试。
1、在终端窗口命令行执行命令sudo make -j12(j后数字代表核数,数字越大编译越快)时,出现下图错误:
查阅资料得知:
2、在终端窗口命令行执行命令sudo make出现下图错误:
解决方法:
3、在终端窗口命令行执行命令sudo make出现下图错误:
无解决方法:主要问题就是Ubuntu 20.04版本编译4.2版本内核的不兼容问题,因为20.04已经开始内置5.x的内核。
博客网址:linux编译内核出错-CSDN论坛
4、在终端窗口命令行执行命令sudo menuconfig出现下图错误:
解决方法:在终端窗口命令行分别输入命令sudo apt-get install bison、sudo apt-get install flex。
5、在终端窗口命令行执行命令sudo make -j12出现下图错误:
解决方法:原因可能是gcc-9的一些更改出现了bug,重新安装一遍gcc-8。
6、在终端窗口命令行执行命令sudo make -j12 出现下图错误:
解决方法:终端窗口命令行分别输入
1、cd linux-5.2.6
2、vi .config
3、输入/CONFIG_SYSTEM_TRUSTED_KEYRING找到相应的地方
4、删除 debian/canonical-certs.pem
5、按下Esc键,输入:wq!保存
#include
#include
#include
#include
#include
#include
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) {
//该函数将文件内容通过msg复制给buf,以此实现文件内容的读取。
//read_proc为实现读取文件内容的函数指针
if (count > temp)
count = temp;
temp = temp - count;
raw_copy_to_user(buf, msg, count);
if (count == 0)
temp = len;
return count;
}
static const struct file_operations proc_fops = {
//赋予文件world只读的属性
.read = read_proc
};
void create_new_proc_entry(void) {
//create a new directory named hello, and return a pointer point to this dir
//创建一个名为hello的新目录,并返回指向该目录的指针
proc_parent = proc_mkdir("hello", NULL); //创建一个目录hello
if (!proc_parent) //创建目录失败
printk(KERN_INFO "Error creating proc entry");
//create a file named world, add read attribute to this file using proc_fops
//创建一个名为world的文件,使用proc_fops将read属性添加到此文件
//world文件的父目录指针即为先前创建hello目录所返回的指针
proc_create("world", 0, proc_parent, (const struct proc_ops*)&proc_fops); //第一个参数为文件名,第二个参数为文件的读写权限,第三个参数为其父目录的结构体指针,第四个参数为文件的读写操作结构体。
msg = "hello world\n"; //文件内容
len = strlen(msg); //获得字符串msg的长度
temp = len;
printk(KERN_INFO "1.len = %d", len);
printk(KERN_INFO "proc initialized");
}
int proc_init(void) { //模块的初始化函数
create_new_proc_entry();
return 0;
}
void proc_cleanup(void) { //模块的退出和清理函数
printk(KERN_INFO " Inside cleanup_module\n");
remove_proc_entry("hello", proc_parent); //移除目录hello
remove_proc_entry("world", NULL); //移除文件world
}
module_init(proc_init); //向内核注册模块提供新功能
module_exit(proc_cleanup); //注销由模块提供所有的功能
MODULE_LICENSE("GPL"); //模块具有GUN公共许可证
ifneq ($(KERNELRELEASE),)
obj-m := proc_hello.o #产生proc_hello模块的目标文件
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
CONFIG_MODULE_SIG=n
#shell uname -r: Linux内核源代码的当前版本
#PWD: 模块所在的当前路径
#include
#include
#include
int main() {
unsigned long sys_num = 323;
unsigned long value = 0;
__asm__("int $0x80"
:"=a"(value)
:"0"((long)(sys_num)));
//syscall((long)(323));
printf("The value is %ld\n", value);
return value;
}
ifneq ($(KERNELRELEASE),)
obj-m := proc_hello.o #产生proc_hello模块的目标文件
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
CONFIG_MODULE_SIG=n
#shell uname -r: Linux内核源代码的当前版本
#PWD: 模块所在的当前路径