Linux内核模块化编程

        每个进程在启动的时候会做两件事 ,虚拟出来一个4G大小的内存空间,会打开三个标准的文件描述符。

        内存空间分为两部分:

                0-3G  用户空间  之前系统阶段所写的代码运行在用户空间。

                3G-4G 内核空间 驱动阶段所写的代码运行在内核空间。

 

 

        内核可以通过接口直接访问硬件,用户一般是不能直接操作硬件的,内核层代码是由一个新的框架的,这个框架就是我们今天介绍的。

一、模块化编程

1.内核层代码的框架

内核层的代码必须要包含的头文件

#include<linux/kernel.h>

#include<linux/module.h>
内核代码没有main函数的  内核代码的入口函数   

加载函数

static int __init myled_init(void)

{

        return 0;

}

卸载函数

static void __exit myled_exit(void)

{

}

有了头文件和加载卸载函数之后并不能马上使用

 要想使用内核代码,我们还需要加上对应的声明

声明  加载函数

module_init(myled_init)

声明  卸载函数

module_exit(myled_exit)

声明  遵循开源协议

MODULE_LICENSE(“GPL”)

2.内核层代码示例

在内核层要想打印提示信息可以使用 printk 跟printf用法是一样

#include<linux/kernel.h>
#include<linux/module.h>


static int __init myled_init(void)
{
	printk("this is my first driver code\n");
	return 0;
}


static void __exit myled_exit(void)
{
	printk("i am myled exit\n");
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

3.内核层代码的编译

要想编译内核的源码  我们需要了解如下两点

1.内核的源码运行在arm架构的开发板上

        我们写代码时候用的是  x86架构,编译的时候就需要借助于交叉编译工具。

2.编译后的代码是运行在linux3.5的内核之下的

        编译的时候就要借助于linux3.5的内核

3.了解makefile

obj-m += led.o    #表示我们要新增的模块对应.o文件

KDIR:=/home/duan/work/20230620202/kernel/linux-3.5   #依赖的内核的路径

all:
    make -C $(KDIR) M=$(PWD) modules  #

clean:
    rm -rf *.ko *.o  *.symvers *.order *.mod.c

        $(MAKE) -C $(KDIR) M=$(PWD) modules
        1) -C $(KDIR)
        表示在$(KERNELDIR)目录下执行make命令。
        2) M=$(PWD)
        表示包含$(PWD)下的Makefile文件。
        3) modules
        表示模块编译。

4.编译后的代码的使用

通过make的命令  最终生成了一个文件  led.ko,这个文件就是内核的模块,可以用来加载和卸载。加载的时候就会调用到加载函数,卸载的时候就会调用到卸载函数,模块的加载和卸载  就需要借助于两个命令。

         insmod :加载模块

         rmmod :卸载模块

例如:

        insmod  led.ko

        rmmod  led.ko

二、多模块编程

 1.多个模块相互调用

        被调用的函数所在的模块  要对被调用的函数进行一个声明,告诉内核 整个内核的所有模块都可以调用这个函数,被调用函数所在模块示例代码。以下代码仅用于测试、熟悉多模块间相互调用,熟悉框架,了解代码编写过程,并无实际应用。

led.c文件:

#include<linux/kernel.h>
#include<linux/module.h>
void myfunc(void)
{
	printk("i am a led func\n");
}

static int __init myled_init(void)
{
	printk("i am led init\n");
	return 0;
}

static void __exit myled_exit(void)
{
	printk("i am led exit\n");
}
EXPORT_SYMBOL(myfunc);
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

调用函数的代码示例

key.c文件

#include<linux/kernel.h>
#include<linux/module.h>

void myfunc(void);


static int __init mykey_init(void)
{
	printk("i am key init\n");
	myfunc();
	return 0;
}

static void __exit mykey_exit(void)
{
	printk("i am key exit\n");
}

module_init(mykey_init);
module_exit(mykey_exit);
MODULE_LICENSE("GPL");

        加载模块的时候 要先加载被调用的函数所在的模块 例如 加载led.ko

        卸载的时候  要先卸载调用其他模块的函数的模块  例如  先卸载key.ko

2.多个文件编译成一个模块

        这种情况就类似于C语言里的一个入口函数调用别的文件里函数。

1.c:

#include<linux/kernel.h>
#include<linux/module.h>
void func(void);
void myfunc(void);

static int __init myled_init(void)
{
	printk("i am my led init\n");
	myfunc();
	func();
	return 0;
}

static void __exit myled_exit(void)
{

	printk("i am my led exit\n");
}

module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

2.c

#include<linux/kernel.h>
#include<linux/module.h>


void myfunc(void)
{
	printk("i am  my func\n");
}

3.c

#include<linux/kernel.h>
#include<linux/module.h>


void func(void)
{
	printk("i am func\n");
}

Makefile:

obj-m += led.o
led-objs := 1.o 2.o 3.o
KDIR:=/home/duan/work/20230620202/kernel/linux-3.5 #依赖的内核的路径

all:
	make -C $(KDIR) M=$(PWD) modules
clean:
	rm -rf *.ko *.o  *.symvers *.order *.mod.c	

调用结果:

 3.模块化传参

        类似于C语言的主函数传参

led.c:

#include<linux/kernel.h>
#include<linux/module.h>

int tmp;

static int __init myled_init(void)
{
	printk("tmp=%d\n",tmp);
	return 0;
}

static  void __exit myled_exit(void)
{

}

module_param(tmp,int,0744);
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

模块传参调用过程及结果:

 

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值