linux設備和驅動加載的先后順序

Linux驅動先注冊總線,總線上可以先掛device,也可以先掛driver,那么究竟怎么控制先后的順序呢。

Linux系統使用兩種方式去加載系統中的模塊動態靜態

靜態加載:將所有模塊的程序編譯到Linux內核中,由do_initcall函數加載

核心進程(/init/main.c)kernel_inità do_basic_setup()àdo_initcalls()該函數中會將在__initcall_start和__initcall_end之間定義的各個模塊依次加載。那么在__initcall_start 和 __initcall_end之間都有些什么呢?

找到/arch/powerpc/kernel/vmlinux.lds文件,找到.initcall.init段:

?

.initcall.init : {

? __initcall_start = .;

? *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)

? __initcall_end = .;

? }

?

可以看出在這兩個宏之間依次排列了14個等級的宏,由于這其中的宏是按先后順序鏈接的,所以也就表示,這14個宏有優先級:0>1>1s>2>2s………>7>7s。

那么這些宏有什么具體的意義呢,這就要看/include/linux/init.h文件:

?

#define pure_initcall(fn)????????????? __define_initcall("0",fn,0)

#define core_initcall(fn)????????????? __define_initcall("1",fn,1)

#define core_initcall_sync(fn)?????????? __define_initcall("1s",fn,1s)

#define postcore_initcall(fn)????????????? __define_initcall("2",fn,2)

#define postcore_initcall_sync(fn)??? __define_initcall("2s",fn,2s)

#define arch_initcall(fn)??????? __define_initcall("3",fn,3)

#define arch_initcall_sync(fn)??????????? __define_initcall("3s",fn,3s)

#define subsys_initcall(fn)????????? __define_initcall("4",fn,4)

#define subsys_initcall_sync(fn)?????? __define_initcall("4s",fn,4s)

#define fs_initcall(fn)?????????????????? __define_initcall("5",fn,5)

#define fs_initcall_sync(fn)???????? __define_initcall("5s",fn,5s)

#define rootfs_initcall(fn)??????????? __define_initcall("rootfs",fn,rootfs)

#define device_initcall(fn)????????? __define_initcall("6",fn,6)

#define device_initcall_sync(fn)?????? __define_initcall("6s",fn,6s)

#define late_initcall(fn)???????? __define_initcall("7",fn,7)

#define late_initcall_sync(fn)???????????? __define_initcall("7s",fn,7s)

?

??? 這里就定義了具體的宏,我們平時用的module_init在靜態編譯時就相當于device_initcall。舉個例子,在2.6.24的內核中:gianfar_device使用的是arch_initcall,而gianfar_driver使用的是module_init,因為arch_initcall的優先級大于module_init,所以gianfar設備驅動的device先于driver在總線上添加。

?

?

系統初始化函數集(subsys_initcall)和初始化段應用

前言:前段時間做一個項目需要設計一個動態庫,并希望在加載庫的同時自動執行一些初始化動作,于是聯想到了linux內核眾子系統的初始化,于是研究之,并在過這程中發現了初始化段的存在,利用初始化段實現了該功能。工作一年,筆記積累多了,慢慢變得雜亂無章,于是開博,一方面整理筆記,梳理知識,另一方面和大家交流,共同進步。

?

?

keyword:subsys_initcall, init, init_call

?

1 系統初始化調用函數集分析(靜態)

1.1 函數定義

?在linux內核代碼里,運用了subsys_initcall來進行各種子系統的初始化,具體怎么初始化的呢?其實并不復雜。以2.6.29內核作為例子。在<include/linux/init.h>下就能找到subsys_initcall的定義:

#define pure_initcall(fn)????????????? __define_initcall("0",fn,0)

#define core_initcall(fn)????????????? __define_initcall("1",fn,1)

#define core_initcall_sync(fn)??? __define_initcall("1s",fn,1s)

#define postcore_initcall(fn)????? __define_initcall("2",fn,2)

#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)

#define arch_initcall(fn)????????????? __define_initcall("3",fn,3)

#define arch_initcall_sync(fn)??? __define_initcall("3s",fn,3s)

#define subsys_initcall(fn)????????? __define_initcall("4",fn,4)

#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)

#define fs_initcall(fn)????????????????? __define_initcall("5",fn,5)

#define fs_initcall_sync(fn)??????? __define_initcall("5s",fn,5s)

