Linux内核模块_helloworld

本文介绍如何在Linux环境下配置内核源代码树,并基于此进行内核模块的开发。包括安装必要的编译工具、下载和编译内核源码、创建和加载简单的内核模块等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们平时使用的用户程序和驱动程序不一样,驱动程序作为一个模块连接到内核模块并运行在内核空间里。引用LDD上的一句话“因为2.6内核的模块要和内核源代码树中的目标文件连接,通过这种方式,可得到一个更加健壮的模块加载器,但是需要这些目标文件存在于内核目录树中”。这里提到的内核目录树就是我们在运行我们自己构造的模块前,需要在我们的系统中已经配置好内核源代码树,然后在把构造好的目标模块和内核树连接起来再运行。
查看自己的系统里有没有配置好内核树的方法:在/lib/modules/2.6.35-30-generic目录下面,看看有没有build文件夹,如果有的话,说明我们的系统里已经有内核树了,如果没有的话,就需要自己构建一个内核树了。

1.安装编译内核所需软件

sudo apt-get install build-essential kernel-package libncurses5-dev fakeroot

2.下载内核源码编译

apt-cache search linux-source

apt-get install Linux-source-2.6.35

tar jxvf linux-source-2.6.32.tar.bz2

sudo cp /boot/config-2.6.35-30-generic /usr/src/linux-source-2.6.35/.config

cd /usr/src/linux-source-2.6.35

make menuconfig

make 

make bzImage

make modules 

make modules_install

最终在/lib/modules目录下面产生一个目录


Makefile 文件,其中default和clean下面一行是以Tab键开头

#如果已经定义KERNELRELEASE,则说明是从内核构造系统调用的
#因此可利用其内建语句
ifneq ($(KERNELRELEASE),)
    obj-m := helloworld.o
    helloworld-objs += hello.o
    
#否则,是直接从命令行调用的
#这时要调用内核构造系统
else
    KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    PWD := $(shell pwd)
    
default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    
endif

clean:
	rm -rf *.o *.ko .*.cmd .tmp_versions Module.* modules.* *.mod.c


KERNELRELEASE是内核顶层Makefile定义的变量,首次执行本例的Makefile时KERNELRELEASE变量没有被定义,所以执行else分支;

-C $(KERNELDIR) 表示跳转到内核源代码目录执行那里的Makefile

M=$(PWD)表示然后返回到当前目录继续读入、执行当前的Makefile

当从内核源码目录返回后,KERNELRELEASE变量已经定义,执行if分支

linux内核makefile体系参考:

http://blog.youkuaiyun.com/artechtor/article/details/1424619


hellowork.ko

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>

static int namecount = 3;
static char* whom = "world";
static int howmany = 1;
static char* namelist[] = {"init1", "init2", "init3", "init4", "init5"};

static int __init hello_init(void)
{
    int i = 0;
    for(i = 0; i < howmany; i++)
    {
        printk(KERN_ALERT "Hello %s,index:%d\n", whom, i+1);
    }
    
    printk(KERN_ALERT "countof(namelist):%d\n", sizeof(namelist)/sizeof(char*));
    
    for(i = 0; i < namecount; i++)
    {
        printk(KERN_ALERT "Hello %s,index:%d\n", namelist[i], i+1);
    }
    return 0;
}

static void __exit hello_exit(void)
{
    printk(KERN_ALERT "Goodbye....\n");
    return;
}

module_init(hello_init);
module_exit(hello_exit);
module_param(howmany, int, 0);
module_param(whom, charp, S_IRUGO);
module_param_array(namelist, charp, &namecount, S_IRUGO);

MODULE_LICENSE("Dual BSD/GPL");

insmod helloworld.ko howmany=10 whom=kitty namelist=john,jack,cathy


内核支持的模块参数类型如下:

bool

invbool

    布尔值(取true或false),关联的变量应该是int型。invbool类型反转其值,也就是说,true值变成false,而false变成true。

charp

    字符指针指。内核会为用户提供字符串分配内存,并相应设置指针。

int

long

short

uint

ulong

ushort

    具有不同长度的基本整数值。以u开头的类型用于无符号数。


模块装载器也支持数组参数,在提供数组值时用逗号划分各数组成员。要声明数组参数,需要使用下面的宏:

module_param_array(name, type,num,perm);

其中,name是数组的名称(也就是模块参数的名称),type是数组元素类型,num是一个整数变量用于记录输入的数组元素个数,而perm是常见的访问许可值。

如果在装载时设置数组参数,则num会被设置为用户提供的值的个数。模块装载器会拒绝接受超过数组大小的值(本例中加载时设置namelist参数超过5个时加载失败)。


perm参数:这个值用来控制谁能访问sysfs中对模块参数的表述(在/sys/module/helloword/parameters目录下面有三个文件:whom/howmany/namelist分别对应三个模块参数,文件内容为模块加载时的参数值。)。如果perm值被设置为0,则不会有对应的sysfs入口项(如果whom参数的给标志被置为0,则在/sys/module/helloword/parameters目录下面没有whom文件);否则,模块参数会在/sys/module中出现,并设置为给定的访问许可。

