日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > linux >内容正文

linux

linux 模块化编译,手把手教Linux驱动1-模块化编程 module

發(fā)布時(shí)間:2024/10/14 linux 101 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux 模块化编译,手把手教Linux驱动1-模块化编程 module 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

大家好,從本篇起,一口君將手把手教大家如何來(lái)學(xué)習(xí)Linux驅(qū)動(dòng),預(yù)計(jì)會(huì)有20篇關(guān)于驅(qū)動(dòng)初級(jí)部分知識(shí)點(diǎn)。本專題會(huì)一直更新,有任何疑問(wèn),可以留言或者加我微信。

Linux的開發(fā)者,遍布世界各地,他們相互之間覺(jué)大數(shù)估計(jì)都不認(rèn)識(shí)。如果真的是對(duì)這些開發(fā)者進(jìn)行統(tǒng)一管理,那是很難做到的。所以大牛們,在設(shè)計(jì)Linux內(nèi)核的時(shí)候,融入了模塊化的思想。也就是說(shuō),現(xiàn)在大家已經(jīng)有一個(gè)現(xiàn)成的Linux操作系統(tǒng)了,所有的開發(fā)者寫的代碼對(duì)于這個(gè)Linux操作系統(tǒng)而言都是一個(gè)模塊,開發(fā)者可以模塊的形式將自己的代碼添加到內(nèi)核,也可以從操作系統(tǒng)中卸載自己的模塊。這種思想,在實(shí)際的開發(fā)中特別別有用。

例如:在你的設(shè)備上已經(jīng)運(yùn)行了一個(gè)成熟的Limux操作系統(tǒng),由于客戶的需求變化,你需要向這個(gè)操作系統(tǒng)上添加一些功能。現(xiàn)在你有兩種做法:

第一種:獲得Linux源代碼,然后修改,添加功能,貌似挺牛,但是如果你寫的代碼不能一次性到達(dá)效果,你就必須去修改,這樣就每次必須重新編譯內(nèi)核,是不是很麻煩。最可怕的是你一不小心,把內(nèi)核源碼給修改錯(cuò)了,那該怎么辦呀?

第二種:快速編寫自己的功能代碼,然后以模塊的形式添加到Linux操作系統(tǒng)中,然后測(cè)試,發(fā)現(xiàn)不行,卸載模塊,繼續(xù)修改代碼,添加模塊(高富帥的干活方式),。。是不是比使用第一種方法的苦逼程序員要輕松很多呀!

大家需要注意的是,一般我們都是通過(guò)模塊化的方法向Linux操作系統(tǒng)添加驅(qū)動(dòng)程序,那些Linux核心的代碼,我個(gè)人覺(jué)得沒(méi)有幾個(gè)人會(huì)覺(jué)得不好,需要重新修改。