#define rootfs_initcall(fn)?????????? __define_initcall("rootfs",fn,rootfs)

#define device_initcall(fn)???????? __define_initcall("6",fn,6)

#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)

#define late_initcall(fn)?????????????? __define_initcall("7",fn,7)

#define late_initcall_sync(fn)????? __define_initcall("7s",fn,7s)

?

而__define_initcall又被定義為

#define __define_initcall(level,fn,id) \

?static initcall_t? __initcall_##fn##id?? __used \

?__attribute__((__section__(".initcall" level ".init"))) = fn

?

所以 subsys_initcall(fn) == __initcall_fn4 它將被鏈接器放于section? .initcall4.init 中。(attribute將會在另一篇文章中介紹)

?

1.2 初始化函數集的調用過程執行過程:

start_kernel->rest_init

系統在啟動后在rest_init中會創建init內核線程

init->do_basic_setup->do_initcalls

do_initcalls中會把.initcall.init.中的函數依次執行一遍:

?

for (call = __initcall_start; call < __initcall_end; call++) {

.??? .....

result = (*call)();

.??? ........

}

?

這個__initcall_start是在文件<arch/xxx/kernel/vmlinux.lds.S>定義的:

?.initcall.init : AT(ADDR(.initcall.init) - LOAD_OFFSET) {

?? __initcall_start = .;

?? INITCALLS

?? __initcall_end = .;

? }

?

INITCALLS被定義于<asm-generic/vmlinux.lds.h>:

#define INITCALLS?????? \

?? *(.initcall0.init)????? \

?? *(.initcall0s.init)????? \

?? *(.initcall1.init)????? \

?? *(.initcall1s.init)????? \

?? *(.initcall2.init)????? \

?? *(.initcall2s.init)????? \

?? *(.initcall3.init)????? \

?? *(.initcall3s.init)?? ???\

?? *(.initcall4.init)????? \

?? *(.initcall4s.init)????? \

?? *(.initcall5.init)????? \

?? *(.initcall5s.init)????? \

?? *(.initcallrootfs.init)????? \

?? *(.initcall6.init)????? \

?? *(.initcall6s.init)????? \

?? *(.initcall7.init)????? \

?? *(.initcall7s.init)

?

?

2 基于模塊方式的初始化函數(動態)

2.1函數定義subsys_initcall的靜態調用方式應該講清楚來龍去脈了,現在看看動態方式的初始化函數調用(模塊方式)。在<include/linux/init.h>里,如果MODULE宏被定義,說明該函數將被編譯進模塊里,在系統啟動后以模塊方式被調用。

#define core_initcall(fn)???????? module_init(fn)

#define postcore_initcall(fn)? module_init(fn)

#define arch_initcall(fn)??????? module_init(fn)

#define subsys_initcall(fn)??? module_init(fn)

#define fs_initcall(fn)???????????? module_init(fn)

#define device_initcall(fn)???? module_init(fn)

#define late_initcall(fn)???????? module_init(fn)

這是在定義MODULE的情況下對subsys_initcall的定義,就是說對于驅動模塊,使用subsys_initcall等價于使用module_init

?

?

2.2 module_init 分析下面先看看module_init宏究竟做了什么

#define module_init(initfn)???? \

?static inline initcall_t __inittest(void)? \ /*定義此函數用來檢測傳入函數的類型,并在編譯時提供警告信息*/

?{ return initfn; }???? \

