linux驅動開發經驗逐步積累2

注:筆記多少會有問題,多多包涵。只是作爲一個記錄而已


1. cdev_add的核心思想
cdev_add允許添加一個字符設備到內核,其核心是kobj_map,也可以添加一個字符設備集合,他可以包含count個連續的子設備號,此時dev_t dev爲該字符設備集的base設備號,如cdev_add(cdev, 81, 256)。
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
    p->dev = dev;
    p->count = count;
    return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}
kobj_map核心是構建一個struct probe包含一個字符設備集合(rang可以是1,最大是256個),這說明其他81號字符設備,均落在一個probe中,即只是一個cdev存在,這使得操作設備節點時,所以major爲81的設備都是同一個cdev,使得最終操作的file ops都是一致的。。

當然對於上述將256個設備集合在一個cdev中,也可以通過cdev_add(cdev, dev_t, 1)來單個註冊,只是每次kobj_map時都會存在一個probe,256次就是256個probe list,這就允許在操作每個設備節點有自己的cdev以及各自獨立的file ops操作。在linux驅動中,同一主設備號的設備,可能最終面臨的file ops接口會有不同。

2 linux file多次open問題
那麼有沒有不同的文件描述符使用同一個 file 結構體呢?答案是肯定的,使用 Linux 中的 dup 或 dup2 系統調用即可複製文件描述符,這樣便可以使兩個文件描述符指向同一個 file 結構體。這時,如果使用這兩個文件描述符進行的讀操作是不獨立的。
假設 fd1 和 fd2 兩個文件描述符都指向同一個 file 結構體,當使用 close 系統調用關閉 fd1後, fd2 仍可以使用。這是因爲在 file 結構體中還維護者一個引用計數 count,當 close(fd1) 後,count 減 1,只有在 count 爲 0 時,內核纔會真正的釋放 file 結構體真正關閉文

3 linux file中的f_ops表示當前file被讀寫後產生的位置偏移量,每次對文件的讀寫操作都是從f_ops開始即每次讀寫多少個字節後f_ops都會自動偏移,使得下次開始時就是從最新的位置開始讀寫數據,即在讀寫都是基於上次文件f_ops所在的位置開始。但實際使用中可以通過lseek來修改該f_ops的位置,便於讀寫。
用戶空間 內核
open() sys_open(), filp_open()
close() sys_close(), filp_close()
read() sys_read(), filp_read()
write() sys_write(), filp_write()

4cscope -Rbq創建cscope索引庫

5 "C:\Program Files (x86)\PuTTY\putty.exe" -load xxx -ssh -l zezhigon -pw gzz@19890908188

“[]”表示option,因此可以定義一個只有node name的空節點

7mount命令掛載不同的文件系統
mount 掛載的文件系統類型 設備 掛載的目錄

8 linux內核中page和pfn,以及內核physical address和virtual address之間的關係。
page_to_pfn:通過page找到所屬的物理頁幀號。
pfn_to_page::通過物理頁幀號找到對應的page。//一般page的屬性中包含pfn.
virt_to_page:通過內核虛擬地址找到所屬的page
page_to_phys:通過page中的pfn轉爲物理地址,一般是以pfn<<PAGE_SHIFT.
page_address:通過page找到所屬頁對應的虛擬地址(896M地段內存線性,高端內存映射通過hash表維護從而獲取虛擬地址)

9 linux errno宏可以用於處理當前錯誤的原因,常用於fopen等操作

10 在init.rc中定義 mount debugfs debugfs /sys/kernel/debug,掛載debugfs(ramfs)文件系統到/sys/kernel/debug所在的super root根目錄。
mount selinux selinux /sys/fs/selinux

12 每個掛載的文件系統super inode根節點均爲/,一般掛載點如/dev, /sys等位置的inode和dentry都是屬於rootfs文件系統,當ramfs文件系統或者sysfs選擇其作爲掛載點時,會新建一個super inode的root,也就是super_block,來作爲新的文件系統入口inode,其特點是不存在dentry的概念。只有當在/下開始建立file或者dir時,纔會根據super inode的fops來建立新的inode。即根文件系統以“/” super inode的inode_ops分別建立dev和sys這兩個目錄,然後當mount掛載sysfs和tmpfs系統到/dev,/sys下並創建各自的super indo,可認爲/即爲該super inode root,如/dev/fb0,/sys/class
   當處理當掛載點時,他本質還是前一級別的文件系統,更具inode/dentry中掛載點的屬性,來判斷是非屬於掛載點,如果是的話將掛載文件的root inode/dentry修正爲當前的目錄path_look_up的起點,繼續查看比如/dev/fb0,fb0在新的文件系統中的dentry,如果沒有則新建dentry,同時根據parent的inode ops來創建dentry對應的inode,並將兩者綁定,從而在新的文件系統下完成一個新的目錄或者文件的創建。

