Linux内核模块编程指南(一)(转)

  当第一个原始的程序员在最开始的窑洞计算机之墙上凿过第一个程序时,那是一个在羚羊图案上画上“Hello, world”的程序。罗马人的编程书籍上用“Salut, Mundi”程序开始。我不知道打破这个传统的人身上发生了什么而且我想不去追究这个比较安全。

  一个内核模块必须至少有两个功能: init_module 在该模块被插入内核时被调用, cleanup_module 仅仅在它被清除前调用。 典型的, init_module 要么在内核里为什么东西登记一个指针,要么用它自己的代码代替内核的某个功能 (通常那个代码做一些事情然后调用原始的功能). cleanup_module 功能被假定撤消init_module 做的任何事情, 因此模块可以被安全地卸载。

  范例 hello.c

  /* hello.c

* Copyright (C) 1998 by Ori Pomerantz** "Hello, world" - 内核模块版本.*//* 必要的头文件 *//* 内核模块标准 */#include /* 我们在做内核的工作 */#include /* 明确的,一个模块 *//* 处理 CONFIG_MODVERSIONS */#if CONFIG_MODVERSIONS==1#define MODVERSIONS#include#endif/* 初始化模块 */int init_module(){printk("Hello, world - this is the kernel speaking ");/* 如果我们返回一个非零值, 那就意味着* init_module 初始化失败并且内核模块* 不能加载 */return 0;}/* Cleanup - 撤消 init_module 所做的任何事情 */void cleanup_module(){printk("Short is the life of a kernel module ");}

  内核模块的Make文件

  一个内核模块单独是不可执行的,但目标文件在运行时被连接进内核。因此,在编译时要使用 -c 标记. 而且, 所有的内核模块必须结合特定的定义过的符号进行编译。

  __KERNEL__ -- 这个符号告诉头文件这些代码将在内核模式运行,不要当作用户进程的一部分。

  MODULE -- 这个符号告诉头文件为内核模块给出适当的定义。

  LINUX -- 从技术上说这不是必要的。然而,如果你曾经想过写一系列要在不止一个的操作系统 上编译的内核模块,那么你将为你在这所做的高兴。这将允许你条件编译平台独立性的部分。

  还有其他一些需要或不需要包括的符号,这取决于内核用什么标记编译。如果你不能确定内核是如何编译的,请查看 /usr/include/linux/config.h

  __SMP__ -- Symmetrical MultiProcessing(对称多处理). 如果内核被编译为支持对称多处理 (即使它仅仅运行于单CPU上),这个符号必须包含.如果你使用对称多处理,你还有很多其他的事情需要 做(参看第12章).

  CONFIG_MODVERSIONS -- 如果CONFIG_MODVERSIONS 被激活, 你需要使它在编译内核模块时已定义 并且包含/usr/include/linux/modversions.h. 这也可以比、被代码自己完成。

  范例 Makefile

  # 为基本的内核模块写的Make文件

  CC=gcc

