linux内核模块编程入门
这是我第一次写博客,没想到竟然是关于linnux的。我写博客的目的很简单,就是为了记录我自己的学习历程。等以后翻出来看的时候觉得是一种成长,一种收获。网上有人称“每一天都觉得昨天的自己是个傻逼,就说明你在进步”,我的期望就是这样的。行了,废话不多了,来看模块编程吧!
一.什么是模块?
模块是具有独立功能的程序,它可以被单独编译但是不能独立运行。模块在运行时被连接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的。经常用来实现驱动程序,实现一种文件系统或其他内核上层的功能。
模块本身不会被编译进内核映像,从而控制了内核的大小。模块一旦被加载就和内核中的其他部分完全一样。模块编程和内核版本有很大的关系。
二.用户层编程和模块编程的区别
下面这个表不是自己的,是从别处搜来的,自己理解了,给大家分享一下
用户程序 内核模块程序
使用函数 lib库 内核函数
运行空间 用户空间 内核空间
运行权限 普通用户 超级用户
入口函数 main() module_init
出口函数 exit() module_exit
编译 gcc make
运行 直接运行 插入模块
三.编写内核模块
1.头文件的写法
#include<linux/module.h> //module.h头文件中包含了对模块结构的定义和模块的版本信息,任何一个内核模块编写都要包含这个头文件
#include<linux/init.h> //init.h包含了宏_init和宏_exit,_init告诉编译程序相关的函数和变量仅用于初始化,编译程序将标有_init的代码存储在特殊的内存段中,初始化结束后释放这段内存
#include<linux/kernel.h> //包含了常用的内核函数
有时候还需要添加一些其他的头文件。这个根据你函数中用到的程序有关。
2.编写内核模块必须用到两个函数,注册函数和卸载函数
注册函数的写法
static int __init fun_init(void) fun_init是入口函数的名字,是注册函数的函数名
{
}
卸载函数的写法
static void __exit fun_exit(void) fun_exit是出口函数的名字,是卸载函数的函数名。注意函数的返回值为空
{
}
3.在内核中,打印输出使用printk()这个内核函数,与用户程序有差别
三.Makefile的编写
1.Makefile是一种脚本,这个脚本用于多文件的编译
2.make程序只对改变了的程序进行重新编译。
关于make请大家查看相关资料,太多了写不完。
四。实例
注意,内核模块的编译和编写都与用户程序有差别,编译使用的make而不是gcc 编写的程序使用的是内核函数而不是库函数,请大家注意
本来想举一个打印输出进程pid号的内核模块编程呢,这是这会儿太晚了还是hello world吧!
写一个内核程序,起名hello.c
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
static int __init hello_init(void)
{
printk("<1>hello,world,from the kernel space...\n");
return 0;
}
static void __exit hello_exit(void)
{
printk("bye,from the kernel space\n");
}
module_init(hello_init); // 该函数告诉内核你编写的程序是从哪里开始的
module_exit(hello_exit); // 离开
MODULE_LICENSE("GPL"); // 告诉内核该模块有GUN公共许可
2.makefile
obj-m:=hello.o
CURRENT_PATH:=$(shell pwd)
LINUX_KERNEL:=$(shell uname -r)
LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL) //这个是fedora的绝对路径表示
all:
make -C $(LINUX_KERNEL_PATH) M =$(CURRENT_PATH) modules
clean:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean
3.使用make命令进行编译,编译完以后生成了六个文件总共八个文件。然后插入内核模块,我们把最可爱的hello.ko插入内核。(root下才行)使用命令insmod hello.ko,插入后使用lsmod查看是否插入,然后再用dmesg就可以查看打印输出的结果hello,world!在使用rmmod hello.ko删除模块,并且使用dmesg查看打印输出的结果。printk()的打印结果在内核,用户层看不到,所以需要使用dmesg.
我也是初学者,菜鸟一个,有问题请指正