?int init_module(void) __attribute__((alias(#initfn))); /*聲明init_modlue為 initfn的別名,insmod只查找名字為init_module函數并調用*/

?

typedef int (*initcall_t)(void); /*函數類型定義*/

?

在以模塊方式編譯一個模塊的時候,會自動生成一個xxx.mod.c文件, 在該文件里面定義一個struct module變量,并把init函數設置為上面的init_module() 而上面的這個init_module,被alias成模塊的初始化函數(參考<gcc關鍵字:__attribute__, alias, visibility, hidden>)。

?

也就是說,模塊裝載的時候(insmod,modprobe),sys_init_module()系統調用會調用module_init指定的函數(對于編譯成>模塊的情況)。

?

2.3 module的自動加載內核在啟動時已經檢測到了系統的硬件設備,并把硬件設備信息通過sysfs內核虛擬文件系統導出sysfs文件系統由系統初始化腳本掛載到/sys上udev掃描sysfs文件系統,根據硬件設備信息生成熱插拔(hotplug)事件,udev再讀取這些事件,生成對應的硬件設備文件。由于沒有實際的硬件插拔動作,所以這一過程被稱為coldplug。

udev完成coldplug操作,需要下面三個程序:

udevtrigger——掃描sysfs文件系統,生成相應的硬件設備hotplug事件。

udevd——作為deamon,記錄hotplug事件,然后排隊后再發送給udev,避免事件沖突(race conditions)。

udevsettle——查看udev事件隊列,等隊列內事件全部處理完畢才退出。

要規定事件怎樣處理就要編寫規則文件了.規則文件是udev的靈魂,沒有規則文件,udev無法自動加載硬件設備的驅動模塊。它一般位于<etc/udev/rules.d>

?

?

3初始化段的應用這里給出一個簡單的初始化段的使用例子,將a.c編譯成一個動態庫,其中,函數a()和函數c()放在兩個不同的初始化段里,函數b()默認放置;編譯main.c,鏈接到由a.c編譯成的動態庫,觀察各函數的執行順序。

# cat a.c #include <stdio.h>

typedef int (*fn) (void);

int a(void)

{

??? printf("a\n");

??? return 0;

}

__attribute__((__section__(".init_array.2"))) static fn init_a = a;

int c(void)

{

??? printf("c\n");

??? return 0;

}

__attribute__((__section__(".init_array.1"))) static fn init_c = c;

?

int b()

{

??? printf("b\n");

??? return 0;

}

?

# cat main.c

#include<stdio.h>

int b();

int main()

{

??? printf("main\n");

??? b();

}

?

# cat mk.sh

?

gcc -fPIC -g -c a.c

gcc -shared -g -o liba.so a.o

cp liba.so /lib/ -fr

gcc main.c liba.so

ldconfig

./a.out?

?

# gcc -fPIC -g -c a.c

# gcc -shared -g -o liba.so a.o

# cp liba.so /lib/

# gcc main.c liba.so

# ldconfig

# ./a.out

a

c

main

b

?

?

在類unix操作系統中,驅動加載方式一般分為:動態加載和靜態加載,下面分別對其詳細論述。
一、動態加載
動態加載是將驅動模塊加載到內 核中,而不能放入/lib/modules/下。
??? 在2.4內核中,加載驅動命令為:insmod ,刪除模塊為:rmmod
??? 在2.6以上內核中,除了insmod與rmmod外,加載命令還有modprobe;
??? insmod與modprobe不同之處:
??? insmod 絕對路徑/××.o,而modprobe ××即可,不用加.ko或.o后綴,也不用加路徑;最重要的一點是:modprobe同時會加載當前模塊所依賴的其它模塊;
??? lsmod查看當前加載到內核中的所有驅動模塊,同時提供其它一些信息,比如其它模塊是否在使用另一個模塊。
二、靜態加載
(一)概念
??? 在執行make menuconfig命令進行內核配置裁剪時,在窗口中可以選擇是否編譯入內核,還是放入/lib/modules/下相應內核版本目錄中,還是不選。
(二) 操作步驟
??? linux設備一般分為:字符設備、塊設備和網絡設備,每種設備在內核源代碼目錄樹drivers/下都有對應的目錄,其加載方法類似,以下以字符設備靜 態加載為例,假設驅動程序源代碼名為ledc.c,具體操作步驟如下:
??? 第一步:將ledc.c源程序放入內核源碼drivers/char/下;
??? 第二步:修改drivers/char/Config.in文件,具體修改如下:
?? ?? ?? 按照打開文件中的格式添加即可;
?? ?? ?? 在文件的適當位置(這個位置隨便都可以,但這個位置決定其在make menuconfig窗口中所在位置)加入以下任一段代碼:
?? ?? ??
?? ?? ?? tristate 'LedDriver' CONFIG_LEDC
?? ?? ?? if [ "$CONFIG_LEDC" = "y" ];then
?? ?? ?? bool '?? Support for led on h9200 board' CONFIG_LEDC_CONSOLE
?? ?? ?? fi
?? ?? ?? 說明:以上代碼使用tristate來定義一個宏,表示此驅動可以直接編譯至內核(用*選擇),也可以編制至/lib/modules/下(用M選擇), 或者不編譯(不選)。

?? ?? ?? bool 'LedDriver' CONFIG_LEDC
?? ?? ?? if [ "$CONFIG_LEDC" = "y" ];then
?? ?? ?? bool '?? Support for led on h9200 board' CONFIG_LEDC_CONSOLE
?? ?? ?? fi
?? ?? ?? 說明:以上代碼使用tristate來定義一個宏,表示此驅動只能直接編譯至內核(用*選擇)或者不編譯(不選),不能編制至/lib/modules/ 下(用M選擇)。
???
??? 第三步:修改drivers/char/Makefile文件
?? ?? ?? 在適當位置加入下面一行代碼:
?? ?? ?? obj-$(CONFIG_LEDC)?? +=?? ledc.o
?? ?? ?? 或者在obj-y一行中加入ledc.o,如:
?? ?? ?? obj-y += ledc.o mem.o 后面不變;

??? OK,經過以上的設置就可以在執行make menuconfig命令后的窗口中的character devices---> 中進行選擇配置了。選擇后重新編譯就ok了。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/253755.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/253755.shtml
英文地址,請注明出處:http://en.pswp.cn/news/253755.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CMOS 圖像傳感器——Skipping 和 Binning 模式

在通常的CMOS讀取方式中&#xff0c;由于像素讀取規模的差異&#xff0c;不同的分辨率對應不同的幀率。在通道帶寬固定的前提下&#xff0c;想要提高幀率就要考慮是否需要縮小視野&#xff08;外圈裁切&#xff09;。若不希望視野縮小&#xff0c;需要減少采樣的分辨率。 常用的…

DAVINCI DM365-368中 linux-2.6.32的移植

http://www.360doc.com/content/12/0318/16/532901_195392228.shtml 很詳細的一篇文章&#xff0c;在此感謝了&#xff01; http://www.rosoo.net/a/201001/8316.html DM系列芯片外設詳細介紹

Jacoco--測試覆蓋率工具

介紹JaCoCo&#xff08;Java Code Coverage&#xff09;是一種分析單元測試覆蓋率的工具&#xff0c;使用它運行單元測試后&#xff0c;可以給出代碼中哪些部分被單元測試測到&#xff0c;哪些部分沒有沒測到&#xff0c;并且給出整個項目的單元測試覆蓋情況百分比&#xff0c;…

HTML 標記大全參考手冊

1.文件結構 文件類型 <HTML></HTML> &#xff08;放在文檔的開頭與結尾&#xff09; 文件主題 <TITLE></TITLE> &#xff08;必須放在「文頭」區塊內&#xff09; 文頭 <HEAD></HEAD> &#xff08;描述性資料&#xff0c;如「主題」&#…

APB協議學習

APB(Advanced Peripheral Bus) 1、APB的概述與特點 APB主要用于低帶寬的周邊外設之間的連接&#xff0c;例如UART、1284等&#xff0c;它的總線架構不像AHB支持多個主模塊&#xff0c;在APB里面唯一的主模塊就是APB 橋。其特性包括&#xff1a;兩個時鐘周期傳輸&#xff1b;無…

私有協議棧開發

通信協議從廣義上區分&#xff0c;可以分為公有協議和私有協議。由于私有協議的靈活性&#xff0c;它往往會在某個公司或者組織內部使用&#xff0c;按需定制&#xff0c;也因為如此&#xff0c;升級起來會非常方便&#xff0c;靈活性好。絕大多數的私有協議傳輸層都基于TCP/IP…

制作NFS

最近學習NFS&#xff0c;用本地測試. 以下是我的測試過程 環境 ubuntu 10.4 vm 7.1 終端 ifconfig 得到 ubuntu資料 INET ADDR 192.168.0.4 BCAST 192.168.0.255 MASK 255.255.255.0 一 安裝NFS $ sudo apt-get install nfs-kernel-server $ sudo apt-get install nfs…

【筆記篇】C#筆記2

返回目錄&#xff1a;目錄請戳這里~ C#數組 基本概念不提。。int[] a; bool[] b new bool[10]; float[] c {0.5, 57.0, 233.3, 12345.67 }; double[] d new double[/*3*/]{233.33, 1926.0817, 4396.0 }; 然后數組和指針有很大的不同。。。 Array類不會用…… 有多維數組和…

SFB 項目經驗-51-某上市企業2千人Exchange 2013升級2016高可用之傷01

SFB 項目經驗-51-某上市企業2千人Exchange 2013升級2016高可用之傷01&#xff08;帶病撰寫項目實戰筆記&#xff09;問題描述&#xff1a;2000人企業使用Exchange 2013郵件服務器標準版&#xff0c;n年!1&#xff09;問題1&#xff1a;標準版僅支持5個郵箱數據庫。2&#xff09…

數字圖像處理——2D降噪

圖像降噪處理主要分為2D&#xff08;空域&#xff09;與3D降噪&#xff08;時域/多幀&#xff09;&#xff0c;而2D降噪由于相關的實現算法豐富&#xff0c;效果各異&#xff0c;有著豐富的研究價值。理解2D降噪算法的流程&#xff0c;也對其他的增強算法有很大的幫助&#xff…

項目開發(Require + E.js)

最近在做的幾個項目&#xff0c;分別用了不同的框架跟方式&#xff0c;有個H5的項目&#xff0c;用了vue框架&#xff0c; 這個項目我還沒有正式加入進去&#xff0c; 等手頭的這個項目完成就可以去搞vue了&#xff0c; 現在手頭的這個項目是一個招聘的項目&#xff0c; 用到了…

五個常用的Linux監控腳本代碼

bash中 2>&1 & 的解釋 1、首先&#xff0c;bash中0&#xff0c;1&#xff0c;2三個數字分別代表STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO&#xff0c;即標準輸入&#xff08;一般是鍵盤&#xff09;&#xff0c;標準輸出&#xff08;一般是顯示屏&#xff0c;準…

AHB協議學習

1. 簡介 AHB(Advanced High Performance Bus)總線規范是AMBA(Advanced Microcontroller Bus Architecture) V2.0總線規范的一部分&#xff0c;AMBA總線規范是ARM公司提出的總線規范&#xff0c;被大多數SoC設計采用&#xff0c;它規定了AHB (Advanced High-performance Bus)、A…

jquery lazy load

LazyLoad是一個Js編寫的Jq插件,它可以延遲加載頁面中的圖片,在瀏覽器可視范圍中的圖片會被加載。如何使用&#xff1a;LazyLoad依賴于Jquery&#xff0c;在html的結尾處 ,就是在</body>前。<script type"text/javascript" src"jquery.js"><…

linux內核中的循環緩沖區

Linux內核中的循環緩沖區&#xff08;circular buffer&#xff09;為解決某些特殊情況下的競爭問題提供了一種免鎖的方法。這種特殊的情況就是當生產者和消費者都只有一個&#xff0c;而在其它情況下使用它也是必須要加鎖的。 循環緩沖區定義在include/linux/kfifo.h中&#xf…

js的規范寫法ES5(自己以后按照這樣寫)

1、引號的使用&#xff0c;單引號 優先&#xff08;如果不是引號嵌套&#xff0c;不要使用雙引號&#xff09; 正常情況&#xff1a;console.log(hello there) 雙引號轉碼&#xff1a; $("<div classbox>") 2、空格的使用問題&#xff1a;&#xff08…

刪除本地git的遠程分支和遠程刪除git服務器的分支

在項目中使用git管理代碼后&#xff0c;有些時候會創建很多不同名稱的分支&#xff0c;以此區分各個分支代碼功能。 而隨著代碼的合并&#xff0c;以前的分支就可能不再需要保存了&#xff0c;所以就要對沒有用的分支進行刪除&#xff0c;包括緊急回滾時從中抽取某一個版本記錄…

數字圖像處理——引導濾波

一、概述 引導濾波是由何愷明等人于2010年發表在ECCV的文章《Guided Image Filtering》中提出的&#xff0c;后續于2013年發表。引導過濾器根據局部線性模型原理&#xff0c;通過考慮引導圖像的內容來計算過濾輸出&#xff0c;引導圖像可以是輸入圖像本身或另一個不同的圖像。具…

Ubuntu 18.04換國內源

2019獨角獸企業重金招聘Python工程師標準>>> 參考文檔&#xff1a; https://blog.csdn.net/zhangjiahao14/article/details/80554616 https://blog.csdn.net/xiangxianghehe/article/details/80112149 1.復制源文件備份&#xff0c;以防萬一 我們要修改的文件是sour…

video4linux簡介

Video4linux&#xff08;簡稱V4L),是linux中關于視頻設備的內核驅動,現在已有Video4linux2&#xff0c;還未加入linux內核&#xff0c;使用需自己下載補丁。在Linux中&#xff0c;視頻設備是設備文件&#xff0c;可以像訪問普通文件一樣對其進行讀寫&#xff0c;攝像頭在/dev/v…