MODCFLAGS := -Wall -DMODULE -D__KERNEL__ -DLINUXhello.o: hello.c /usr/include/linux/version.h$(CC) $(MODCFLAGS) -c hello.cecho insmod hello.o to turn it onecho rmmod hello to turn if offechoecho X and kernel programming do not mix.echo Do the insmod and rmmod from outside X.

  因此,现在唯一剩下的事情就是su 为root用户 (你不能作为root用户编译,对吗? 生活在边缘1.1...), 然后insmod hello 和 rmmod hello 到你的系统核心. 当你完成这个,注意 /proc/modules中的新内核模块。

  顺便说一下,之所以推荐不要在 X下做insmod是因为当内核用printk打印消息时它将消息发送到控制台。当你不使用 X, 它就发往你正在使用的虚拟终端(你用Alt-F选定的那个) 而使你可以看见。另一方面,当你使用 X, 有两种可能。要么你有一个用xterm -C打开的终端,在这种情况下输出将被发送到那儿;或者你没有打开终端,在这种情况下输出被发往被X“隐蔽”的虚拟终端7。

  如果你的内核变得不稳定,不用X得到调试信息更有可能。在X之外printk 直接从内核打印到控制台。另一方面,在X下,printk变成用户模式进程 (xterm -C). 当该进程正占用CPU,它被假设发往X服务进程,然后,当X服务进程占用 CPU, 它被假设去显示该信息 --但是一个不稳定的内核通常意味着崩溃或重新启动然而你不想延迟得到错误信息,这也许可以向你解释什么地方出错了。

  多文件内核模块

  有时候将内核模块分为几个源文件是有意义的,你需要做下面的事情:

  1. 除了一个文件外在所有的源文件中加入一行 #define __NO_VERSION__. 这是很重要的,因为module.h通常包含kernel_version的定义, 它是一个和模块一起编译的内核版本的全局变量。如果你需要version.h, 你需要自己包含它,因为module.h 在有 __NO_VERSION__的定义的情况下不自动包含它。

  2. 像通常那样编译所有的源文件。

  3. 将所有的目标文件合并成一个。在x86架构下使用 ld -m elf_i386 -r -o < 模块名>.o .o

  这里有一个这样的内核模块的范例。

  范例 start.c

  /* start.c

* Copyright (C) 1999 by Ori Pomerantz** "Hello, world" - 内核模块版本.* 这个文件只包含启动程序*//* 必要的头文件 *//* 内核模块标准头文件 */#include /* 我们正在做内核的工作 */#include /* 明确的指定是内核模块 *//* 处理 CONFIG_MODVERSIONS */#if CONFIG_MODVERSIONS==1#define MODVERSIONS#include#endif/* 初始化模块 */int init_module(){printk("Hello, world - this is the kernel speaking ");/* If we return a non zero value, it means that* init_module failed and the kernel module* can't be loaded */return 0;}

  范例 stop.c

  /* stop.c

* Copyright (C) 1999 by Ori Pomerantz** "Hello, world" - 内核模块版本* 这个文件只包含终止程序*//* 必要的头文件 *//* 内核模块的标准头文件 */#include /* 我们正在做内核的工作 */#define __NO_VERSION__ /* 这不是内核模块文件 */#include /* 明确的指定是内核模块 */#include /* 因为有 __NO_VERSION__ 而不能被自动包含*//* 处理 CONFIG_MODVERSIONS */#if CONFIG_MODVERSIONS==1#define MODVERSIONS#include#endif/* Cleanup - 撤消init_module 所做的任何事情 */void cleanup_module(){printk("Short is the life of a kernel module ");}

  范例 Makefile

  # 多文件内核模块的Make文件

  CC=gcc

MODCFLAGS := -Wall -DMODULE -D__KERNEL__ -DLINUXhello.o: start.o stop.old -m elf_i386 -r -o hello.o start.o stop.ostart.o: start.c /usr/include/linux/version.h$(CC) $(MODCFLAGS) -c start.cstop.o: stop.c /usr/include/linux/version.h$(CC) $(MODCFLAGS) -c stop.c

  多内核版本源文件

  内核展现给进程的主要界面是系统调用,它通常跨版本保持相同。新的系统调用被加入,但通常老的保持和原来严格的一样。这对于向后兼容性是必要的--新的内核版本不应打破常规的进程。在大多情况下,设备文件也将保持相同。另一方面,和内核内部的接口可以并且在版本之间有改变。

  Linux内核版本分为稳定版 (n..m) 和开发版 (n..m).开发版包含所有的好的新思想,包括那些被认为是错误或需要在下一版重新实现的东西。结果,你不能期盼在那些版本中界面保持相同(这也是我为什么不在这本书中操心去支持它的原因,那需要太多的工作并且很快就过时了)。另一方面,在稳定版中我们可以期盼界面保持相同,除了错误修订版(数字m)。

  这个版本的内核模块编程指南包括对 2.0.x 和2.2.x 内核版本的支持。既然这两个版本间有差异,这就需要根据版本进行条件编译。可以使用宏 LINUX_VERSION_CODE来做这件事。在 a.b.c 版的内核中,这个宏的值是 216a+28b+c。为了 得到某个内核版本的值,我们可以使用 KERNEL_VERSION 宏. 因为在 2.0.35版中没有定义它, 我们可以在必要的时候自己定义它。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/14102/viewspace-116392/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/14102/viewspace-116392/