13 makefile gcc 鏈接庫問題
-L: “鏈接”的時候,去找的目錄,也就是所有的 -lFOO 選項裏的庫,都會先從 -L 指定的目錄去找,然後是默認的地方。編譯時的-L選項並不影響環境變量LDLIBRARYPATH,-L只是指定了程序編譯連接時庫的路徑,並不影響程序執行時庫的路徑,系統還是會到默認路徑下查找該程序所需要的庫,如果找不到,還是會報錯,類似cannot open shared object file。

-rpath-link:這個也是用於“鏈接”的時候的,例如你顯示指定的需要la,但是 liba.so 本身是需要libb.so 的,後者你並沒有顯示指定lb,而編譯器知道是 liba.so 引用到它,這個時候,對libb.so會間接的先從 -rpath-link 給的路徑裏查找。

-rpath: “運行”的時候,去找的目錄。運行的時候,要找 .so 文件,會從這個選項裏指定的地方去找。對於交叉編譯,交叉編譯鏈接器需已經配置 --with-sysroot 選項才能起作用。也就是說,-rpath指定的路徑會被記錄在生成的可執行程序中,用於運行時查找需要加載的動態庫。-rpath-link 則只用於鏈接時查找

`-lARCHIVE'
`--library=ARCHIVE'
增加一個檔案文件ARCHIVE到連接的文件列表中.這個選項可以被多次使用. 'ld'會爲每一個指定的
ARCHIVE搜索它的路徑列表,尋找`libARCHIVE.a'

對於支持共享庫的系統, 'ld'可能還會搜索擴展名不是'.a'庫.特別的,在ELF和SunOS系統上,'ld'會
在搜索帶有'.a'擴展名的庫前搜索帶'.so'擴展名的庫.

-llibrary
-l library
           Search the library named library when linking.  (The second alternative with the library as a separate argument is only for POSIX compliance and is not recommended.)
           It makes a difference where in the command you write this option; the linker searches and processes libraries and object files in the order they are specified.
           Thus, foo.o -lz bar.o searches library z after file foo.o but before bar.o.  If bar.o refers to functions in z, those functions may not be loaded.
           The linker searches a standard list of directories for the library, which is actually a file named liblibrary.a.  The linker then uses this file as if it had been specified precisely by name.
         The directories searched include several standard system directories plus any that you specify with -L.
    Normally the files found this way are library files---archive files whose members are object files.  The linker handles an archive file by scanning through it for members which define symbols that have so far been referenced but not defined.  But if the file that is found is an ordinary object file, it is linked in the usual fashion.  The only difference between using an -l option and specifying a file name is that -l surrounds library with lib and .a and searches several directories.

14 sysfs屬性文件中的__ATTR宏定義
 #define __ATTR(_name,_mode,_show,_store) { /
     .attr = {.name = __stringify(_name), .mode = _mode }, /
     .show = _show,     /
     .store = _store,     /
    }

15 linux虛擬終端中執行命令行的響應過程
首先是執行/bin/sh可執行文件來作爲終端命令行接收與處理的入口。以及創建新的進程來執行命令行傳入的可執行文件
/bin/sh作爲一個終端入口,本質也是一個運行着的程序,一般由內核啓動的第一個init進程來fork出子進程後啓動:
 fork   execve          execve         fork            execve 
init --> init --> /sbin/getty --> /bin/login --> /bin/login --> /bin/sh


16 write和read等linux中的系統調用都屬於glibc庫,內部採用SIG軟中斷方式來進入內核態後實現sys_write和sys_read等的系統調用。對於帶緩存的fwrite和fread函數,也是由glibc來實現的,源碼以_IO_new_fopen->_IO_file_open爲主,本質是構建一個FILE來表示系統調用open返回的fd。即fopen/fread本質只是對於系統調用的一層封裝而已。
而對於Android系統而言,一般使用自帶的Bionic libc庫來實現系統調用,生成libc.so/libc_bionic.a等等(前者屬於lib目錄要打包到system中去的,後者主要用於中間編譯使用,直接被打包進可執行文件)
(部分可能還需要使用的libc++.so的庫源碼位於android/external/libcxx目錄下)
系統調用在傳遞參數使,是將參數通過一組ebx/ecx/edx等參數作爲函數形參壓棧後再調用具體的系統調用sys_open函數,每個不同的系統調用處理函數,會有不同的形參個數,所以每次調用前這個形參入棧過程都是需要有SAVE_ALL這個宏來完成的。

17 Makefile多目標依賴關係與目標多依賴
obj0 obj1 : objxxx
在make處理時,如果obj依賴如obj1則執行objxxx的依賴關係,即每個目標依賴的是同一個事務,會被解析爲
obj0 : objxxx
obj1 : objxxx
其中命令行中出現的$@是依次從obj0 obj1這個目標列表中取值

obj:obj0
obj:obj1
是等價於obj:obj0 obj1

18 靜態庫lib.a的本質
靜態庫只是一堆object對象的集合,使用ar命令可以將編譯產生的.o文件打包成.a靜態庫。
生成靜態庫只有編譯,而沒有鏈接故可以不指定他所依賴的一些動態庫;而動態庫在生成的時候時既有編譯的動作也有鏈接的動作。靜態庫在被別的程序(可執行程序或是動態庫)鏈接的時候,鏈接器會將程序中使用到函數的代碼從靜態庫文件中拷貝到應用程序中的。
靜態庫生成時是沒有鏈接的,所以生成它的時候不需要指定它所依賴的外部庫;動態庫生成時是有鏈接動作的,那當然就要指定它依賴哪些外部庫了.
鏈接只在生成可執行文件和動態庫時會做ld相關的操作


19 make處理makefile的基本流序,類似gcc編譯源文件一樣
   當執行make命令時,會讀取對應的makefile文件,然後按照順序進行預處理:包括include其他的makefile文件,各種變量的賦值處理,各種隱式編譯規則的生成處理,直到最後全部預處理完成纔會真正開始執行目標文件的編譯輸出。其中目標所依賴文件的編譯規則輸出可以定義在makefile的各種位置,不需要一定定義在前面目標執行規則的前面,不具有前後關聯性。

20 在Makefile文件中define自定義的函數不僅可以處理變量參數,還可以定義相關的目標依賴編譯規則:
2198 define copy-one-file
2199 $(2): $(1) | $(ACP)
2200     @echo "Copy: $$@"
2201     $$(copy-file-to-target)
2202 endef

21 linux shell輸出重定向,將命令輸出到新的文件或者設備節點
格式:
command-line1 [1-n] > file或文件操作符或設備文件

上面命令意思是:將一條命令執行結果(標準輸出,或者錯誤輸出,本來都要打印到屏幕上面的) 重定向其它輸出設備(文件,打開文件操作符,或打印機等等)1,2分別是標準輸出,0表示錯誤輸出。
其中對文件而言>>添加在文件尾部,>會清空文件後將命令輸出寫入到新的文件之中。

22 makefile中的變量申明/賦值以及取值引用
變量申明:
A := foo
變量引用$是取變量所代表的值:
B := $(A)
$(B)則爲foo

變量的嵌套
假設foo := foo.o
$($(A))=$(foo)=foo.o

foo := $(A).o則foo變量賦值爲foo.o

23 Makefile中的foreach函數
foreach 函數和別的函數非常的不一樣。因爲這個函數是用來做循環用的,Makefile中的foreach函數幾乎是仿照於Unix標準Shell (/bin/sh)中的for語句,或是C-Shell(/bin/csh)中的foreach語句而構建的。它的語法是:

  $(foreach <var>,<list>,<text>)

這個函數的意思是,把參數<list>;中的單詞逐一取出放到參數<var>;所指定的變量中,然後再執行< text>;所包含的表達式。每一次<text>;會返回一個字符串,循環過程中,<text>;的所返回的每個字符串會 以空格分隔,最後當整個循環結束時,<text>;所返回的每個字符串所組成的整個字符串(以空格分隔)將會是foreach函數的返回值。

所以,<var>;最好是一個變量名,<list>;可以是一個表達式,而<text>;中一般會使用<var>;這個參數來依次枚舉<list>;中的單詞。舉個例子:
names := a b c d
files := $(foreach n,$(names),$(n).o)

上面的例子中,$(name)中的單詞會被挨個取出,並存到變量“n”中,“$(n).o”每次根據“$(n)”計算出一個值,這些值以空格分隔,最後作爲foreach函數的返回,所以$(files)的值是“a.o b.o c.o d.o”。
注意,foreach中的<var>;參數是一個臨時的局部變量,foreach函數執行完後,參數<var>;的變量將不在作用,其作用域只在

24 makefile中的sort函數遇到重複字符是隻保留一個
$(sort, a b a c )輸出爲a b c 

25 Makefile中的subst和patsubst區別
兩者都有字符替換的能力,後者在匹配字符時當沒有出現通配符合%時,只有當TEXT完全匹配pattern纔會被replace中替換。而subst具備替換TEXT中部分字符的能力,patsubst在非通配%模式下不具備部分替換。

25 Makefile中的ifdef/endif宏
ifdef用來檢測當前變量是否爲空值還是已經被賦值,而不是C語言的ifdef是否存在該宏,當檢測的變量爲空值時,直接else處理

26 使用export導入環境變量時等號兩邊是不能有空格的
即export PATH=xxxx:xxxxx
否則會報錯:export `=' not a valid identifier的一般原因

