精华内容
下载资源
问答
  • Linux module 原理与实现

    2008-10-20 09:15:00
    知道在什么时候,Linux 出现了 module 这种东西,的确,它是 Linux 的一大革新。有了 module 之后,写 device driver 不再是一项恶梦,修改 kernel 也不再是一件痛苦的事了。因为你不需要每次要测试 driver 就重新 ...

        知道在什么时候,Linux 出现了 module 这种东西,的确,它是 Linux 的一大革新。有了 module 之后,写 device driver 不再是一项恶梦,修改 kernel 也不再是一件痛苦的事了。因为你不需要每次要测试 driver 就重新 compile kernel 一次。那简直是会累死人。Module 可以允许我们动态的改变 kernel,加载 device driver,而且它也能缩短我们 driver development 的时间。在这篇文章里,我将要跟各位介绍一下 module 的原理,以及如何写一个 module。

      module 翻译成中文就是模块,不过,事实上去翻译这个字一点都没意义。在讲模块之前,我先举一个例子。相信很多人都用过 RedHat。在 RedHat 里,我们可以执行 sndconfig,它可以帮我们 config 声卡。config 完之后如果捉得到你的声卡,那你的声卡马上就可以动了,而且还不用重新激活计算机。这是怎幺做的呢 ? 就是靠module。module 其实是一般的程序。但是它可以被动态载到 kernel 里成为 kernel的一部分。载到 kernel 里的 module 它具有跟 kernel 一样的权力。可以 access 任何 kernel 的 data structure。你听过 kdebug 吗 ? 它是用来 debug kernel 的。它就是先将它本身的一个 module 载到 kernel 里,而在 user space 的 gdb 就可以经由跟这个 module 沟通,得知 kernel 里的 data structure 的值,除此之外,还可以经由载到 kernel 的 module 去更改 kernel 里 data structure。

      我们知道,在写 C 程序的时候,一个程序只能有一个 main。Kernel 本身其实也是一个程序,它本身也有个 main,叫 start_kernel()。当我们把一个 module 载到 kernel 里的时候,它会跟 kernel 整合在一起,成为 kernel 的一部分。请各位想想,那 module 可以有 main 吗 ? 答案很明显的,是 No。理由很简单。一个程序只能有一个 main。在使用 module 时,有一点要记住的是 module 是处于被动的角色。它是提供某些功能让别人去使用的。

      Kernel 里有一个变量叫 module_list,每当 user 将一个 module 载到 kernel 里的时候,这个 module 就会被记录在 module_list 里面。当 kernel 要使用到这个 module 提供的 function 时,它就会去 search 这个 list,找到 module,然后再使用其提供的 function 或 variable。每一个 module 都可以 export 一些 function 或变量来让别人使用。除此之外,module 也可以使用已经载到 kernel 里的 module 提供的 function。这种情形叫做 module stack。比方说,module A 用到 module B 的东西,那在加载 module A 之前必须要先加载 module B。否则 module A 会无法加载。除了 module 会 export 东西之外,kernel 本身也会 export 一些 function 或 variable。同样的,module 也可以使用 kernel 所 export 出来的东西。由于大家平时都是撰写 user space 的程序,所以,当突然去写 module 的时候,会把平时写程序用的 function 拿到 module 里使用。像是 printf 之类的东西。我要告诉各位的是,module 所使用的 function 或 variable,要嘛就是自己写在 module 里,要嘛就是别的 module 提供的,再不就是 kernel 所提供的。你不能使用一般 libc 或 glibc所提供的 function。像 printf 之类的东西。这一点可能是各位要多小心的地方。(也许你可以先 link 好,再载到 kernel,我好象试过,但是忘了)

      刚才我们说到 kernel 本身会 export 出一些 function 或 variable 来让 module 使用,但是,我们不是万能的,我们怎么知道 kernel 有开放哪些东西让我们使用呢 ? Linux 提供一个 command,叫 ksyms,你只要执行 ksyms -a 就可以知道 kernel 或目前载到 kernel 里的 module 提供了那些 function 或 variable。底下是我的系统的情形:

    c0216ba0 drive_info_R744aa133
    c01e4a44 boot_cpu_data_R660bd466
    c01e4ac0 EISA_bus_R7413793a
    c01e4ac4 MCA_bus_Rf48a2c4c
    c010cc34 __verify_write_R203afbeb
    . . . . .

      在 kernel 里,有一个 symbol table 是用来记录 export 出去的 function 或 variable。除此之外,也会记录着哪个module export 那些 function。上面几行中,表示 kernel 提供了 drive_info 这个 function/variable。所以,我们可以在 kernel 里直接使用它,等载到 kernel 里时,会自动做好 link 的动作。由此,我们可以知道,module 本身其实是还没做 link 的一些 object code。一切都要等到 module 被加载 kernel 之后,link 才会完成。各位应该可以看到 drive_info 后面还接着一些奇怪的字符串。_R744aa133,这个字符串是根据目前 kernel 的版本再做些 encode 得出来的结果。为什幺额外需要这一个字符串呢 ?

      Linux 不知道从那个版本以来,就多了一个 config 的选项,叫做 Set version number in symbols of module。这是为了避免对系统造成不稳定。我们知道 Linux 的 kernel 更新的很快。在 kernel 更新的过程,有时为了效率起见,会对某些旧有的 data structure 或 function 做些改变,而且一变可能有的 variable 被拿掉,有的 function 的 prototype 跟原来的都不太一样。如果这种情形发生的时候,那可能以前 2.0.33 版本的 module 拿到 2.2.1 版本的 kernel 使用,假设原来 module 使用了 2.0.33 kernel 提供的变量叫 A,但是到了 2.2.1 由于某些原因必须把 A 都设成 NULL。那当此 module 用在 2.2.1 kernel 上时,如果它没去检查 A 的值就直接使用的话,就会造成系统的错误。也许不会整个系统都死掉,但是这个 module 肯定是很难发挥它的功能。为了这个原因,Linux 就在 compile module 时,把 kernel 版本的号码 encode 到各个 exported function 和 variable 里。

      所以,刚才也许我们不应该讲 kernel 提供了 drive_info,而应该说 kernel 提供了 driver_info_R744aa133 来让我们使用。这样也许各位会比较明白。也就是说,kernel 认为它提供的 driver_info_R744aa133 这个东西,而不是 driver_info。所以,我们可以发现有的人在加载 module 时,系统都一直告诉你某个 function 无法 resolved。这就是因为 kernel 里没有你要的 function,要不然就是你的 module 里使用的 function 跟 kernel encode 的结果不一样。所以无法 resolve。解决方式,要嘛就是将 kernel 里的 set version 选项关掉,要嘛就是将 module compile 成 kernel 有办法接受的型式。

      那有人就会想说,如果 kernel 认定它提供的 function 名字叫做 driver_info_R744aa133 的话,那我们写程序时,是不是用到这个 funnction 的地方都改成 driver_info_R744aa133 就可以了。答案是 Yes。但是,如果每个 function 都要你这样写,你不会觉得很烦吗 ? 比方说,我们在写 driver 时,很多人都会用到 printk 这个 function。这是 kernel 所提供的 function。它的功能跟 printf 很像。用法也几乎都一样。是 debug 时很好用的东西。如果我们 module 里用了一百次 printk,那是不是我们也要打一百次的 printk_Rdd132261 呢 ? 当然不是,聪明的人马上会想到用 #define printk printk_Rdd132261 就好了嘛。所以啰,Linux 很体贴的帮我们做了这件事。

      如果各位的系统有将 set version 的选项打开的话,那大家可以到 /usr/src/linux/include/linux/modules 这个目录底下。这个目录底下有许多的 ..ver档案。这些档案其实就是用来做 #define 用的。我们来看看 ksyms.ver 这个档案里,里面有一行是这样子的 :

    #define printk _set_ver(printk)

      set_ver 是一个 macro,就是用来在 printk 后面加上 version number 的。有兴趣的朋友可以自行去观看这个 macro 的写法。用了这些 ver 檔,我们就可以在 module 里直接使用 printk 这样的名字了。而这些 ver 档会自动帮我们做好 #define 的动作。可是,我们可以发现这个目录有很多很多的 ver 檔。有时候,我们怎么知道我们要呼叫的 function 是在那个 ver 档里有定义呢 ? Linux 又帮我们做了一件事。/usr/src/linux/include/linux/modversions.h 这个档案已经将全部的 ver 档都加进来了。所以在我们的 module 里只要 include 这个档,那名字的问题都解决了。但是,在此,我们奉劝各位一件事,不要将 modversions.h 这个档在 module 里 include 进来,如果真的要,那也要加上以下数行:

    #ifdef MODVERSIONS
    #include <linux/modversions.h>
    #endif

      加入这三行的原因是,避免这个 module 在没有设定 kernel version 的系统上,将 modversions.h 这个档案 include 进来。各位可以去试试看,当你把 set version 的选项关掉时,modversions.h 和 modules 这个目录都会不见。如果没有上面三行,那 compile 就不会过关。所以一般来讲,modversions.h 我们会选择在 compile 时传给 gcc 使用。就像下面这个样子。

    gcc -c -D__KERNEL__ -DMODULE -DMODVERSIONS main.c /
    -include usr/src/linux/include/linux/modversions.h

      在这个 command line 里,我们看到了 -D__KERNEL__,这是说要定义 __KERNEL__ 这个 constant。很多跟 kernel 有关的 header file,都必须要定义这个 constant 才能 include 的。所以建议你最好将它定义起来。另外还有一个 -DMODVERSIONS。这个 constant 我刚才忘了讲。刚才我们说要解决 fucntion 或 variable 名字 encode 的方式就是要 include modversions.h,其实除此之外,你还必须定义 MODVERSIONS 这个 constant。再来就是 MODULE 这个 constant。其实,只要是你要写 module 就一定要定义这个变量。而且你还要 include module.h 这个档案,因为 _set_ver 就是定义在这里的。

      讲到这里,相信各位应该对 module 有一些认识了,以后遇到 module unresolved 应该不会感到困惑了,应该也有办法解决了。

      刚才讲的都是使用别人的 function 上遇到的名字 encode 问题。但是,如果我们自己的 module 想要 export 一些东西让别的 module 使用呢。很简单。在 default 上,在你的 module 里所有的 global variable 和 function 都会被认定为你要 export 出去的。所以,如果你的 module 里有 10 个 global variable,经由 ksyms,你可以发现这十个 variable 都会被 export 出去。这当然是个很方便的事啦,但是,你知道,有时候我们根本不想把所有的 variable 都 export 出去,万一有个 module 没事乱改我们的 variable 怎幺办呢 ? 所以,在很多时候,我们都只会限定几个必要的东西 export 出去。在 2.2.1 之前的 kernel (不是很确定) 可以利用 register_symtab 来帮我们。但是,现在更新的版本早就出来了。所以,在此,我会介绍 kernel 2.2.1 里所提供的。kernel 2.2.1 里提供了一个 macro,叫做 EXPORT_SYMBOL,这是用来帮我们选择要 export 的 variable 或 function。比方说,我要 export 一个叫 full 的 variable,那我只要在 module 里写:

    EXPORT_SYMBOL(full);

      就会自动将 full export 出去,你马上就可以从 ksyms 里发现有 full 这个变量被 export 出去。在使用 EXPORT_SYMBOL 之前,要小心一件事,就是必须在 gcc 里定义 EXPORT_SYMTAB 这个 constant,否则在 compile 时会发生 parser error。所以,要使用 EXPORT_SYMBOL 的话,那 gcc 应该要下:

    gcc -c -D__KERNEL__ -DMODULE -DMODVERSIONS -DEXPORT_SYMTAB /
    main.c -include /usr/src/linux/include/linux/modversions.h

      如果我们不想 export 任何的东西,那我们只要在 module 里下

    EXPORT_NO_SYMBOLS;

      就可以了。使用 EXPORT_NO_SYMBOLS 用不着定义任何的 constant。其实,如果各位使用过旧版的 register_symbol 的话,一定会觉得新版的方式比较好用。至少我是这样觉得啦。因为使用 register_symbol 还要先定义出自己的 symbol_table,感觉有点麻烦。

    当我们使用 EXPORT_SYMBOL 把一些 function 或 variable export 出来之后,我们使用 ksyma -a 去看一些结果。我们发现 EXPORT_SYMBOL(full) 的确是把 full export出来了 :

    c8822200 full [my_module]
    c01b8e08 pci_find_slot_R454463b5
    . . .

      但是,结果怎幺跟我们想象中的不太一样,照理说,应该是 full_Rxxxxxx 之类的东西才对啊,怎幺才出现 full 而已呢 ? 奇怪,问题在那里呢 ?

      其实,问题就在于我们没有对本身的 module 所 export 出来的 function 或 variable 的名字做 encode。想想,如果在 module 的开头。我们加入一行

    #define full full_Rxxxxxx

      之后,我们再重新 compile module 一次,载到 kernel 之后,就可以发现 ksyms -a 显示的是

    c8822200 full_Rxxxxxx [my_module]
    c01b8e08 pci_find_slot_R454463b5
    . . . . .

      了。那是不是说,我们要去对每一个 export 出来的 variable 和 function 做 define 的动作呢 ? 当然不是啰。记得吗,前头我们讲去使用 kernel export 的 function 时,由于 include 了一些 .ver 的档案,以致于我们不用再做 define 的动作。现在,我们也要利用 .ver 的档案来帮我们,使我们 module export 出来的 function 也可以自动加入 kernel version 的 information。也就是变成 full_Rxxxxxx 之类的东西。

      Linux 里提供了一个 command,叫 genksyms,就是用来帮我们产生这种 .ver 的档案的。它会从 stdin 里读取 source code,然后检查 source code 里是否有 export 的 variable 或 function。如果有,它就会自动为每个 export 出来的东西产生一些 define。这些 define 就是我们之前说的。等我们有了这些 define 之后,只要在我们的 module 里加入这些 define,那 export 出来的 function 或 variable 就会变成上面那个样子。

      假设我们的程序都放在一个叫 main.c 的档案里,我们可以使用下列的方式产生这些 define。

    gcc -E -D__GENKSYMS__ main.c | genksyms -k 2.2.1 > main.ver

      gcc 的 -E 参数是指将 preprocessing 的结果 show 出来。也就是说将它 include 的档案,一些 define 的结果都展开。-D__GENKSYMS__ 是一定要的。如果没有定义这个 constant,你将不会看到任何的结果。用一个管线是因为 genksyms 是从 stdin 读资料的,所以,经由管线将 gcc 的结果传给 genksyms。-k 2.2.1 是指目前使用的 kernel 版本是 2.2.1,如果你的 kernel 版本不一样,必须指定你的 kernel 的版本。产生的 define 将会被放到 main.ver 里。产生完 main.ver 档之后,在 main.c 里将它 include 进来,那一切就 OK 了。有件事要告诉各位的是,使用这个方式产生的 module,其 export 出来的东西会经由 main.ver 的 define 改头换面。所以如果你要让别人使用,那你必须将 main.ver 公开,不然,别人就没办法使用你 export 出来的东西了。

      讲了这幺多,相信各位应该都已经比较清楚 module 在 kernel 中是怎幺样一回事,也应该知道为什幺有时候 module 会无法加载了。除此之外,各位应该还知道如何使自己 module export 出来的东西也具有 kernel version 的 information。

      接下来,要跟各位讲的就是,如何写一个 module 了。其实,写一个 module 很简单的。如果你了解我上面所说的东西。那我再讲一次,再用个例子,相信大家就都会了。要写一个 module,必须要提供两个 function。这两个 function 是给 insmod 和 rmmod 使用的。它们分别是 init_module(),以及 cleanup_module()。

    int init_module();
    void cleanup_module();

      相信大家都知道在 Linux 里可以使用 insmod 这个 command 来将某个 module 加载。比方说,我有一个 module 叫 hello.o,那使用 insmod hello.o 就可以将 hello 这个 module 载到 kernel 里。观察 /etc/modules 应该就可以看到 hello 这个 module 的名字。如果要将 hello 这个 module 移除,则只要使用 rmmod hello 就可以了。insmod 在加载 module 之后,就会去呼叫 module 所提供的 init_module()。如果传回 0 表示成功,那 module 就会被加载。如果失败,那加载的动作就会失败。一般来讲,我们在 init_module() 做的事都是一些初始化的工作。比方说,你的 module 需要一块内存,那你就可以在 init_module() 做 kmalloc 的动作。想当然尔。cleanup_module() 就是在 module 要移除的时候做的事。做的事一般来讲就是一些善后的工作,比方像把之前 kmalloc 的内存 free 掉。

      由于 module 是载到 kernel 使用的,所以,可能别的 module 会使用你的 module,甚至某些 process 也会使用到你的 module,为了避免 module 还有人使用时就被移除,每个 module 都有一个 use count。用来记录目前有多少个 process 或 module 正在使用这个 module。当 module 的 use count 不等于 0 时,module 是不会被移除掉的。也就是说,当 module 的 use count 不等于 0 时,cleanup_module() 是不会被呼叫的。

      在此,我要介绍三个 macro,是跟 module 的 use count 有关的。

    MOD_INC_USE_COUNT
    MOD_DEC_USE_COUNT
    MOD_IN_USE

      MOD_INC_USE_COUNT 是用来增加 module 的 use count,而 MOD_DEC_USE_COUNT 是用来减少 module 的 use count。至于 MOD_IN_USE 则是用来检查目前这个 module 是不是被使用中。也就是检查 use count 是否为 0。module 的 use count 必须由写 module 的人自己来 maintain。系统并不会自动为你把 use count 加一或减一。一切都得由自己控制。下面有一个例子,但是,并不会介绍这三个 macro 的使用方法。将来如果有机会,我再来介绍这三个 macro 的用法。

      这个例子很简单。其实只是示范如何使用 init_module() 以及 cleanup_module() 来写一个 module。当然,这两个 function 只是构成 module 的基本条件罢了。至于 module 里要提供的功能则是看各人的需要。

    main.c
    #define MODULE
    #include <linux/module.h>
    #include <asm/uaccess.h>
    int full;
    EXPORT_SYMBOL(full); /* 将 full export 出去 */
    int init_module( void )
    {
    printk( "<5> Module is loaded/n" );
    return 0;
    }
    void cleanup_module( void )
    {
    printk( "<5> Module is unloaded/n" );
    }

      关于 printk 是这样子的,它是 kernel 所提供的一个打印讯息的 function。kernel 有 export 这个 function。所以你可以自由的使用它。它的用法跟 printf 几乎一模一样。唯独讯息的开头是 <5>,其实,不见得这三个字符啦。也可以是 <4>,<3>,<7> 等等的东西。这是代表这个讯息的 prioirty 或 level。<5> 表示的是跟 KERNEL 有关的讯息。

    main.ver:

      利用 genksyms 产生出来的。

    gcc -E -D__GENKSYMS__ main.c | genksyms -k 2.2.1 > main.ver

      接下来,就是要把 main.c compile 成 main.o

    gcc -D__KERNEL__ -DMODVERSIONS -DEXPORT_SYMTAB -c /
    -I/usr/src/linux/include/linux -include /
    /usr/src/linux/include/linux/modversions.h /
    -include ./main.ver main.c

      好了。main.o 已经成功的 compile 出来了,现在下一个 command,

    insmod main.o

      检查看 /proc/modules 里是否有 main 这个 module。如果有,表示 main 这个 module 已经载到 kernel 了。再下一个指令,看看 full export 出去的结果。

    ksyms

    结果显示

    Address Symbol Defined by
    c40220e0 full_R355b84b2 [main]
    c401d04c ne_probe [ne]
    c401a04c ei_open [8390]
    c401a094 ei_close [8390]
    c401a504 ei_interrupt [8390]
    c401af1c ethdev_init [8390]
    c401af80 NS8390_init [8390]

      可以看到 full_R355b84b2,表示,我们已经成功的将 full 的名字加上 kernel version 的 information 了。当我们不需要这个 module 时,我们就可以下一个 command,

    rmmod main

      这样 main 就会被移除掉了。再检查看看 /proc/modules 就可以发现 main 那一行不见了。各位现在可以看一下 /var/log/message 这个档案,应该可以发现以两行

    Apr 12 14:19:05 host kernel: Module is loaded
    Apr 12 14:39:29 host kernel: Module is unloaded

      这两行就是 printk 印出来的。

      关于 module 的介绍已经到此告一段落了。其实,使用 module 实在是很简单的一件事。对于要发展 driver 或是增加 kernel 某些新功能的人来讲,用 module 不啻为一个方便的方式。希望这篇文章对各位能有所帮助。

     

    展开全文
  • Linux module原理与实现 -zt

    千次阅读 2007-01-04 15:48:00
    Linux module原理与实现不知道在什幺时候,Linux 出现了 module 这种东西,的确,它是 Linux 的一大革新。有了 module 之后,写 device driver 不再是一项恶梦,修改 kernel 也不再是一件痛苦的事了。因为你不需要...

    Linux module原理与实现

    不知道在什幺时候,Linux 出现了 module 这种东西,的确,它是 Linux 的一大革新。有了 module 之后,写 device driver 不再是一项恶梦,修改 kernel 也不再是一件痛苦的事了。因为你不需要每次要测试 driver 就重新 compile kernel 一次。那简直是会累死人。Module 可以允许我们动态的改变 kernel,加载 device driver,而且它也能缩短我们 driver development 的时间。在这篇文章里,我将要跟各位介绍一下 module 的原理,以及如何写一个 module。

    module 翻译成中文就是模块,不过,事实上去翻译这个字一点都没意义。在讲模块之前,我先举一个例子。相信很多人都用过 RedHat。在 RedHat 里,我们可以执行 sndconfig,它可以帮我们 config 声卡。config 完之后如果捉得到你的声卡,那你的声卡马上就可以动了,而且还不用重新激活计算机。这是怎幺做的呢 ? 就是靠module。module 其实是一般的程序。但是它可以被动态载到 kernel 里成为 kernel的一部分。载到 kernel 里的 module 它具有跟 kernel 一样的权力。可以 access 任何 kernel 的 data structure。你听过 kdebug 吗 ? 它是用来 debug kernel 的。它就是先将它本身的一个 module 载到 kernel 里,而在 user space 的 gdb 就可以经由跟这个 module 沟通,得知 kernel 里的 data structure 的值,除此之外,还可以经由载到 kernel 的 module 去更改 kernel 里 data structure。

    我们知道,在写 C 程序的时候,一个程序只能有一个 main。Kernel 本身其实也是一个程序,它本身也有个 main,叫 start_kernel()。当我们把一个 module 载到 kernel 里的时候,它会跟 kernel 整合在一起,成为 kernel 的一部分。请各位想想,那 module 可以有 main 吗 ? 答案很明显的,是 No。理由很简单。一个程序只能有一个 main。在使用 module 时,有一点要记住的是 module 是处于被动的角色。它是提供某些功能让别人去使用的。

    Kernel 里有一个变量叫 module_list,每当 user 将一个 module 载到 kernel 里的时候,这个 module 就会被记录在 module_list 里面。当 kernel 要使用到这个 module 提供的 function 时,它就会去 search 这个 list,找到 module,然后再使用其提供的 function 或 variable。每一个 module 都可以 export 一些 function 或变量来让别人使用。除此之外,module 也可以使用已经载到 kernel 里的 module 提供的 function。这种情形叫做 module stack。比方说,module A 用到 module B 的东西,那在加载 module A 之前必须要先加载 module B。否则 module A 会无法加载。除了 module 会 export 东西之外,kernel 本身也会 export 一些 function 或 variable。同样的,module 也可以使用 kernel 所 export 出来的东西。由于大家平时都是撰写 user space 的程序,所以,当突然去写 module 的时候,会把平时写程序用的 function 拿到 module 里使用。像是 printf 之类的东西。我要告诉各位的是,module 所使用的 function 或 variable,要嘛就是自己写在 module 里,要嘛就是别的 module 提供的,再不就是 kernel 所提供的。你不能使用一般 libc 或 glibc所提供的 function。像 printf 之类的东西。这一点可能是各位要多小心的地方。(也许你可以先 link 好,再载到 kernel,我好象试过,但是忘了)

    刚才我们说到 kernel 本身会 export 出一些 function 或 variable 来让 module 使用,但是,我们不是万能的,我们怎幺知道 kernel 有开放那里东西让我们使用呢 ? Linux 提供一个 command,叫 ksyms,你只要执行 ksyms -a 就可以知道 kernel 或目前载到 kernel 里的 module 提供了那些 function 或 variable。底下是我的系统的情形:

    c0216ba0 drive_info_R744aa133
    c01e4a44 boot_cpu_data_R660bd466
    c01e4ac0 EISA_bus_R7413793a
    c01e4ac4 MCA_bus_Rf48a2c4c
    c010cc34 __verify_write_R203afbeb
    . . . . .

    在 kernel 里,有一个 symbol table 是用来记录 export 出去的 function 或 variable。除此之外,也会记录着那个 module export 那些 function。上面几行中,表示 kernel 提供了 drive_info 这个 function/variable。所以,我们可以在 kernel 里直接使用它,等载到 kernel 里时,会自动做好 link 的动作。由此,我们可以知道,module 本身其实是还没做 link 的一些 object code。一切都要等到 module 被加载 kernel 之后,link 才会完成。各位应该可以看到 drive_info 后面还接着一些奇怪的字符串。_R744aa133,这个字符串是根据目前 kernel 的版本再做些 encode 得出来的结果。为什幺额外需要这一个字符串呢 ?

    Linux 不知道从那个版本以来,就多了一个 config 的选项,叫做 Set version number in symbols of module。这是为了避免对系统造成不稳定。我们知道 Linux 的 kernel 更新的很快。在 kernel 更新的过程,有时为了效率起见,会对某些旧有的 data structure 或 function 做些改变,而且一变可能有的 variable 被拿掉,有的 function 的 prototype 跟原来的都不太一样。如果这种情形发生的时候,那可能以前 2.0.33 版本的 module 拿到 2.2.1 版本的 kernel 使用,假设原来 module 使用了 2.0.33 kernel 提供的变量叫 A,但是到了 2.2.1 由于某些原因必须把 A 都设成 NULL。那当此 module 用在 2.2.1 kernel 上时,如果它没去检查 A 的值就直接使用的话,就会造成系统的错误。也许不会整个系统都死掉,但是这个 module 肯定是很难发挥它的功能。为了这个原因,Linux 就在 compile module 时,把 kernel 版本的号码 encode 到各个 exported function 和 variable 里。

    所以,刚才也许我们不应该讲 kernel 提供了 drive_info,而应该说 kernel 提供了 driver_info_R744aa133 来让我们使用。这样也许各位会比较明白。也就是说,kernel 认为它提供的 driver_info_R744aa133 这个东西,而不是 driver_info。所以,我们可以发现有的人在加载 module 时,系统都一直告诉你某个 function 无法 resolved。这就是因为 kernel 里没有你要的 function,要不然就是你的 module 里使用的 function 跟 kernel encode 的结果不一样。所以无法 resolve。解决方式,要嘛就是将 kernel 里的 set version 选项关掉,要嘛就是将 module compile 成 kernel 有办法接受的型式。

    那有人就会想说,如果 kernel 认定它提供的 function 名字叫做 driver_info_R744aa133 的话,那我们写程序时,是不是用到这个 funnction 的地方都改成 driver_info_R744aa133 就可以了。答案是 Yes。但是,如果每个 function 都要你这样写,你不会觉得很烦吗 ? 比方说,我们在写 driver 时,很多人都会用到 printk 这个 function。这是 kernel 所提供的 function。它的功能跟 printf 很像。用法也几乎都一样。是 debug 时很好用的东西。如果我们 module 里用了一百次 printk,那是不是我们也要打一百次的 printk_Rdd132261 呢 ? 当然不是,聪明的人马上会想到用 #define printk printk_Rdd132261 就好了嘛。所以啰,Linux 很体贴的帮我们做了这件事。

    如果各位的系统有将 set version 的选项打开的话,那大家可以到 /usr/src/linux/include/linux/modules 这个目录底下。这个目录底下有所多的 ..ver档案。这些档案其实就是用来做 #define 用的。我们来看看 ksyms.ver 这个档案里,里面有一行是这样子的 :

    #define printk _set_ver(printk)

    set_ver 是一个 macro,就是用来在 printk 后面加上 version number 的。有兴趣的朋友可以自行去观看这个 macro 的写法。用了这些 ver 檔,我们就可以在 module 里直接使用 printk 这样的名字了。而这些 ver 档会自动帮我们做好 #define 的动作。可是,我们可以发现这个目录有很多很多的 ver 檔。有时候,我们怎幺知道我们要呼叫的 function 是在那个 ver 档里有定义呢 ? Linux 又帮我们做了一件事。/usr/src/linux/include/linux/modversions.h 这个档案已经将全部的 ver 档都加进来了。所以在我们的 module 里只要 include 这个档,那名字的问题都解决了。但是,在此,我们奉劝各位一件事,不要将 modversions.h 这个档在 module 里 include 进来,如果真的要,那也要加上以下数行:

    #ifdef MODVERSIONS
    #include <linux/modversions.h>
    #endif

    加入这三行的原因是,避免这个 module 在没有设定 kernel version 的系统上,将 modversions.h 这个档案 include 进来。各位可以去试试看,当你把 set version 的选项关掉时,modversions.h 和 modules 这个目录都会不见。如果没有上面三行,那 compile 就不会过关。所以一般来讲,modversions.h 我们会选择在 compile 时传给 gcc 使用。就像下面这个样子。

    gcc -c -D__KERNEL__ -DMODULE -DMODVERSIONS main.c /
    -include usr/src/linux/include/linux/modversions.h

    在这个 command line 里,我们看到了 -D__KERNEL__,这是说要定义 __KERNEL__ 这个 constant。很多跟 kernel 有关的 header file,都必须要定义这个 constant 才能 include 的。所以建议你最好将它定义起来。另外还有一个 -DMODVERSIONS。这个 constant 我刚才忘了讲。刚才我们说要解决 fucntion 或 variable 名字 encode 的方式就是要 include modversions.h,其实除此之外,你还必须定义 MODVERSIONS 这个 constant。再来就是 MODULE 这个 constant。其实,只要是你要写 module 就一定要定义这个变量。而且你还要 include module.h 这个档案,因为 _set_ver 就是定义在这里的。

    讲到这里,相信各位应该对 module 有一些认识了,以后遇到 module unresolved 应该不会感到困惑了,应该也有办法解决了。

    刚才讲的都是使用别人的 function 上遇到的名字 encode 问题。但是,如果我们自己的 module 想要 export 一些东西让别的 module 使用呢。很简单。在 default 上,在你的 module 里所有的 global variable 和 function 都会被认定为你要 export 出去的。所以,如果你的 module 里有 10 个 global variable,经由 ksyms,你可以发现这十个 variable 都会被 export 出去。这当然是个很方便的事啦,但是,你知道,有时候我们根本不想把所有的 variable 都 export 出去,万一有个 module 没事乱改我们的 variable 怎幺办呢 ? 所以,在很多时候,我们都只会限定几个必要的东西 export 出去。在 2.2.1 之前的 kernel (不是很确定) 可以利用 register_symtab 来帮我们。但是,现在更新的版本早就出来了。所以,在此,我会介绍 kernel 2.2.1 里所提供的。kernel 2.2.1 里提供了一个 macro,叫做 EXPORT_SYMBOL,这是用来帮我们选择要 export 的 variable 或 function。比方说,我要 export 一个叫 full 的 variable,那我只要在 module 里写:

    EXPORT_SYMBOL(full);

    就会自动将 full export 出去,你马上就可以从 ksyms 里发现有 full 这个变量被 export 出去。在使用 EXPORT_SYMBOL 之前,要小心一件事,就是必须在 gcc 里定义 EXPORT_SYMTAB 这个 constant,否则在 compile 时会发生 parser error。所以,要使用 EXPORT_SYMBOL 的话,那 gcc 应该要下:

    gcc -c -D__KERNEL__ -DMODULE -DMODVERSIONS -DEXPORT_SYMTAB /
    main.c -include /usr/src/linux/include/linux/modversions.h

    如果我们不想 export 任何的东西,那我们只要在 module 里下

    EXPORT_NO_SYMBOLS;

    就可以了。使用 EXPORT_NO_SYMBOLS 用不着定义任何的 constant。其实,如果各位使用过旧版的 register_symbol 的话,一定会觉得新版的方式比较好用。至少我是这样觉得啦。因为使用 register_symbol 还要先定义出自己的 symbol_table,感觉有点麻烦。

    当我们使用 EXPORT_SYMBOL 把一些 function 或 variable export 出来之后,我们使用 ksyma -a 去看一些结果。我们发现 EXPORT_SYMBOL(full) 的确是把 full export出来了 :

    c8822200 full [my_module]
    c01b8e08 pci_find_slot_R454463b5
    . . .

    但是,结果怎幺跟我们想象中的不太一样,照理说,应该是 full_Rxxxxxx 之类的东西才对啊,怎幺才出现 full 而已呢 ? 奇怪,问题在那里呢 ?

    其实,问题就在于我们没有对本身的 module 所 export 出来的 function 或 variable 的名字做 encode。想想,如果在 module 的开头。我们加入一行

    #define full full_Rxxxxxx

    之后,我们再重新 compile module 一次,载到 kernel 之后,就可以发现 ksyms -a 显示的是

    c8822200 full_Rxxxxxx [my_module]
    c01b8e08 pci_find_slot_R454463b5
    . . . . .

    了。那是不是说,我们要去对每一个 export 出来的 variable 和 function 做 define 的动作呢 ? 当然不是啰。记得吗,前头我们讲去使用 kernel export 的 function 时,由于 include 了一些 .ver 的档案,以致于我们不用再做 define 的动作。现在,我们也要利用 .ver 的档案来帮我们,使我们 module export 出来的 function 也可以自动加入 kernel version 的 information。也就是变成 full_Rxxxxxx 之类的东西。

    Linux 里提供了一个 command,叫 genksyms,就是用来帮我们产生这种 .ver 的档案的。它会从 stdin 里读取 source code,然后检查 source code 里是否有 export 的 variable 或 function。如果有,它就会自动为每个 export 出来的东西产生一些 define。这些 define 就是我们之前说的。等我们有了这些 define 之后,只要在我们的 module 里加入这些 define,那 export 出来的 function 或 variable 就会变成上面那个样子。

    假设我们的程序都放在一个叫 main.c 的档案里,我们可以使用下列的方式产生这些 define。

    gcc -E -D__GENKSYMS__ main.c | genksyms -k 2.2.1 > main.ver

    gcc 的 -E 参数是指将 preprocessing 的结果 show 出来。也就是说将它 include 的档案,一些 define 的结果都展开。-D__GENKSYMS__ 是一定要的。如果没有定义这个 constant,你将不会看到任何的结果。用一个管线是因为 genksyms 是从 stdin 读资料的,所以,经由管线将 gcc 的结果传给 genksyms。-k 2.2.1 是指目前使用的 kernel 版本是 2.2.1,如果你的 kernel 版本不一样,必须指定你的 kernel 的版本。产生的 define 将会被放到 main.ver 里。产生完 main.ver 档之后,在 main.c 里将它 include 进来,那一切就 OK 了。有件事要告诉各位的是,使用这个方式产生的 module,其 export 出来的东西会经由 main.ver 的 define 改头换面。所以如果你要让别人使用,那你必须将 main.ver 公开,不然,别人就没办法使用你 export 出来的东西了。

    讲了这幺多,相信各位应该都已经比较清楚 module 在 kernel 中是怎幺样一回事,也应该知道为什幺有时候 module 会无法加载了。除此之外,各位应该还知道如何使自己 module export 出来的东西也具有 kernel version 的 information。

    接下来,要跟各位讲的就是,如何写一个 module 了。其实,写一个 module 很简单的。如果你了解我上面所说的东西。那我再讲一次,再用个例子,相信大家就都会了。要写一个 module,必须要提供两个 function。这两个 function 是给 insmod 和 rmmod 使用的。它们分别是 init_module(),以及 cleanup_module()。

    int init_module();
    void cleanup_module();

    相信大家都知道在 Linux 里可以使用 insmod 这个 command 来将某个 module 加载。比方说,我有一个 module 叫 hello.o,那使用 insmod hello.o 就可以将 hello 这个 module 载到 kernel 里。观察 /etc/modules 应该就可以看到 hello 这个 module 的名字。如果要将 hello 这个 module 移除,则只要使用 rmmod hello 就可以了。insmod 在加载 module 之后,就会去呼叫 module 所提供的 init_module()。如果传回 0 表示成功,那 module 就会被加载。如果失败,那加载的动作就会失败。一般来讲,我们在 init_module() 做的事都是一些初始化的工作。比方说,你的 module 需要一块内存,那你就可以在 init_module() 做 kmalloc 的动作。想当然尔。cleanup_module() 就是在 module 要移除的时候做的事。做的事一般来讲就是一些善后的工作,比方像把之前 kmalloc 的内存 free 掉。

    由于 module 是载到 kernel 使用的,所以,可能别的 module 会使用你的 module,甚至某些 process 也会使用到你的 module,为了避免 module 还有人使用时就被移除,每个 module 都有一个 use count。用来记录目前有多少个 process 或 module 正在使用这个 module。当 module 的 use count 不等于 0 时,module 是不会被移除掉的。也就是说,当 module 的 use count 不等于 0 时,cleanup_module() 是不会被呼叫的。

    在此,我要介绍三个 macro,是跟 module 的 use count 有关的。

    MOD_INC_USE_COUNT
    MOD_DEC_USE_COUNT
    MOD_IN_USE

    MOD_INC_USE_COUNT 是用来增加 module 的 use count,而 MOD_DEC_USE_COUNT 是用来减少 module 的 use count。至于 MOD_IN_USE 则是用来检查目前这个 module 是不是被使用中。也就是检查 use count 是否为 0。module 的 use count 必须由写 module 的人自己来 maintain。系统并不会自动为你把 use count 加一或减一。一切都得由自己控制。下面有一个例子,但是,并不会介绍这三个 macro 的使用方法。将来如果有机会,我再来介绍这三个 macro 的用法。

    这个例子很简单。其实只是示范如何使用 init_module() 以及 cleanup_module() 来写一个 module。当然,这两个 function 只是构成 module 的基本条件罢了。至于 module 里要提供的功能则是看各人的需要。

    main.c
    #define MODULE
    #include <linux/module.h>
    #include <asm/uaccess.h>
    int full;
    EXPORT_SYMBOL(full); /* 将 full export 出去 */
    int init_module( void )
    {
    printk( "<5> Module is loaded/n" );
    return 0;
    }
    void cleanup_module( void )
    {
    printk( "<5> Module is unloaded/n" );
    }

    关于 printk 是这样子的,它是 kernel 所提供的一个打印讯息的 function。kernel 有 export 这个 function。所以你可以自由的使用它。它的用法跟 printf 几乎一模一样。唯独讯息的开头是 <5>,其实,不见得这三个字符啦。也可以是 <4>,<3>,<7> 等等的东西。这是代表这个讯息的 prioirty 或 level。<5> 表示的是跟 KERNEL 有关的讯息。

    main.ver:

    利用 genksyms 产生出来的。

    gcc -E -D__GENKSYMS__ main.c | genksyms -k 2.2.1 > main.ver

    接下来,就是要把 main.c compile 成 main.o

    gcc -D__KERNEL__ -DMODVERSIONS -DEXPORT_SYMTAB -c /
    -I/usr/src/linux/include/linux -include /
    /usr/src/linux/include/linux/modversions.h /
    -include ./main.ver main.c

    好了。main.o 已经成功的 compile 出来了,现在下一个 command,

    insmod main.o

    检查看 /proc/modules 里是否有 main 这个 module。如果有,表示 main 这个 module 已经载到 kernel 了。再下一个指令,看看 full export 出去的结果。

    ksyms

    结果显示

    Address Symbol Defined by
    c40220e0 full_R355b84b2 [main]
    c401d04c ne_probe [ne]
    c401a04c ei_open [8390]
    c401a094 ei_close [8390]
    c401a504 ei_interrupt [8390]
    c401af1c ethdev_init [8390]
    c401af80 NS8390_init [8390]

    可以看到 full_R355b84b2,表示,我们已经成功的将 full 的名字加上 kernel version 的 information 了。当我们不需要这个 module 时,我们就可以下一个 command,

    rmmod main

    这样 main 就会被移除掉了。再检查看看 /proc/modules 就可以发现 main 那一行不见了。各位现在可以看一下 /var/log/message 这个档案,应该可以发现以两行

    Apr 12 14:19:05 host kernel: Module is loaded
    Apr 12 14:39:29 host kernel: Module is unloaded

    这两行就是 printk 印出来的。

    关于 module 的介绍已经到此告一段落了。其实,使用 module 实在是很简单的一件事。对于要发展 driver 或是增加 kernel 某些新功能的人来讲,用 module 不啻为一个方便的方式。希望这篇文章对各位能有所帮助。

    展开全文
  • Linux insmod指令原理

    2021-05-25 15:29:51
    linux 系统会调用linux/kernel/module.c里面的sys_init_modul函数 sys_init_modul函数先做一些校验,之后进行关键的两步 mod = load_module(umod, len, uargs);//模块加载 ret = do_one_initcall(mod->init);//...

    之前写的好像不太对,我的理解:
    输入insmod xxx.ko之后(基于linux3.5版本)

    1. linux 系统会调用linux/kernel/module.c里面的sys_init_modul函数
    2. sys_init_modul函数先做一些校验,之后进行关键的两步
      mod = load_module(umod, len, uargs);//模块加载
      ret = do_one_initcall(mod->init);//模块init函数调用
    3. load_module模块加载分四步
      copy_and_check将模块从用户空间拷贝到内核空间,
      layout_and_allocate为模块进行地址空间分配,
      simplify_symbols为模块进行符号解析,
      apply_relocations为模块进行重定位。
    4. do_one_initcall就是我们自己写的module_init()驱动函数。
      在此需要另外说几点:
      xxx.ko是目标文件,无法直接运行,需要链接后才可以运行,因此就就需要进行3的load_module模块加载的那几步。有关重定位的知识见此博客:https://blog.csdn.net/gyyu32g/article/details/78508406
      链接(Link)
      目标文件经过链接(Link)以后才能变成可执行文件。既然目标文件和可执行文件的格式是一样的,为什么还要再链接一次呢,直接作为可执行文件不行吗?
      不行的!因为编译只是将我们自己写的代码变成了二进制形式,它还需要和系统组件(比如标准库、动态链接库等)结合起来,这些组件都是程序运行所必须的。
      链接(Link)其实就是一个“打包”的过程,它将所有二进制形式的目标文件和系统组件组合成一个可执行文件。完成链接的过程也需要一个特殊的软件,叫做链接器(Linker)。
      随着我们学习的深入,我们编写的代码越来越多,最终需要将它们分散到多个源文件中,编译器每次只能编译一个源文件,生成一个目标文件,这个时候,链接器除了将目标文件和系统组件组合起来,还需要将编译器生成的多个目标文件组合起来。
    展开全文
  • 为此,Linux提供了一种全新的机制,叫(可安装)“模块”(module)。利用这个机制,可以根据需要,在不必对内核重新编译链接的条件下,将可安装模块动态的插入运行中的内核,成为内核的一个有机组成部分;或者从...
  • linux module Makefile

    千次阅读 2010-03-11 02:15:00
    不知道在什幺时候,转载Linux 出现了 module 这种东西,的确,它是 Linux 的一大革新。有了 module 之后,写device driver 不再是一项恶梦,修改 kernel 也不再是一件痛苦的事了。因为你不需要每次要测试 driver 就...

    不知道在什幺时候,转载Linux 出现了 module 这种东西,的确,它是 Linux 的一大革新。有了 module 之后,写 device driver 不再是一项恶梦,修改 kernel 也不再是一件痛苦的事了。因为你不需要每次要测试 driver 就重新 compile kernel 一次。那简直是会累死人。Module 可以允许我们动态的改变 kernel,加载 device driver,而且它也能缩短我们 driver development 的时间。在这篇文章里,我将要跟各位介绍一下 module 的原理,以及如何写一个 module。

    module 翻译成中文就是模块,不过,事实上去翻译这个字一点都没意义。在讲模块之前,我先举一个例子。相信很多人都用过 RedHat。在 RedHat 里,我们可以执行 sndconfig,它可以帮我们 config 声卡。config 完之后如果捉得到你的声卡,那你的声卡马上就可以动了,而且还不用重新激活计算机。这是怎幺做的呢 ? 就是靠module。module 其实是一般的程序。但是它可以被动态载到 kernel 里成为 kernel的一部分。载到 kernel 里的 module 它具有跟 kernel 一样的权力。可以 access 任何 kernel 的 data structure。你听过 kdebug 吗 ? 它是用来 debug kernel 的。它就是先将它本身的一个 module 载到 kernel 里,而在 user space 的 gdb 就可以经由跟这个 module 沟通,得知 kernel 里的 data structure 的值,除此之外,还可以经由载到 kernel 的 module 去更改 kernel 里 data structure。

    我们知道,在写 C 程序的时候,一个程序只能有一个 main。Kernel 本身其实也是一个程序,它本身也有个 main,叫 start_kernel()。当我们把一个 module 载到 kernel 里的时候,它会跟 kernel 整合在一起,成为 kernel 的一部分。请各位想想,那 module 可以有 main 吗 ? 答案很明显的,是 No。理由很简单。一个程序只能有一个 main。在使用 module 时,有一点要记住的是 module 是处于被动的角色。它是提供某些功能让别人去使用的。

    Kernel 里有一个变量叫 module_list,每当 user 将一个 module 载到 kernel 里的时候,这个 module 就会被记录在 module_list 里面。当 kernel 要使用到这个 module 提供的 function 时,它就会去 search 这个 list,找到 module,然后再使用其提供的 function 或 variable。每一个 module 都可以 export 一些 function 或变量来让别人使用。除此之外,module 也可以使用已经载到 kernel 里的 module 提供的 function。这种情形叫做 module stack。比方说,module A 用到 module B 的东西,那在加载 module A 之前必须要先加载 module B。否则 module A 会无法加载。除了 module 会 export 东西之外,kernel 本身也会 export 一些 function 或 variable。同样的,module 也可以使用 kernel 所 export 出来的东西。由于大家平时都是撰写 user space 的程序,所以,当突然去写 module 的时候,会把平时写程序用的 function 拿到 module 里使用。像是 printf 之类的东西。我要告诉各位的是,module 所使用的 function 或 variable,要嘛就是自己写在 module 里,要嘛就是别的 module 提供的,再不就是 kernel 所提供的。你不能使用一般 libc 或 glibc所提供的 function。像 printf 之类的东西。这一点可能是各位要多小心的地方。(也许你可以先 link 好,再载到 kernel,我好象试过,但是忘了)

    刚才我们说到 kernel 本身会 export 出一些 function 或 variable 来让 module 使用,但是,我们不是万能的,我们怎幺知道 kernel 有开放那里东西让我们使用呢 ? Linux 提供一个 command,叫 ksyms,你只要执行 ksyms -a 就可以知道 kernel 或目前载到 kernel 里的 module 提供了那些 function 或 variable。底下是我的系统的情形:

    c0216ba0 drive_info_R744aa133
    c01e4a44 boot_cpu_data_R660bd466
    c01e4ac0 EISA_bus_R7413793a
    c01e4ac4 MCA_bus_Rf48a2c4c
    c010cc34 __verify_write_R203afbeb
    . . . . .

    在 kernel 里,有一个 symbol table 是用来记录 export 出去的 function 或 variable。除此之外,也会记录着那个 module export 那些 function。上面几行中,表示 kernel 提供了 drive_info 这个 function/variable。所以,我们可以在 kernel 里直接使用它,等载到 kernel 里时,会自动做好 link 的动作。由此,我们可以知道,module 本身其实是还没做 link 的一些 object code。一切都要等到 module 被加载 kernel 之后,link 才会完成。各位应该可以看到 drive_info 后面还接着一些奇怪的字符串。_R744aa133,这个字符串是根据目前 kernel 的版本再做些 encode 得出来的结果。为什幺额外需要这一个字符串呢 ?

    Linux 不知道从那个版本以来,就多了一个 config 的选项,叫做 Set version number in symbols of module。这是为了避免对系统造成不稳定。我们知道 Linux 的 kernel 更新的很快。在 kernel 更新的过程,有时为了效率起见,会对某些旧有的 data structure 或 function 做些改变,而且一变可能有的 variable 被拿掉,有的 function 的 prototype 跟原来的都不太一样。如果这种情形发生的时候,那可能以前 2.0.33 版本的 module 拿到 2.2.1 版本的 kernel 使用,假设原来 module 使用了 2.0.33 kernel 提供的变量叫 A,但是到了 2.2.1 由于某些原因必须把 A 都设成 NULL。那当此 module 用在 2.2.1 kernel 上时,如果它没去检查 A 的值就直接使用的话,就会造成系统的错误。也许不会整个系统都死掉,但是这个 module 肯定是很难发挥它的功能。为了这个原因,Linux 就在 compile module 时,把 kernel 版本的号码 encode 到各个 exported function 和 variable 里。

    所以,刚才也许我们不应该讲 kernel 提供了 drive_info,而应该说 kernel 提供了 driver_info_R744aa133 来让我们使用。这样也许各位会比较明白。也就是说,kernel 认为它提供的 driver_info_R744aa133 这个东西,而不是 driver_info。所以,我们可以发现有的人在加载 module 时,系统都一直告诉你某个 function 无法 resolved。这就是因为 kernel 里没有你要的 function,要不然就是你的 module 里使用的 function 跟 kernel encode 的结果不一样。所以无法 resolve。解决方式,要嘛就是将 kernel 里的 set version 选项关掉,要嘛就是将 module compile 成 kernel 有办法接受的型式。

    那有人就会想说,如果 kernel 认定它提供的 function 名字叫做 driver_info_R744aa133 的话,那我们写程序时,是不是用到这个 funnction 的地方都改成 driver_info_R744aa133 就可以了。答案是 Yes。但是,如果每个 function 都要你这样写,你不会觉得很烦吗 ? 比方说,我们在写 driver 时,很多人都会用到 printk 这个 function。这是 kernel 所提供的 function。它的功能跟 printf 很像。用法也几乎都一样。是 debug 时很好用的东西。如果我们 module 里用了一百次 printk,那是不是我们也要打一百次的 printk_Rdd132261 呢 ? 当然不是,聪明的人马上会想到用 #define printk printk_Rdd132261 就好了嘛。所以啰,Linux 很体贴的帮我们做了这件事。

    如果各位的系统有将 set version 的选项打开的话,那大家可以到 /usr/src/linux/include/linux/modules 这个目录底下。这个目录底下有所多的 ..ver档案。这些档案其实就是用来做 #define 用的。我们来看看 ksyms.ver 这个档案里,里面有一行是这样子的 :

    #define printk _set_ver(printk)

    set_ver 是一个 macro,就是用来在 printk 后面加上 version number 的。有兴趣的朋友可以自行去观看这个 macro 的写法。用了这些 ver 檔,我们就可以在 module 里直接使用 printk 这样的名字了。而这些 ver 档会自动帮我们做好 #define 的动作。可是,我们可以发现这个目录有很多很多的 ver 檔。有时候,我们怎幺知道我们要呼叫的 function 是在那个 ver 档里有定义呢 ? Linux 又帮我们做了一件事。/usr/src/linux/include/linux/modversions.h 这个档案已经将全部的 ver 档都加进来了。所以在我们的 module 里只要 include 这个档,那名字的问题都解决了。但是,在此,我们奉劝各位一件事,不要将 modversions.h 这个档在 module 里 include 进来,如果真的要,那也要加上以下数行:

    #ifdef MODVERSIONS
    #include <linux/modversions.h>;
    #endif

    加入这三行的原因是,避免这个 module 在没有设定 kernel version 的系统上,将 modversions.h 这个档案 include 进来。各位可以去试试看,当你把 set version 的选项关掉时,modversions.h 和 modules 这个目录都会不见。如果没有上面三行,那 compile 就不会过关。所以一般来讲,modversions.h 我们会选择在 compile 时传给 gcc 使用。就像下面这个样子。

    gcc -c -D__KERNEL__ -DMODULE -DMODVERSIONS main.c /
    -include usr/src/linux/include/linux/modversions.h

    在这个 command line 里,我们看到了 -D__KERNEL__,这是说要定义 __KERNEL__ 这个 constant。很多跟 kernel 有关的 header file,都必须要定义这个 constant 才能 include 的。所以建议你最好将它定义起来。另外还有一个 -DMODVERSIONS。这个 constant 我刚才忘了讲。刚才我们说要解决 fucntion 或 variable 名字 encode 的方式就是要 include modversions.h,其实除此之外,你还必须定义 MODVERSIONS 这个 constant。再来就是 MODULE 这个 constant。其实,只要是你要写 module 就一定要定义这个变量。而且你还要 include module.h 这个档案,因为 _set_ver 就是定义在这里的。

    讲到这里,相信各位应该对 module 有一些认识了,以后遇到 module unresolved 应该不会感到困惑了,应该也有办法解决了。

    刚才讲的都是使用别人的 function 上遇到的名字 encode 问题。但是,如果我们自己的 module 想要 export 一些东西让别的 module 使用呢。很简单。在 default 上,在你的 module 里所有的 global variable 和 function 都会被认定为你要 export 出去的。所以,如果你的 module 里有 10 个 global variable,经由 ksyms,你可以发现这十个 variable 都会被 export 出去。这当然是个很方便的事啦,但是,你知道,有时候我们根本不想把所有的 variable 都 export 出去,万一有个 module 没事乱改我们的 variable 怎幺办呢 ? 所以,在很多时候,我们都只会限定几个必要的东西 export 出去。在 2.2.1 之前的 kernel (不是很确定) 可以利用 register_symtab 来帮我们。但是,现在更新的版本早就出来了。所以,在此,我会介绍 kernel 2.2.1 里所提供的。kernel 2.2.1 里提供了一个 macro,叫做 EXPORT_SYMBOL,这是用来帮我们选择要 export 的 variable 或 function。比方说,我要 export 一个叫 full 的 variable,那我只要在 module 里写:

    EXPORT_SYMBOL(full);

    就会自动将 full export 出去,你马上就可以从 ksyms 里发现有 full 这个变量被 export 出去。在使用 EXPORT_SYMBOL 之前,要小心一件事,就是必须在 gcc 里定义 EXPORT_SYMTAB 这个 constant,否则在 compile 时会发生 parser error。所以,要使用 EXPORT_SYMBOL 的话,那 gcc 应该要下:

    gcc -c -D__KERNEL__ -DMODULE -DMODVERSIONS -DEXPORT_SYMTAB /
    main.c -include /usr/src/linux/include/linux/modversions.h

    如果我们不想 export 任何的东西,那我们只要在 module 里下

    EXPORT_NO_SYMBOLS;

    就可以了。使用 EXPORT_NO_SYMBOLS 用不着定义任何的 constant。其实,如果各位使用过旧版的 register_symbol 的话,一定会觉得新版的方式比较好用。至少我是这样觉得啦。因为使用 register_symbol 还要先定义出自己的 symbol_table,感觉有点麻烦。

    当我们使用 EXPORT_SYMBOL 把一些 function 或 variable export 出来之后,我们使用 ksyma -a 去看一些结果。我们发现 EXPORT_SYMBOL(full) 的确是把 full export出来了 :

    c8822200 full [my_module]
    c01b8e08 pci_find_slot_R454463b5
    . . .

    但是,结果怎幺跟我们想象中的不太一样,照理说,应该是 full_Rxxxxxx 之类的东西才对啊,怎幺才出现 full 而已呢 ? 奇怪,问题在那里呢 ?

    其实,问题就在于我们没有对本身的 module 所 export 出来的 function 或 variable 的名字做 encode。想想,如果在 module 的开头。我们加入一行

    #define full full_Rxxxxxx

    之后,我们再重新 compile module 一次,载到 kernel 之后,就可以发现 ksyms -a 显示的是

    c8822200 full_Rxxxxxx [my_module]
    c01b8e08 pci_find_slot_R454463b5
    . . . . .

    了。那是不是说,我们要去对每一个 export 出来的 variable 和 function 做 define 的动作呢 ? 当然不是啰。记得吗,前头我们讲去使用 kernel export 的 function 时,由于 include 了一些 .ver 的档案,以致于我们不用再做 define 的动作。现在,我们也要利用 .ver 的档案来帮我们,使我们 module export 出来的 function 也可以自动加入 kernel version 的 information。也就是变成 full_Rxxxxxx 之类的东西。

    Linux 里提供了一个 command,叫 genksyms,就是用来帮我们产生这种 .ver 的档案的。它会从 stdin 里读取 source code,然后检查 source code 里是否有 export 的 variable 或 function。如果有,它就会自动为每个 export 出来的东西产生一些 define。这些 define 就是我们之前说的。等我们有了这些 define 之后,只要在我们的 module 里加入这些 define,那 export 出来的 function 或 variable 就会变成上面那个样子。

    假设我们的程序都放在一个叫 main.c 的档案里,我们可以使用下列的方式产生这些 define。

    gcc -E -D__GENKSYMS__ main.c | genksyms -k 2.2.1 >; main.ver

    gcc 的 -E 参数是指将 preprocessing 的结果 show 出来。也就是说将它 include 的档案,一些 define 的结果都展开。-D__GENKSYMS__ 是一定要的。如果没有定义这个 constant,你将不会看到任何的结果。用一个管线是因为 genksyms 是从 stdin 读资料的,所以,经由管线将 gcc 的结果传给 genksyms。-k 2.2.1 是指目前使用的 kernel 版本是 2.2.1,如果你的 kernel 版本不一样,必须指定你的 kernel 的版本。产生的 define 将会被放到 main.ver 里。产生完 main.ver 档之后,在 main.c 里将它 include 进来,那一切就 OK 了。有件事要告诉各位的是,使用这个方式产生的 module,其 export 出来的东西会经由 main.ver 的 define 改头换面。所以如果你要让别人使用,那你必须将 main.ver 公开,不然,别人就没办法使用你 export 出来的东西了。

    讲了这幺多,相信各位应该都已经比较清楚 module 在 kernel 中是怎幺样一回事,也应该知道为什幺有时候 module 会无法加载了。除此之外,各位应该还知道如何使自己 module export 出来的东西也具有 kernel version 的 information。

    接下来,要跟各位讲的就是,如何写一个 module 了。其实,写一个 module 很简单的。如果你了解我上面所说的东西。那我再讲一次,再用个例子,相信大家就都会了。要写一个 module,必须要提供两个 function。这两个 function 是给 insmod 和 rmmod 使用的。它们分别是 init_module(),以及 cleanup_module()。

    int init_module();
    void cleanup_module();

    相信大家都知道在 Linux 里可以使用 insmod 这个 command 来将某个 module 加载。比方说,我有一个 module 叫 hello.o,那使用 insmod hello.o 就可以将 hello 这个 module 载到 kernel 里。观察 /etc/modules 应该就可以看到 hello 这个 module 的名字。如果要将 hello 这个 module 移除,则只要使用 rmmod hello 就可以了。insmod 在加载 module 之后,就会去呼叫 module 所提供的 init_module()。如果传回 0 表示成功,那 module 就会被加载。如果失败,那加载的动作就会失败。一般来讲,我们在 init_module() 做的事都是一些初始化的工作。比方说,你的 module 需要一块内存,那你就可以在 init_module() 做 kmalloc 的动作。想当然尔。cleanup_module() 就是在 module 要移除的时候做的事。做的事一般来讲就是一些善后的工作,比方像把之前 kmalloc 的内存 free 掉。

    由于 module 是载到 kernel 使用的,所以,可能别的 module 会使用你的 module,甚至某些 process 也会使用到你的 module,为了避免 module 还有人使用时就被移除,每个 module 都有一个 use count。用来记录目前有多少个 process 或 module 正在使用这个 module。当 module 的 use count 不等于 0 时,module 是不会被移除掉的。也就是说,当 module 的 use count 不等于 0 时,cleanup_module() 是不会被呼叫的。

    在此,我要介绍三个 macro,是跟 module 的 use count 有关的。

    MOD_INC_USE_COUNT
    MOD_DEC_USE_COUNT
    MOD_IN_USE

    MOD_INC_USE_COUNT 是用来增加 module 的 use count,而 MOD_DEC_USE_COUNT 是用来减少 module 的 use count。至于 MOD_IN_USE 则是用来检查目前这个 module 是不是被使用中。也就是检查 use count 是否为 0。module 的 use count 必须由写 module 的人自己来 maintain。系统并不会自动为你把 use count 加一或减一。一切都得由自己控制。下面有一个例子,但是,并不会介绍这三个 macro 的使用

    方法。将来如果有机会,我再来介绍这三个 macro 的用法。

    这个例子很简单。其实只是示范如何使用 init_module() 以及 cleanup_module() 来写一个 module。当然,这两个 function 只是构成 module 的基本条件罢了。至于 module 里要提供的功能则是看各人的需要。

    main.c
    #define MODULE
    #include <linux/module.h>;
    #include <asm/uaccess.h>;
    int full;
    EXPORT_SYMBOL(full); /* 将 full export 出去 */
    int init_module( void )
    {
    printk( "<5>; Module is loaded/n" );
    return 0;
    }
    void cleanup_module( void )
    {
    printk( "<5>; Module is unloaded/n" );
    }

    关于 printk 是这样子的,它是 kernel 所提供的一个打印讯息的 function。kernel 有 export 这个 function。所以你可以自由的使用它。它的用法跟 printf 几乎一模一样。唯独讯息的开头是 <5>;,其实,不见得这三个字符啦。也可以是 <4>;,<3>;,<7>; 等等的东西。这是代表这个讯息的 prioirty 或 level。<5>; 表示的是跟 KERNEL 有关的讯息。

    main.ver:

    利用 genksyms 产生出来的。

    gcc -E -D__GENKSYMS__ main.c | genksyms -k 2.2.1 >; main.ver

    接下来,就是要把 main.c compile 成 main.o

    gcc -D__KERNEL__ -DMODVERSIONS -DEXPORT_SYMTAB -c /
    -I/usr/src/linux/include/linux -include /
    /usr/src/linux/include/linux/modversions.h /
    -include ./main.ver main.c

    好了。main.o 已经成功的 compile 出来了,现在下一个 command,

    insmod main.o

    检查看 /proc/modules 里是否有 main 这个 module。如果有,表示 main 这个 module 已经载到 kernel 了。再下一个指令,看看 full export 出去的结果。

    ksyms

    结果显示

    Address Symbol Defined by
    c40220e0 full_R355b84b2 [main]
    c401d04c ne_probe [ne]
    c401a04c ei_open [8390]
    c401a094 ei_close [8390]
    c401a504 ei_interrupt [8390]
    c401af1c ethdev_init [8390]
    c401af80 NS8390_init [8390]

    可以看到 full_R355b84b2,表示,我们已经成功的将 full 的名字加上 kernel version 的 information 了。当我们不需要这个 module 时,我们就可以下一个 command,

    rmmod main

    这样 main 就会被移除掉了。再检查看看 /proc/modules 就可以发现 main 那一行不见了。各位现在可以看一下 /var/log/message 这个档案,应该可以发现以两行

    Apr 12 14:19:05 host kernel: Module is loaded
    Apr 12 14:39:29 host kernel: Module is unloaded

    这两行就是 printk 印出来的。

    关于 module 的介绍已经到此告一段落了。其实,使用 module 实在是很简单的一件事。对于要发展 driver 或是增加 kernel 某些新功能的人来讲,用 module 不啻为一个方便的方式。希望这篇文章对各位能有所帮助。


    庄荣城 (J.C. Chuang), cjc86@cs.ccu.edu.tw[/color]

    摘要

    Linux内核模块编程的资料有些纷繁复杂,有的过于简单,有的过于庞杂,我试图用笔记的形式想读者展示怎样来进程Linux模块编程,力图做到简明扼 要,这篇文章也是作为本人备忘的资料,所以有些地方过于简略是难免的。本来这篇文章的目的就是让用户知其然,至于所以然还是请参考相应的资料,其实最好的 资料莫过于Linux Kernel Source。

    适用范围:

        * Linux Kernel >= 2.6.0


    Linux模块简介

    首先这个module不同于microkernel的module,microkernel的module是一个个的daemon进程,工作于用户空 间,Linux的module只是一个内核的目标代码,内核通过执行运行时的连接,来把它整合到kernel中去,所以说Linux的module机制并 没有改变Linux内核为monolithic OS本质,其module也是工作于内核模式,享有内核的所有特权。

    至于为什么要引入Linux Kernle Module(一下简称LKM),我想至少有一下几点:

        * 模块化编程的需要,降低开发和维护成本。
        * 增强系统的灵活性,使得修改一些内核功能而不必重新编译内核和重启系统。
        * 降低内核编程的复杂性,使入门门槛降低。


    相关宏及头文件

    LKM需要包含以下头文件:<linux/kernel.h> <linux/module.h>

    需要定义以下宏:__KERNEL__, MODULE
    一个简单的内核模块示例

    /*file:   hello.c*/
    #ifndef __KERNEL__
           #define __KERNEL__
    #endif
    #ifndef MODULE
           #define MODULE
    #endif
    #include <linux/module.h>
    #include <linux/kernel.h>
    static int __init hello_init(void)
    {
           printk(KERN_ALERT "Hello, my LKM./n");
           return 0;
    }
    static void __exit hello_exit(void)
    {
           printk(KERN_ALERT "Bye, my LKM./n");
    }
    module_init(hello_init);
    module_exit(hello_exit);

    很简答吧,不是吗?这个LKM的功能其实也很简单,就是当通过insmod加载它的时候,他打印Hello, my LKM.通过rmmod卸载它的时候他打印bye, my LKM.一个最基本的内核模块一般都包含有两个函数,一个是初始化函数(比如说这里的hello_init),一个是卸载函数(hello_exit), 当然也可以没有任何函数,只是提供一些变量。但是初始化函数和卸载函数必须成对出现。并且init函数当操作成功时返回值大于等于零,当操作失败时,返回 非零。宏module_init和module_exit用于注册初始化函数和卸载函数。
    LKM的编译

    一个示例的Makefile如下所示

    obj-m := hello.o
    KERNELBUILD := /lib/modules/`uname -r`/build
    default:
            make -C $(KERNELBUILD) M=$(shell pwd) modules
    clean:
            rm -rf *.o .*.cmd *.ko *.mod.c .tmp_versions

    如果这个目录下面还有其它模块,只需要在hello.o后面添加就行了。

    obj-m := hello.o mod.o

    在模块所在目录执行make,等成功后就可以得到我们想要的模块(hello.ko)了。

    如果一个模块存在许多源文件,比如:hello, 由hello1.c hello2.c共同连接而成,需要在Makefile中加入如下行

    hello-objs := hello1.o hello2.o

    LKM的加载

    Linux为用户提供了modutils,用来操纵模块。这个工具集主要包括:

    insmod 安装模块
    rmmod 删除模块
    modprobe 比较高级的加载和删除模块,可以解决模块之间的依赖性
    lsmod 列出已经加载的模块和其信息
    modinfo 用于查询模块的相关信息,比如作者,版权...

    试着用命令insmod hello.ko加载模块,rmmod删除模块,看看有什么事情发生了。你有可能看不见任何输出,难道是有错误发生?No,执行命令tail /var/log/message呵呵,是不是看到了?
    或执行命令:dmesg

    Feb 19 00:07:35 gentux Hello, my LKM.
    Feb 19 00:07:38 gentux Bye, my LKM.

    模块其它信息

    比较常用信息常常包括:作者、描述、版权等,为此LKM为我们提供了如下宏:

    MODULE_AUTHOR("author");
    MODULE_DESCRIPTION("the description");
    MODULE_LICENSE("GPL");
    MODULE_SUPPORTED_DEVICE("dev"); // 设备驱动程序所支持的设备。

    比较常用的Free license有"GPL", "GPL v2", "GPL and additional rights", "Dual BSD/GPL", "Dual MPL/GPL"。
    模块参数

    用户空间的应用程序可以接受用户的参数,LKM也可以做到,只是方式有些不同而已。相关的宏有:

    MODULE_PARM(var, type);
    MODULE_PARM_DESC(var, "the description of the var");

    模块参数的类型(即MODULE_PARM中的type)有一下几种:

        * b byte(unsigned char)
        * h short
        * i int
        * l long
        * s string(char*)

    这些参数最好有默认值,如果有些必要参数用户没有设置可以通过在module_init指定的init函数返回负值来拒绝模块的加载。 LKM还支持数组类型的模块,如果在类型符号前加上数字n则表示最大程度为n的数组,用“-”隔开的数字分别代表最小和最大的数组长度。

    示例:
    MODULE_PARM(var, "4i"); // 最大长度为4的整形数组
    MODULE_PARM(var, "2-6i"); // 最小长度为2,最大长度为6的整形数组

    如何用insmod传入参数,其实man一下就可以了,不过现在的man有些过于简单,所以在此说明一下:

    insmod variable=value[,value2...] ...

    其中value可以用引号括起来,也可以不用。但是有一点“=”前后不能留有空格,并且value中也不能有空格。
    模块符号的导出

    和用户空间的应用程序不同的是,引入一个模块的目的常常是为了给内核提供一些routine,来完成特定的功能,很少有模块什么符号都不导出,为此Linux为用户提供了如下宏:

    EXPORT_SYMBOL(var); // 输出symbol var
    EXPORT_SYMBOL_GPL(var); // 输出的symbol版权为GPL

    模块之间的依赖性

    有的时候两个模块之间可能有依赖性,要加载的模块A,依赖于模块B,此时insmod是无能为力的,只能用modprobe来加载模块和其依赖的模块,否则只能手动一个个加载。

    modprobe通过读取由depmod -a生成的/lib/modules/version/modules.dep来获得其所依赖的模块列表(也有可能是一个模块树),然后调用insmod来一个个按顺序加载。
    命名空间的问题

       1. 对于不需要export的全局symbol最好用static进行修饰,限制其作用域为本文件,以防污染内核的命名空间。
       2. 对于由内核或其它模块export的一些symbol,最好用extern进行修饰,以示其不在本文件。
       3. 在可能用到errno变量的场合,因为内核没有export此symbol,只能有用户自行定义,比如:int errno;

    一个较复杂的模块示例

    /*file:   hello.c*/
    #ifndef __KERNEL__
    #define __KERNEL__
    #endif
    #ifndef MODULE
    #define MODULE
    #endif
    #include <linux/module.h>
    #include <linux/kernel.h>
    MODULE_AUTHOR("xiaosuo <xiaosuo@gmail.com>");
    MODULE_LICENSE("GPL");
    MODULE_DESCRIPTION("This module is a example.");
    static int int_var = 0;
    static const char *str_var = "default";
    static int int_array[6];
    MODULE_PARM(int_var, "i");
    MODULE_PARM_DESC(int_var, "A integer variable");
    MODULE_PARM(str_var, "s");
    MODULE_PARM_DESC(str_var, "A string variable");
    MODULE_PARM(int_array, "2-6i");
    MODULE_PARM_DESC(int_array, "A integer array");
    static int __init hello_init(void)
    {
           int i;
           printk(KERN_ALERT "Hello, my LKM./n");
           printk(KERN_ALERT "int_var %d./n", int_var);
           printk(KERN_ALERT "str_var %s./n", str_var);
           for(i = 0; i < 6; i ++){
                   printk("int_array[%d] = %d/n", i, int_array[i]);
           }
           return 0;
    }
    static void __exit hello_exit(void)
    {
           printk(KERN_ALERT "Bye, my LKM./n");
    }
    module_init(hello_init);
    module_exit(hello_exit);

    2.6内核驱动模块编译方法

    问题虽小,可困扰了好久。。。

    以前在debian下,每次编译都会出一大堆的错误。sm的kernel版也没有问出个所以然来。

    只是怀疑所用的kernel和kernel header版本不一致的原因。debian下装东西基本都是apt-get完事儿。。。很少去编译,也就不好确定,这里究竟有没有问题了。。麻烦的是 sourcelist中,根本没有我用的kernel的header.... 重编内核的心都有了!

    回VMWare装个了Ubuntu...第一次完这个,,只能上当。。

    不能选择安装的软件包。。一狠心,就默认安装了吧。。这可好,足足装了一夜!什么gnome,openoffice全上来了!

    懒得理他。。这次小心翼翼 kernel和kernel header版本都是 2.6.10-5-386...

    这次可以确定问题所在了。。。原来应该这样:

     

    代码模板如下,注意__init 和 __exit

    CODE

    #define MODULE

    #include linux/module.h>

    #include linux/config.h>

    #include linux/init.h>

    static int __init name_of_initialization_routine(void) {

    /*

    * code here

    */

    }

    static void __exit name_of_cleanup_routine(void) {

    /*

    * code here

    */

    }

    module_init(name_of_initialization_routine);

    module_exit(name_of_cleanup_routine);

    MODULE宏也不再是必须的了,在编译时可不定义

    编译过程的区别

    2.4的编译过程如下

    gcc -D__KERNEL__ -DMODULE -I/usr/src/linux-2.4.21/include -O2 -c testmod.c

    要定义__KERNEL__是因为在内核头文件中,某些符号只在定义这个宏后才会公开

    2.6的编译过程

    写一个简单的Makefile只有一行

    obj-m := testmod.o

    然后使用如下命令编译

    make -C /usr/src/linux-2.6.1 SUBDIRS=$PWD modules

    $PWD是你的内核模块所在的目录 使用如上命令时会自动重编译系统的内核模块与你的模块

    所以 保存内核源目录中的版本号与配置文件与你当前运行内核相同是必要的

    编译之后可以

    #insmod testmod.ko

    #rmmod test.o

    over

    展开全文
  • Linux Module And Device Driver

    千次阅读 2014-10-21 16:58:16
    通过查看实际通过看include/linux/init.h源代码,我们可以发现,对于动态加载的模块,其module_init和module_exit定义为: #define module_init(initfn) \ static inline initcall_t __inittest(void) \ ...
  • 你不知道的Linux Kernel——Linux内核的工作原理

    万次阅读 多人点赞 2019-03-06 10:32:18
    Linux内核更新是越来越快了,可能由于Linux的普及,大家都开始关注了,各种安全隐患也越来越多。支持Intel、Alpha、PPC、Sparc、IA-64、ARM、MIPS、Amiga、Atari和IBMs/390等,还支持32位大文件系统。而在Intel平台...
  • Linux模块原理

    2007-09-23 22:13:29
    Linux模块原理.
  • linux ftrace原理

    千次阅读 2016-03-01 21:43:26
    Trace 对于软件的维护和性能分析至关重要,ftrace 是当前 Linux 内核中一种新的 trace 工具。本文介绍 ftrace 的使用和实现原理,并将 ftrace 和 systemTap,LTTng 等软件进行对比,希望读者能够对 ftrace 有一个...
  • 面试专题:Linux运维精华面试题

    千次阅读 多人点赞 2019-03-14 15:31:53
    下面是一名资深Linux运维求职数十家公司总结的Linux运维面试精华,助力大家跳槽找个高薪好工作。 1、什么是运维?什么是游戏运维? 1)运维是指大型组织已经建立好的网络软硬件的维护,就是要保证业务的上线与...
  • 目 录 中文目录格式 中文目录格式 摘要1 关键词1 前言2 第1章Linux设备驱动程序编写方式.3 1设备驱动程序的编写模式 2 module原理 第2章Linux下的驱动设备类型21 1 Character Devices 2 Block Devices 第3章Linux...
  • 前文中介绍了 module 的基本组成部分,现在在上一节的基础上,对 module 做一点点深入的理解。
  • linux进程管理原理

    万次阅读 2017-08-16 05:09:57
    Linux 是一种动态系统,能够适应不断变化的计算需求。linux 计算需求的表现是以进程的通用抽象为中心的。进程可以是短期的(从命令行执行的一个命令),也可以是长期的(一种网络服务)。因此,对进程及其调度进行...
  • 手把手教你如何编写一个Linux驱动并写一个支持物联网的LED演示demo 首先第一步打开电路原理图,找到蜂鸣器部分 首先可以看到VDD 5V也就是说这个蜂鸣器电路需要至少5V的电流才能驱动,后面跟了一个CON2通过原理图...
  • 不知道在什幺时候,Linux 出现了 module 这种东西,的确,它是 Linux 的一大革新。有了 module 之后,写 device driver 不再是一项恶梦,修改 kernel 也不再是一件痛苦的事了。因为你不需要每次要测试 driver 就重新...
  • 以下内容总结自《understanding the linux kernel》 在Linux系统中一个包括但不限于驱动模块,可能需要一个或者多个其他的模块,这些模块又可能需要其他的模块。为了内核能够自动链接模块,内核需要创建一个内核...
  • 在分析驱动之前,先来分析下显示原理,这里以S3C2440为例,看下这个芯片的LCD控制器时序图: VSYNC :帧数据脉冲,脉冲换屏,表示一屏数据开始 HSYNC :行数据脉冲,脉冲换行,表示一行数据开始 LEND :行结束...
  • Linux运维面试题

    千次阅读 2019-08-02 12:57:30
    Linux运维跳槽必备的40道面试精华题(转) 下面是一名资深Linux运维求职数十家公司总结的Linux运维面试精华,助力大家年后跳槽找个高薪好工作。 1、什么是运维?什么是游戏运维? 1)运维是指大型组织已经建立好的网络...
  • Linux platform设备驱动原理及架构

    千次阅读 2019-03-29 10:52:20
    --> __platform_driver_register(drv, THIS_MODULE) --> driver_register(&drv->driver) --> bus_add_driver(drv) --> driver_attach(drv) --> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach) --> __...
  • Linux系统网卡驱动phy工作原理解析

    千次阅读 2020-01-07 20:24:23
    Linux网卡驱动架构 MAC控制器驱动是芯片厂商集成在SDK中 比如三星控制器、海思控制器,这部分驱动厂商已经写好了, linux下自带通用phy驱动...通用phy驱动工作原理 理解改该文件的驱动代码,对网络的识别流程帮助很...
  • linux 内核 :Netlink 原理分析

    千次阅读 2019-06-06 08:58:42
    struct module *module; int (*bind)(struct net *net, int group); void (*unbind)(struct net *net, int group); bool (*compare)(struct net *net, struct sock *sock); int registered; }; ...
  • linux firmware 实现原理

    千次阅读 2019-11-13 13:44:33
    int request_firmware_nowait(struct module * module, bool uevent, const char * name, struct device * device, gfp_t gfp, void * context, void (*cont) (const struct firmware *fw, void *context) ...
  • linux文件系统实现原理简述

    千次阅读 2017-05-06 11:08:10
    [摘要][背景][正文][总结]注意:请使用谷歌浏览器阅读(IE浏览器排版混乱)【摘要】本文将以jffs2文件系统的访问过程为例,从全局视角,介绍一下linux文件系统的实现机理。本文不追求细节实现,旨在通过访问过程...
  • Linux内核头文件提供了一个方便的方法用来管理符号的对模块外部的可见性,因此减少了命名空间的污染(命名空间的名称可能会与内核其他地方定义的名称冲突),并且适当信息隐藏。 如果你的模块需要输出符号给其他模块使用...
  • 主要内容---内核工作原理解析 1, 内核的编译步骤 2, Kconfig和Makefile的使用 3, 内核的裁剪---make ... c, 内核中module_init是什么  d, 挂载是什么意思  e, 祖先进程init是如何被启动的 ------------------...
  • arm linux 内核模块加载过程详解

    千次阅读 2019-02-02 19:14:50
    arm linux 内核模块加载过程详解 基础环境 kernel version:3.10.70 ARCH:arm 参考资料 《深入Linux设备驱动程序内核机制》 《linux设备驱动开发详解》 ...
  • linux内核段属性机制 以subsys_initcall和module_init为例
  • Linux驱动原理

    千次阅读 2009-01-06 22:12:00
    有些吃力,后来自己弄来了Linux的网卡驱动程序硬着头皮看了一遍,终于有了些了解,但是对Linux开发的流程和原理还是有很多不解的地方!就这样折腾了一个星期... Linux系统中,设备驱动程序是操作系统内核的
  • LSM(Linux Security Modules)框架原理解析

    千次阅读 2020-07-01 16:32:44
    1. 基本原理 LSM是内核安全模块的一套框架,本质是插桩法。它的主要有两个特点: 1、在内核安全相关的关键路径上插入了Hook点: 内核安全相关的关键对象有:task_struct(任务和进程)、linux_binprm(程序)、super_...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 38,907
精华内容 15,562
关键字:

linuxmodule原理

linux 订阅