S_IRUGO 在<linux/stat.h>中定义,如果一个参数通过sysfs被修改,则如果模块修改了这个参数的值一样,但是内核不会以任何方式通知模块。通常情况下,不应该设置模块参数为可写的,除非打算检测这种修改并作出相应的动作。


<think>好的,我现在需要帮助用户学习如何在Linux环境下编写和加载一个HelloWorld内核模块。首先,我需要回忆一下相关的知识,并结合提供的引用内容来构建答案。 用户的问题是关于编写和加载HelloWorld内核模块的示例代码教程。根据提供的引用,特别是引用[2]、[3]和[4],我可以提取出关键步骤:编写模块代码、编译模块、加载模块以及验证模块是否加载成功。 首先,编写模块代码。根据引用[2],内核模块需要至少包含初始化和退出函数,这两个函数分别对应模块加载和卸载时的操作。同时,引用[3]提到需要设置模块的许可证,比如MODULE_LICENSE("GPL")。所以,在代码中需要包含头文件linux/init.h和linux/module.h,定义init和exit函数,并使用module_init和module_exit宏来注册它们。另外,添加MODULE_LICENSE、MODULE_AUTHOR和MODULE_DESCRIPTION等宏来提供模块信息。 接下来是编译模块。通常需要编写一个Makefile,引用[4]提到可能需要配置内核代码,但这里用户只需要编译模块,所以Makefile应该指定内核的构建路径,使用obj-m来指定模块名称。编译命令是make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules。这需要用户确认是否安装了对应内核的头文件,如果没有的话可能需要安装linux-headers包。 然后是加载模块。使用insmod命令,如引用[1]所示,insmod用于插入模块到内核。加载后,可以用lsmod | grep helloworld来检查模块是否成功加载,如引用[2]中的示例。卸载模块使用rmmod命令,之后再次检查模块是否被移除。 最后是验证,可能需要使用dmesg来查看内核日志,因为模块的初始化和退出函数中使用了printk函数,这些日志会输出到内核环缓冲区,用户需要执行dmesg | tail来查看最新的日志信息。 在生成相关问题的时候,需要考虑用户可能遇到的后续问题,比如模块参数、日志查看、编译问题以及模块的用途等。这些问题需要结合常见的开发问题,例如如何传递参数给模块,如何调试模块的输出,如何处理编译时的依赖问题,以及模块的实际应用场景。 需要确保回答的结构清晰,分步骤讲解,并且代码示例正确无误。同时,要检查LaTeX语法是否正确,但在这个问题中可能用不到数学公式,所以主要注意代码块和引用标识的正确使用。引用用户提供的引用内容,确保每个引用都对应正确的来源,并在回答末尾列出所有引用。 可能需要注意的几点:确保Makefile中的路径正确,用户可能需要根据实际安装的内核版本调整路径;提醒用户使用sudo来执行insmod和rmmod,因为这些操作需要root权限;在printk中使用不同的日志级别,比如KERN_INFO,以便用户了解如何控制日志输出;检查代码中的函数名称和宏是否正确,比如module_init和module_exit的参数是否正确。 最后,生成的相关问题需要围绕HelloWorld模块的扩展知识,比如编译错误处理、添加参数、查看日志、内核模块的作用等,帮助用户进一步学习和解决问题。</think>### 如何在Linux中编写和加载HelloWorld内核模块 #### 1. 编写内核模块代码 创建一个文件`helloworld.c`,包含以下内容: ```c #include <linux/init.h> #include <linux/module.h> // 初始化函数 static int __init hello_init(void) { printk(KERN_INFO "Hello World! Module loaded.\n"); return 0; } // 退出函数 static void __exit hello_exit(void) { printk(KERN_INFO "Goodbye! Module unloaded.\n"); } // 注册模块函数 module_init(hello_init); module_exit(hello_exit); // 模块信息 MODULE_LICENSE("GPL"); MODULE_AUTHOR("YourName"); MODULE_DESCRIPTION("Simple HelloWorld kernel module"); ``` **说明** - `module_init`和`module_exit`分别定义模块加载和卸载时调用的函数[^2] - `MODULE_LICENSE`必须声明为GPL等兼容许可证[^3] #### 2. 编写Makefile 创建`Makefile`文件: ```makefile obj-m := helloworld.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: $(MAKE) -C $(KDIR) M=$(PWD) clean ``` #### 3. 编译模块 执行以下命令: ```bash make ``` **注意** - 需提前安装内核头文件(如`linux-headers-$(uname -r)`)[^4] #### 4. 加载/卸载模块 ```bash # 加载模块 sudo insmod helloworld.ko # 查看模块 lsmod | grep helloworld # 卸载模块 sudo rmmod helloworld # 查看内核日志 dmesg | tail -n 2 ``` **输出示例** ``` [ 1234.567890] Hello World! Module loaded. [ 1234.567891] Goodbye! Module unloaded. ``` #### 5. 验证流程 $$ \text{编写} \rightarrow \text{编译} \rightarrow \text{加载} \rightarrow \text{日志验证} \rightarrow \text{卸载} $$
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值