Linux 內(nèi)核模塊主要由以下幾個(gè)部分組成:

  • 模塊加載函數(shù)(必須):當(dāng)通過(guò)insmod命令加載內(nèi)核模塊時(shí),模塊的加載函數(shù)會(huì)自動(dòng)被內(nèi)核執(zhí)行,完成本模塊相關(guān)初始化工作;

    模塊卸載函數(shù)(必須):當(dāng)通過(guò)rmmod命令卸載模塊時(shí),模塊的卸載函數(shù)會(huì)自動(dòng)被內(nèi)核執(zhí)行,完成與模塊加載函數(shù)相反的功能;

    模塊許可證聲明(必須):模塊許可證(LICENCE)聲明描述內(nèi)核模塊的許可權(quán)限,如果不聲明LICENCE,模塊被加載時(shí)將收到內(nèi)核被污染的警告。大多數(shù)

    模塊參數(shù)(可選):模塊參數(shù)是模塊被加載的時(shí)候可以被傳遞給他的值,它本身對(duì)應(yīng)模塊內(nèi)部的全局變量;

    模塊導(dǎo)出符號(hào)(可選):內(nèi)核模塊可以導(dǎo)出符號(hào)(symbol,對(duì)應(yīng)于函數(shù)或變量),這樣其他模塊可以使用本模塊中的變量或函數(shù);

    模塊作者等信息聲明(可選)。

  • 直接看代碼,no code no bb!

    看完上面的代碼,請(qǐng)相信,你已經(jīng)對(duì)模塊化編程有了一個(gè)基本的認(rèn)識(shí)。上面這段代碼雖然很簡(jiǎn)單,但是他包含了Linux內(nèi)核模塊化編程需要的所有信息。

    我們來(lái)一起總結(jié)一下Linux內(nèi)核模塊化編程必備的步驟:

    ??包含linux/init.h和inux/module.h這兩個(gè)頭文件;

    通過(guò)MODULE LICENSE("GPL"),告訴內(nèi)核你的模塊遵從"GPL"協(xié)議,這個(gè)事情必須得做。如果不知道GPL的讀者自己去查找相關(guān)資料;Linux能夠成功一個(gè)關(guān)鍵因素就是遵循了GPL,從而一發(fā)不可收拾,在全球蔓延開來(lái)。

    MODULE_AUTHOR("yikoulinux")用來(lái)指定編寫這個(gè)模塊的作者,可以不寫。

    編寫功能代碼,注意這里沒(méi)有main函數(shù),這里叫模塊的入口函數(shù),函數(shù)名一般叫“xxx_init”,這里叫"hello_init"。名字是不是一定要好了,你可以把模塊的入口函數(shù)當(dāng)做你的main兩數(shù),你的代碼就從這個(gè)地方起步吧!

    那這個(gè)函數(shù)什么時(shí)候被調(diào)用呢?在模塊加載到Linux內(nèi)核的時(shí)候,Linux內(nèi)核會(huì)調(diào)用這個(gè)函數(shù)模塊的退出函數(shù),這個(gè)函數(shù)的名字一般叫“xxx_exit”,這里叫“hello_exit”。這個(gè)函數(shù)里,我們一般會(huì)做些資源的釋放。在模塊卸載的時(shí)候會(huì)被調(diào)用到。當(dāng)一個(gè)模塊卸載的時(shí)候,我們肯定要把它占用的資源釋放掉,不然不就造成資源浪費(fèi)了。

    告訴內(nèi)核,你的模塊入入口和模塊出口。Linux內(nèi)核提供了兩個(gè)宏,分別是:module_init 和 module_exit.

    下面我們就來(lái)詳細(xì)說(shuō)一下printk函數(shù)。

    printk的用法和printf類似,print用于用戶空間,printk用于內(nèi)核空間。用printk函數(shù)時(shí),內(nèi)核會(huì)根據(jù)日志級(jí)別,可能把消息打印到當(dāng)前控制臺(tái)上,這個(gè)控制臺(tái)通常是一個(gè)字符模式的終端、一個(gè)串口打印機(jī)或是一個(gè)并口打印機(jī)。

    這些消息正常輸出的前提是:日志輸出級(jí)別小于console_loglevel(在內(nèi)核中數(shù)字越小優(yōu)先級(jí)越高)。

    日志級(jí)別一共有8個(gè)級(jí)別,printk的日志級(jí)別定義如下(在include/linux/kern_levels.h中);

    #define?KERN_EMERG?KERN_SOH?"0"?/system?is?unusable/#define?KERN_ALERT?KERN_SOH?"1"?/action?must?be?taken?immediately/#define?KERN_CRIT?KERN_SOH?"2"?/critical?conditions/#define?KERN_ERR?KERN_SOH?"3"?/error?conditions/#define?KERN_WARNING?KERN_SOH?"4"?/warning?conditions/#define?KERN_NOTICE?KERN_SOH?"5"?/normal?but?significant?condition/#define?KERN_INFO?KERN_SOH?"6"?/informational/#define?KERN_DEBUG?KERN_SOH?"7"?/debug-level?messages/#define?KERN_DEFAULT?KERN_SOH?"d"?/the?default?kernel?loglevel/

    沒(méi)有指定日志級(jí)別的printk語(yǔ)句默認(rèn)采用的級(jí)別是DEFAULT_MESSAGE_LOGLEVEL(這個(gè)默認(rèn)級(jí)別一般為<4>,即與KERN_WARNING在一個(gè)級(jí)別上)。

    我們可以通過(guò)cat/proc/sys/kemel/printk這個(gè)文件,查看系統(tǒng)默認(rèn)的日志級(jí)別

    printk,其實(shí)不用想那么復(fù)雜,你就把它當(dāng)做printf使用也可以的,在這里我們還不能測(cè)試printk輸出的消息,是否能到控制臺(tái)上,因?yàn)槲覀儾恢廊绾尉幾g我們的模塊代碼、如何加載我們的模塊、如何卸載我們的模塊。

    好,接下來(lái)我們來(lái)看看,如何編譯我們的模塊。

    這里,先給出Linux模塊化編譯的流程:

    模塊的編譯分兩步:

    第一步:調(diào)用linux源碼樹的Makefile進(jìn)行收集編譯一個(gè)模塊所需要的信息

    第二步:linux源碼樹的Makefile在收集完信息后,調(diào)用模塊的Makefile。獲取需要編譯成模塊的“.c”文件,最后生成模塊文件

    明白了模塊的編譯流程,接下來(lái)我們就來(lái)看具體如何編寫模塊的Makefile

    #?KERNELRELEASE?:在內(nèi)核源碼樹的Makefile中定義,在當(dāng)前的Makefile中,它的值為空#

    (shell?pwd)獲得當(dāng)前路徑

    在這里插入圖片描述

    我們來(lái)分析Makefile的執(zhí)行過(guò)程

  • 在模塊的源代碼目錄下執(zhí)行make,此時(shí),宏“KERNELRELEASE”【內(nèi)核源碼樹的Makefile會(huì)定義】沒(méi)有定義,因此進(jìn)入else;

  • 記錄內(nèi)核路徑KDIR和當(dāng)前工作目錄PWD;

  • 由于make 后面沒(méi)有目標(biāo),所以make會(huì)在Makefile中的第一個(gè)不是以.開頭的目標(biāo)作為默認(rèn)的目標(biāo)執(zhí)行,于是all成為make的目標(biāo);

    all:的第一個(gè)命令

  • 類似于printf函數(shù),編譯經(jīng)過(guò)此處會(huì)打印提示信息。

  • make的第二條命令會(huì)執(zhí)行 make -C (PWD) modules

    翻譯下就是:

  • make?-C?/lib/modules/3.2.0-29-generic-pae/build?M=/home/peng/driver/1/module?modules-C?表示到存放內(nèi)核的目錄執(zhí)行其Makefile,M=$(PWD)表示返回到當(dāng)前目錄,modules表示編譯成模塊的意思

    之所以這么寫是由內(nèi)核源碼樹的頂層Makefile告訴我們的,當(dāng)我們調(diào)用Linux內(nèi)核源碼樹頂層的Makefile時(shí),找到的是頂層Makefile的“modules”目標(biāo)。我們來(lái)看下頂層Makefile的modules目標(biāo)寫了什么:

    在這里插入圖片描述

    【截取了部分內(nèi)容,我們沒(méi)有必要全部了解,只需要關(guān)心紅色部分即可,特別是對(duì)應(yīng)的英文注釋】

  • 找到modules目標(biāo)后,接下來(lái)Linux源碼樹的頂層Makeflle就需要知道是將那些".c"文件編譯成模塊。誰(shuí)告訴它呢?是的,模塊的Makefile文件。所以接下來(lái)就會(huì)回調(diào)模塊的Makefile。需要注意的是,此時(shí)KERNELRELEASE已經(jīng)在Linux內(nèi)核源碼樹的頂層Makefile中定義過(guò)了,所以此時(shí)它獲得信息是:

    obj-m:=hello.o

    obj-m表示會(huì)將hello.o目標(biāo)編譯成.ko模塊;它告訴linux源碼樹頂層Makefile是動(dòng)態(tài)編譯(編譯成模塊)而不是編譯進(jìn)內(nèi)核(obj-y),linux源碼樹頂層Makefile會(huì)根據(jù)hello.o找到hello.c文件

  • 將模塊文件hello.c編譯為.o,然后再將多個(gè)目標(biāo)鏈接為.ko。

  • 最終編譯結(jié)果如下:

    由執(zhí)行結(jié)果可知,Makefile最終被調(diào)用了三次

    1) 執(zhí)行命令make調(diào)用

    2) 被linux內(nèi)核源碼樹的頂層Makefile調(diào)用,產(chǎn)生.o文件

    3) 被linux源碼樹頂層Makefile調(diào)用,將.o文件鏈接生成.ko文件

    如何將編譯好的模塊添加到Linux內(nèi)核?如何從Linux內(nèi)核將我們的模塊卸載下來(lái)?

    1.模塊的加載命令

    insmod?xxx.ko

    例如:在ubuntu系統(tǒng)中添加自己寫的模塊

    sudo?insmod?hello.ko

    注意:在Linux系統(tǒng)中只有超級(jí)用戶權(quán)限才可以添加模塊到內(nèi)核。

    2.查看系統(tǒng)中的模塊命令

    lsmod

    例如:在系統(tǒng)中搜索自己添加的hello模塊

    sudo?lsmod?|?grep?hello

    3.卸載模塊命令

    sudo?rmmod?模塊名

    例如:卸載系統(tǒng)中的hello模塊

    sudo?mmod?hello

    4.查看加載模塊和卸載模塊通過(guò)printk打印的信息命令

    dmesg或dmesg|tail

    這個(gè)命令主要是從Linux內(nèi)核的ring buffer(環(huán)形緩沖區(qū))中讀取信息的。

    那什么是ring buffer呢?在Limux系統(tǒng)中,所有通過(guò)printk打印出來(lái)的信息都會(huì)送到ring buffer中。我們知道,我們打印出來(lái)的信息是需要在控制臺(tái)設(shè)備上顯示的。在Linux內(nèi)核初始化的時(shí)候,控制臺(tái)設(shè)備并沒(méi)有初始化的時(shí)候,使用printk會(huì)不會(huì)有問(wèn)題

    控制臺(tái)設(shè)備,因?yàn)榇藭r(shí)printk只是把信息輸送到ring buffer中,等控制臺(tái)設(shè)備初始化好后,在根據(jù)ring buffer中消息的優(yōu)先級(jí)決定是否需要輸送到控制臺(tái)設(shè)備上。

    如何清空ring buffer呢?

    ?sudo?dmesg?-c

    操作結(jié)果如下:

    一口君操作全部在特權(quán)模式下,如果在普通用戶權(quán)限下前面加sudo。

    更多嵌入式資料,請(qǐng)關(guān)注公眾號(hào): 一口Linux

    總結(jié)

    以上是生活随笔為你收集整理的linux 模块化编译,手把手教Linux驱动1-模块化编程 module的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。