Linux内核模块编程指南(一)(转)相关推荐

  1. Linux 内核编程指南

    Linux 内核编程指南   PeterJay Salzman MichaelBurian OriPomerantz Copyright© 2001 Peter Jay Salzman 2007−05 ...

  2. Linux内核模块编程系列1-极简内核模块编写

    1.准备工作 使用如下命令查看自己Linux的内核版本 uname -a 结果如下: Linux VM-73-203-debian 4.9.0-6-amd64 #1 SMP Debian 4.9.88 ...

  3. 关于linux内核模块编程时,多个源代码文件Makefile书写的问题

    本文转自:https://blog.csdn.net/u010560290/article/details/44647683 在学习内核模块编程的时候遇到了一些由于Makefile书写不正确导致的问题 ...

  4. 杭电操作系统实验二 --- Linux 内核模块编程(arm架构华为云)

    一.题目介绍 掌握Linux 内核模块的基本概念 ·阅读教材7.3.2节Linux内核模块简介 ,网上查找资料,了解Linux内核模块的基本概念. ·阅读教材7.3.3内核模块编程基础 ,网上查找资料 ...

  5. Linux内核模块编程入门

    针对2.6内核的Linux系统,需要你的机器上已经安装了kernel-devel这个包,也就是编译模块所必须的东西:内核的头文件和一些Makefile. 一,Hello World程序: [code: ...

  6. linux内核模块编写,Linux内核模块编程

    1 总体设计思路 Linux内核是单体式结构,相对于微内核结构而言,其运行效率高,但是系统的可维护性和可扩展性较差.为此,Linux提供了内核模块(module)机制,它不仅可以弥补单体式内核相对于微 ...

  7. 《linux c编程指南》学习手记1

    第一章 c语言基础 linux概述 虚拟文件系统VFS 进程通信机制:管道,信号   消息队列 信号灯 共享内存 第二章 vim与emacs 两种主流编辑器:vim emacs vi 的含义 visu ...

  8. 杭电(杭州电子科技大学)操作系统实验二:Linux内核模块编程

    实验内容 (1)设计一个模块,要求列出系统中所有内核线程的程序名.PID.进程状态.进程优先级.父进程的PID. (2)设计一个带参数的模块,其参数为某个进程的PID号,模块的功能是列出该进程的家族信 ...

  9. linux内核模块编程(六)----字符设备驱动中断开发

    先给自己打个广告,本人的微信公众号正式上线了,搜索:张笑生的地盘,主要关注嵌入式软件开发,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题 一 why 字符设备驱动在我们 ...

最新文章

  1. tiny4412学习之u-boot启动过程
  2. 树莓派配置路由_树莓派安装Ubuntu 20.04 LTS并配置
  3. Cisco 3640策略路由配置
  4. 2021夏季每日一题 【week5 完结】
  5. java settings文件夹_windows下打开.m2文件夹,没有找到setting.xml
  6. 读书笔记--《MicroPython入门指南》
  7. 20145309 《网络对抗技术》信息搜集与漏洞扫描
  8. CoreAnimation-CABasicAnimation
  9. 【Pro ASP.NET MVC 3 Framework】.学习笔记.4.MVC的主要工具-使用Moq
  10. Linux下Socket编程的端口问题( Bind error: Address already in use )
  11. Atitit insert插入数据 目录 1.1. INSERT INTO SET这种方式可读性更好 1 1.1.1. 方式4、INSERT INTO 表名 SET 列名1 = 列值1,列名2=列值
  12. 【数字信号处理】基于matlab GUI IIR低通+FIR高通信号时域+频谱分析【含Matlab源码 1029期】
  13. CSS-背景 超链接
  14. 百度地图api之路线规划
  15. 选择性粘贴HTML,Excel中“选择性粘贴”的五种特殊用法
  16. 麦子学院C++学习笔记
  17. android获取安卓版本,怎么获取android系统当前版本
  18. Atitit 管理的模式扁平化管理 金字塔 直线型管理 垂直管理 水平管理 矩阵式管理 网状式样管理 多头管理 双头管理...
  19. 通过路由器访问光猫(openwrt)
  20. Flink State 深度讲解

热门文章

  1. java中的public void_public void什么意思
  2. 基于FME的地形图图面压盖检查工具的设计与制作
  3. 一百行写一个2048
  4. 怎么把线稿提取出来_用ps如何提取线稿图?简单教程轻松搞定
  5. java实现下雪雪花飘落并堆积效果
  6. Oracle(11g)数据库安装详细图解教程
  7. Oracle逻辑读,物理读
  8. AnySDK吉祥物征名活动开始啦!
  9. 管理计算机域的内置账户怎么取消,如何删除供来宾访问计算机或访问域的内置账?...
  10. [Unity]利用Mesh在Unity中绘制扇形图片2