27 Makefile中添加調試信息打印時一是通過自帶的info,error,warning函數來實現,二是定義target通過echo來實現

28 Makefile中的CURDIR變量值, 表示當前make執行makefile時所處的工作目錄,可通過make -C切換,默認不指定時工作目錄爲當前執行make操作時所在的目錄
當執行make -C xxx -f $(CURDIR)/Makeflie操作時,會讀入xxx所在路徑下的Makefile文件,文件名可以通過-f制定,無指定時默認讀入Makefile文件。CURDIR在讀入-C xxx時自動由make工具生成,即-C指定的目錄絕對路徑xxx所在,供當前Makefile中作爲變量來使用。

29當執行可以執行bin文件出現bin:
整個環境變量下的確無該bin可執行文件存在報command not found

bash bin: No such file or directory
一方面還可能是該文件在執行時找不到程序runtime時所需要的so動態庫,報No such file or directory的錯誤,另外就是無法定位到可執行文件elf中定義的ld-linux.so程序加載器so庫所在絕對路徑,即內核無法加載ld-linux.so來爲程序運行做準備。
在gcc中使用ld鏈接選項時,需要在選項前面加上前綴-Wl,-rpath, xxxx1 -Wl,-rpath, xxxx2

30  Linux下elf文件的動態鏈接器是ld-linux.so,區別於gcc所使用的靜態鏈接器ld對於可執行文件查找路徑先PATH下後當前目錄下
每個ELF格式的可執行文件都可以配備ld-linux.so,該文件在程序運行時被加載,但必須要通過可執行文件告知其絕對路徑所在,該文件是不會依賴其他庫文件。
從上一小節中發現有一個專門的節區.interp存放有動態鏈接器,但是這個節區爲什麼叫做.interp(interpeter)呢?因爲當shell解釋器或者其他父進程通過exec啓動我們的程序時,系統會先爲ld-linux創建內存映像,然後把控制權交給ld-linux,之後ld-linux負責爲可執行程序提供運行環境,負責解釋程序的運行,因此ld-linux也叫做dynamic loader(或intepreter)(關於程序的加載過程請參考資料

31 Makefile中所有以$打頭的單詞都會被解釋成Makefile中的變量。如果你需要調用shell中的變量(或者正則表達式中錨定句位$),都需要加兩個$符號($$)來表示shell的變量
PATH="/data/"
all:
  echo ${PATH}
  echo $$PATH
例子中的第一個${PATH}引用的是Makefile中的變量,而不是shell中的PATH環境變量,後者引用的事Shell中的PATH環境變量

32 linux shell腳本以及各種可執行文件運行環境是繼承至運行它的進程環境。
對於Linux而言,應用程序的默認工作目錄就不同了,它是默認是繼承啓動它的進程的工作目錄的,也就是說,如果進程是在其它目錄下啓動,那應用程序的工作目錄默認就在其它的目錄下。這樣的話使用相對路徑訪問文件就有很大的不確定性:我們永遠也不能保證,用戶一定是從應用程序所在的目錄啓動!

33 Makefile VPATH變量的作用:
make在查找依賴文件時先是在當前工作目錄下查找,然後再去VPATH定義的絕對路徑下去繼續查找依賴文件,最終找到依賴文件後根據是否有VPATH參與將依賴文件的絕對路徑賦值給$<變量,一般$<值爲相對當前工作目錄的路徑所在。


34 gcc -E/-S/-c
-E: 預處理輸出.i文件
-S: 編譯,輸出爲.s文件
-c: 彙編,包括預處理和編譯,將.s彙編後變爲2進制機器碼,默認輸出爲.o目標文件,無鏈接
默認不指定時都會經歷以上過程並加入鏈接操作
gcc a.c -o a.bin
gcc a.c//輸出a.out
以上兩種方式都會一次預處理/編譯/彙編/鏈接等4個步驟的操作,直到輸出可執行文件爲止。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章