今天review linux character driver的時候,發現往往定義的file_operations的.open成員,參數擁有file以及inode參數,可是這個參數哪邊來的?
其實會對open()有好奇,主要也是之前一直在trace linux kernel source內一些socket的東西,socket本身由sockfs支援,不使用open()而用socket()來開啟inode,所以想說兩者的區別是?
kernel內用 inode 結構體用來表示data。因此,它和 file structure
用來表示一個打開了的fd並不相同。對於一個inde,可能會有多個 file structure
對應著多個已打開的多個fd,但是這都只能指向同一個 inode 結構。
回頭看open(),大多數的system call使用interrupt 0x80,所以很快地追蹤到sys_open()是理所當然的入口
簡化整個call stack為sys_open()=>filp_open()=>dentry_open()=>dentry_open()
在呼叫register_chrdev()的時候將有覆寫的file_operations賦予device
底下是一些重要的工作,完整的source code就不列了
sys_open() : 配置fd
filp_open() : 依賴路徑取得nameidata結構,跟著呼叫dentry_open()取回file instance
dentry_open() : 配置file instance,nameidata結構內包含了inode instance,此時將一些必要資料由inode拷貝到file instance,f->f_op = fops_get(inode->i_fop);,此時f->f_op->open(inode,f)就是當時register_chrdev()所註冊的file_operations。
參考資料:
http://hi.baidu.com/potyzhang/item/ae9993a919e86f17a8cfb793
http://hi.baidu.com/heiyebujianwo/item/fa7fe543d99b73ab61d7b9cb
2013年4月17日 星期三
2013年2月11日 星期一
linux socket下的TCP three way handshake淺析
之前好奇three way handshake被包裝在socket api之後,那麼到底是哪個function call完成了這個工作?事實上...我想簡單了,這個工作根本是被kernel完成!?也就是是在function call之外,這樣講有些模糊,首先假設大家知道richard stevens提出來的TCP state diagram,那麼從LISTEN狀態轉換到ESTABLISHED狀態就是經過three way handshake
當呼叫listen()的時候,linux將狀態設定為LISTEN,這是相當直觀,但是accept()在呼叫之後會產生怎樣的事情,我倒是不知道!?根據網路上的說法accept()會因為狀態不是ESTABLISHED而被block,然linux kernel會將LISTEN狀態改為ESTABLISHED狀態整個過程是由:
tcp_v4_rcv()=> tcp_v4_do_rcv() => tcp_rcv_state_process()
在tcp_rcv_state_process()中由LISTEN轉換藉由送出SYN,ACK轉為SYN_RCVD,然後等待client端,送回SYN就可以轉換為ESTABLISHED狀態。當轉換為SYN_RCVD狀態時,建立了request_sock結構,在接收到回傳的SYN建立/轉換成了INET SOCKET
這裡的疑惑就是,誰呼叫了tcp_v4_rcv() !?是accept()?還是kernel?如果是kernel表示,其實accept()呼叫之前,就可以進行three way handshake
根據accept()的呼叫次序
accept()==>sys_accept()==>sys_accept4()==>inet_accept()==>inet_csk_accept()
最後一個function直接處理了inet_connection_sock結構,我比較傾向kernel處理了three way handshake
最後覺得,果然Linux網路實作還真是複雜阿!!
參考資料:
http://blog.csdn.net/yanook/article/details/7019558
http://blog.csdn.net/chensichensi/article/details/5272696
http://www.kernel.org/doc/man-pages/online/pages/man2/accept.2.html
http://basiccoder.com/linux-kernel-network-socket-creation.html
http://blog.csdn.net/yanook/article/details/7019600
http://hi.baidu.com/linux_kernel/item/7a5ae7027ede2edcdde5b094
http://tinyurl.com/aejh37lhttp://linux.chinaunix.net/techdoc/net/2008/12/30/1055672.shtml
http://blog.sina.com.cn/s/blog_52355d840100b6sd.html
最後一個function直接處理了inet_connection_sock結構,我比較傾向kernel處理了three way handshake
最後覺得,果然Linux網路實作還真是複雜阿!!
參考資料:
http://blog.csdn.net/yanook/article/details/7019558
http://blog.csdn.net/chensichensi/article/details/5272696
http://www.kernel.org/doc/man-pages/online/pages/man2/accept.2.html
http://basiccoder.com/linux-kernel-network-socket-creation.html
http://blog.csdn.net/yanook/article/details/7019600
http://hi.baidu.com/linux_kernel/item/7a5ae7027ede2edcdde5b094
http://tinyurl.com/aejh37lhttp://linux.chinaunix.net/techdoc/net/2008/12/30/1055672.shtml
http://blog.sina.com.cn/s/blog_52355d840100b6sd.html
2013年2月9日 星期六
listen()於linux的實作
相信這已經是很多人相當熟悉的function call,根據man page,有一個參數就是backlog的個數,乃至於一些kernel的引用
The behavior of the backlog argument on TCP sockets changed with Linux 2.2. Now it specifies the queue length for completely established sockets waiting to be accepted, instead of the number of incomplete connection requests. The maximum length of the queue for incomplete sockets can be set using /proc/sys/net/ipv4/tcp_max_syn_backlog. When syncookies are enabled there is no logical maximum length and this setting is ignored. See tcp(7) for more information.但是比較明確表示有可能是有其他解釋的是posfix的定義
The backlog argument provides a hint to the implementation which the implementation shall use to limit the number of outstanding connections in the socket's listen queue.可以試驗使用底下的程式建立server
...sockfd = socket(AF_INET, SOCK_STREAM, 0);...listen(sockfd, 3);
sleep(50);
...
隨便寫個client,會發現在sleep期間,還是可以連上該server 3個connections以上,這是發生了啥麼事情!?
事實上只是一個實作上的方式不同,就如同kernel上面人家引述的,linux使用了一個queue來儲存在些incomplete sockets,但是這個queue size卻不等於backlog的參數,因為reqsk_queue_alloc()實作(version 2.6.36)是以2^n在成長,一般最小為16,系統一般的上限為256(int sysctl_max_syn_backlog = 256;)
此外,在richard stevens書中提到兩點,第一點,backlog從來沒有被嚴格的定義過,第二點,backlog不建議設定為0,因為backlog設定為0,將導致他的解釋依照系統實作而定,有些系統是設定為一系統最小backlog數(e.g. 16)
此外,在richard stevens書中提到兩點,第一點,backlog從來沒有被嚴格的定義過,第二點,backlog不建議設定為0,因為backlog設定為0,將導致他的解釋依照系統實作而定,有些系統是設定為一系統最小backlog數(e.g. 16)
所以說,man page有時候還是跟實作上有差異的,參考就好,果然網路程式真的是博大精深阿
2012年7月14日 星期六
udev的流程
udev主要功能
udev其實利用了kobject在/sys下面建立的動態的系統資訊,udev主要成分,namedev、libsys、udev
udev配合hotplug機制,每當有裝置加入的時候,建立sysfs(kobject)下的相對應節點,跟著呼叫namedev在/dev下建立相對應的裝置
如果理解bus、device與driver三者的關係,可以理解到udev功能是可以達成的,但是接著升起的疑問是,流程是如何呢?
bus會利用match這個操作方式尋找合適的device以及driver,但是因為需要kobject來在sysfs下建立相對應的結構,是否意味著要改寫driver?從參考資料的解說,應該是不必。udev會監控linux載入的module,自動生成kobject
所以一開始就註冊了bus,udev會在module載入的時候紀錄準備生成kobject,當裝置插入主機的時候,跟著建立device,使用bus尋找到相關driver,最後udev會生成kobject,也就是在/sys下建立相對應的目錄
bus(開機時候)=>driver/udev(載入module時候)=>device(裝置插入主機接上某個bus)=>/dev/xxx與/sys/bus/xxx建立(udev透過bus.match建立)
以上就是我的理解,不知道有沒有錯誤
參考資料:
http://daydreamer.idv.tw/rewrite.php/read-49.html
- 允許動態建立刪除裝置
- 自行配置裝置編號
- 根據規則建立名稱,而非固定名稱
- 建立kobject物件
udev其實利用了kobject在/sys下面建立的動態的系統資訊,udev主要成分,namedev、libsys、udev
udev配合hotplug機制,每當有裝置加入的時候,建立sysfs(kobject)下的相對應節點,跟著呼叫namedev在/dev下建立相對應的裝置
如果理解bus、device與driver三者的關係,可以理解到udev功能是可以達成的,但是接著升起的疑問是,流程是如何呢?
bus會利用match這個操作方式尋找合適的device以及driver,但是因為需要kobject來在sysfs下建立相對應的結構,是否意味著要改寫driver?從參考資料的解說,應該是不必。udev會監控linux載入的module,自動生成kobject
所以一開始就註冊了bus,udev會在module載入的時候紀錄準備生成kobject,當裝置插入主機的時候,跟著建立device,使用bus尋找到相關driver,最後udev會生成kobject,也就是在/sys下建立相對應的目錄
bus(開機時候)=>driver/udev(載入module時候)=>device(裝置插入主機接上某個bus)=>/dev/xxx與/sys/bus/xxx建立(udev透過bus.match建立)
以上就是我的理解,不知道有沒有錯誤
參考資料:
http://daydreamer.idv.tw/rewrite.php/read-49.html
2012年7月4日 星期三
character driver的岔路上
不能免俗,絕大多數入門的linux driver,除了第一個hello world module之外,跟的就是character driver,因為他最貼近一般人使用fopen, fread, fwrite, fclose的直覺
推薦服用宋寶華的範例,但是在2ed chapter 6使用的測試指令為
echo "hello world"> /dev/globalmem
cat /dev/globalmem
以上可以正常運作,可是跟著我就開始試著其他組合
echo "hello world"> /dev/globalmem
echo "again"> /dev/globalmem
cat /dev/globalmem
開始似乎不大正常的運作,即使使用
echo "hello world"> /dev/globalmem
echo "again">> /dev/globalmem
cat /dev/globalmem
也於事無補,我開始懷疑ppos這個表示目前檔案位置的參數有沒以正確運作,在經過一番追蹤,似乎發現不如預期。我恍然大悟,原來要從process、file、inode角度去切入,就明白這一切了。
過程中我也寫了個簡單的程式,使用fopen, fread, fwrite, fclose去操作,因為一點點小小的失誤反而讓我看到一些覺得奇怪的地方,首先fopen, fread, fwrite, fclose的應該是對應到了driver內的operations,但是我在fopen的時候,最後讀寫模式寫錯了"w+",只寫了"w",結果編譯過程還是通過了,但是fread沒有作用,照樣有輸出,真是好神奇阿@@a有興趣的人可以用strace試驗看看
推薦服用宋寶華的範例,但是在2ed chapter 6使用的測試指令為
echo "hello world"> /dev/globalmem
cat /dev/globalmem
以上可以正常運作,可是跟著我就開始試著其他組合
echo "hello world"> /dev/globalmem
echo "again"> /dev/globalmem
cat /dev/globalmem
開始似乎不大正常的運作,即使使用
echo "hello world"> /dev/globalmem
echo "again">> /dev/globalmem
cat /dev/globalmem
也於事無補,我開始懷疑ppos這個表示目前檔案位置的參數有沒以正確運作,在經過一番追蹤,似乎發現不如預期。我恍然大悟,原來要從process、file、inode角度去切入,就明白這一切了。
過程中我也寫了個簡單的程式,使用fopen, fread, fwrite, fclose去操作,因為一點點小小的失誤反而讓我看到一些覺得奇怪的地方,首先fopen, fread, fwrite, fclose的應該是對應到了driver內的operations,但是我在fopen的時候,最後讀寫模式寫錯了"w+",只寫了"w",結果編譯過程還是通過了,但是fread沒有作用,照樣有輸出,真是好神奇阿@@a有興趣的人可以用strace試驗看看
ioremap的理由
參考資料內說得非常好,有許多理由
因為一般在kernel模式下操作的是virtual address,透過MMU的page table來處理virtual address與physical address,但是實體io port或者memory,要是直接存取會產生兩個問題,一個是受限於特殊硬體架構,另外一個是不應直接處理physical address。於是乎ioremap就是透過修改page table,讓physical address對應到virtual address(等於在virtual address規劃出一區塊,使OS知道使用該區塊等同在使用某區塊的physical address),這樣一來就某種程度隔開特殊的硬體架構,也避免直接使用physical address。
跟著由於硬體的特性,雖然ioreamp讓程式設計師使用實體記憶體如同使用一般memory一樣,但是因為有時硬體可能只是個io port,訊號隨著時間一直變動(有點類似volatile變數一樣),所以在讀取跟寫入的時候最好透過特殊函數如writel或者readl之類,不要直接用assign operator(等號)
http://www.programmer-club.com/showSameTitleN/embedded/1820.html
http://blog.csdn.net/do2jiang/article/details/5450839
1. 一般io access, 有所謂的IO bus或使用memory mapped IO, 以I386而言, 它有in/out系列的instruction來做IO bus access, 而ARM則沒有.
2. 對於IO bus access, 一般driver只要有access right就可, 而由於一般而 言C/C++ 無法generate in/out系列的instruction, 所以要以inline assembly來達成這個目的, 而一般的inb(), inw(), outb(), outw()...等 等的functions或macros會可以做到這件事.
3. 對於memory mapped IO, 由於是直接access memory, 必須跟OS的memory system結合, 所以就會有一些access right, non-cacheable, virtual address等的問題.
4. 對PCI而言, 還有所謂的PCI bus address, 而一般PCI bus driver在啟動 後會enumerate上面的card, 了解它們的memory regions的size, 並assign PCI bus address給每一memory region, 至於如何由system bus address 對到PCI bus address要看SOC的設計. 在access時, 是virtual address-> physical address->PCI bus address.
5. OS並不會知道那一塊被拿來當memory mapped IO, 所以, 往往是只有開放 RAM的部份的space, 其實一般startup code其MMU的部份還會打開SOC的 register space或一些local bus banks的space, 以相同的virtual address 來使用對應的physical address, 但是, startup code一般我們希望不要 放太多變動性太大的東西, 這樣也會把太多枝枝葉葉的codes放入主要程式 中, 另外, 與PCI相關的考量放入startup code, 也會變得很繁瑣. 所以 ioremap很重要的是就是讓driver可以為它要使用的空間找到一塊virtual address的space.
6. 有一些扁平式的設計, 直接一開始全部對應好, 將4G virtual address對應 到等值的physical address, 所以你不用ioremap也可使用正常, 不過, 使 用ioremap也沒甚麼不好吧, 反而是養成良好的習慣. 而通常, 有virtual memory的MMU, 一般都是只開有限的區域, 即使它們只是直接對應等值的 address, 因為這樣可以加強系統的debug功能, report非法的memory access.
7. 一般ARM有分TLB或protection unit的, protection unit只做保護和cache/ write buffer的控制, TLB則可以實現virtual memory.
因為一般在kernel模式下操作的是virtual address,透過MMU的page table來處理virtual address與physical address,但是實體io port或者memory,要是直接存取會產生兩個問題,一個是受限於特殊硬體架構,另外一個是不應直接處理physical address。於是乎ioremap就是透過修改page table,讓physical address對應到virtual address(等於在virtual address規劃出一區塊,使OS知道使用該區塊等同在使用某區塊的physical address),這樣一來就某種程度隔開特殊的硬體架構,也避免直接使用physical address。
跟著由於硬體的特性,雖然ioreamp讓程式設計師使用實體記憶體如同使用一般memory一樣,但是因為有時硬體可能只是個io port,訊號隨著時間一直變動(有點類似volatile變數一樣),所以在讀取跟寫入的時候最好透過特殊函數如writel或者readl之類,不要直接用assign operator(等號)
http://www.programmer-club.com/showSameTitleN/embedded/1820.html
http://blog.csdn.net/do2jiang/article/details/5450839
2012年7月1日 星期日
inode、file以及container_of
回顧LDD3的第一個character device範例,其實感覺裡面一次給出了太多東西,會令人暈頭轉向,其實在宋寶華的書中給出一個比較簡單的範例
兩者之間的差異決定的對程式展現的方式,其中以container_of這個macro最為經典,建議先看完宋寶華的範例,再瀏覽LDD3的範例,可以知道這個macro的關鍵性作用
LDD3展示了,一個module可以同時為同一類的裝置提供驅動程式,讓device(裝置)與operations(操作),可以結合,同時可以分開,一個module提供多個裝置,相同的操作,其中隱含了物件或者說ADT(abstract data type)的概念,這個在driver開方上很重要,udev更是把這種概念發揚光大
inode跟file兩個重要的結構提供了不同的作用,inode大多存在於kernel space,file則是user/kernel space都有(使用者一般操作file),同時也是支撐VFS (virtual file system)的兩個重要結構
struct scull_dev {
struct scull_qset *data; /* Pointer to first quantum set */
int quantum; /* the current quantum size */
int qset; /* the current array size */
unsigned long size; /* amount of data stored here */
unsigned int access_key; /* used by sculluid and scullpriv */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
struct scull_dev *scull_devices; /* allocated in scull_init_module */
最後一個scull_devices表示一堆同類的devices
有了以上的瞭解之後來看open、read函數
static int open(struct inode *inode,struct file *filp){
...
}
static ssize_t read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){
...
}
在LDD3的範例在init函數配置好了所有的scull dev(因為不只一個device),但是在open函數的時候要如何找到對應的scull_devices中的哪一個?因為我們想把他assign給file.private_data方便以後給read函數使用
一般而言,由於inode包含了cdev這個變數,我們知道cdev來自於scull_dev裡面,所以會想藉此找出scull_dev,裡面包含了如何計算cdev應該在scull_dev內的位移,配合位移,藉由調整記憶體的address,我們就可以找到scull_dev了,而此時container_of就可以幫我們忙
struct scull_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev; /* for other methods */
constainer_of這個macro會自動完成計算位移以及轉型的動作,將目前打開的device所指向的scull_dev變數找出來。也就是只有scull_device.cdev結構成員,卻可以找到scull_dev的結構變數。做了那麼多工作,就是為了把scull_device配置給filp->private_data,後面read函數就可以應用她了
如果今天只有一個device,那麼其實也不用那麼麻煩,就如宋寶華的範例,直接將struct scull_dev onlyone設定為global變數,然後直接在open函數的時候assign給filp->private_data即可
由上面可以了解到,LDD3在一個範例內展示了
不細看還真的很難體驗這個module竟然有如此多的東西,也感嘆這章節竟然寫得如是簡單/簡潔,真的不知道LDD3的作者是故意的還是假設讀者的Linux背景很夠
如果有興趣了解container_of到底如何運作,可以參考參考資料連結
參考資料:
http://space.itpub.net/14805538/viewspace-445624
http://tw.myblog.yahoo.com/hughes-blog/article?mid=88&prev=-1&next=87
- LDD3一次配置多個device,對多個device視為同一類,使用同一個module
- 宋寶華一開始只有一個device,只給定一個module
兩者之間的差異決定的對程式展現的方式,其中以container_of這個macro最為經典,建議先看完宋寶華的範例,再瀏覽LDD3的範例,可以知道這個macro的關鍵性作用
LDD3展示了,一個module可以同時為同一類的裝置提供驅動程式,讓device(裝置)與operations(操作),可以結合,同時可以分開,一個module提供多個裝置,相同的操作,其中隱含了物件或者說ADT(abstract data type)的概念,這個在driver開方上很重要,udev更是把這種概念發揚光大
inode跟file兩個重要的結構提供了不同的作用,inode大多存在於kernel space,file則是user/kernel space都有(使用者一般操作file),同時也是支撐VFS (virtual file system)的兩個重要結構
- file結構有兩個重要成員,一個是file_operations,也就是操作file的functions,另外一個是private_data指標,通常module會將對應的device資料的記憶體位置交給private_data
- inode則擁有i_cdev或者i_bdev,分別表示character device或者block device這兩個指標,雖然一般開發者會重新包裝自己的結構,將struct cdev包裝起來,以支援定義的驅動程式,如LDD3範例
struct scull_dev {
struct scull_qset *data; /* Pointer to first quantum set */
int quantum; /* the current quantum size */
int qset; /* the current array size */
unsigned long size; /* amount of data stored here */
unsigned int access_key; /* used by sculluid and scullpriv */
struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
struct scull_dev *scull_devices; /* allocated in scull_init_module */
最後一個scull_devices表示一堆同類的devices
有了以上的瞭解之後來看open、read函數
static int open(struct inode *inode,struct file *filp){
...
}
static ssize_t read(struct file *filp, char __user *buf, size_t size, loff_t *ppos){
...
}
在LDD3的範例在init函數配置好了所有的scull dev(因為不只一個device),但是在open函數的時候要如何找到對應的scull_devices中的哪一個?因為我們想把他assign給file.private_data方便以後給read函數使用
一般而言,由於inode包含了cdev這個變數,我們知道cdev來自於scull_dev裡面,所以會想藉此找出scull_dev,裡面包含了如何計算cdev應該在scull_dev內的位移,配合位移,藉由調整記憶體的address,我們就可以找到scull_dev了,而此時container_of就可以幫我們忙
struct scull_dev *dev; /* device information */
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev; /* for other methods */
constainer_of這個macro會自動完成計算位移以及轉型的動作,將目前打開的device所指向的scull_dev變數找出來。也就是只有scull_device.cdev結構成員,卻可以找到scull_dev的結構變數。做了那麼多工作,就是為了把scull_device配置給filp->private_data,後面read函數就可以應用她了
如果今天只有一個device,那麼其實也不用那麼麻煩,就如宋寶華的範例,直接將struct scull_dev onlyone設定為global變數,然後直接在open函數的時候assign給filp->private_data即可
由上面可以了解到,LDD3在一個範例內展示了
- VFS這樣的抽象操作需要file以及inode配合
- scull這個module可以同時支援多個一樣的裝置
- 如何註冊character device,以及要注意的事項
- 如何實作file operations
- 隱含在Linux Device Driver中分層以及分工的架構
- ...
不細看還真的很難體驗這個module竟然有如此多的東西,也感嘆這章節竟然寫得如是簡單/簡潔,真的不知道LDD3的作者是故意的還是假設讀者的Linux背景很夠
如果有興趣了解container_of到底如何運作,可以參考參考資料連結
參考資料:
http://space.itpub.net/14805538/viewspace-445624
http://tw.myblog.yahoo.com/hughes-blog/article?mid=88&prev=-1&next=87
2012年6月30日 星期六
Hello World Module
不能免俗的一個hello world等級的module,基本上就是學習編譯技巧跟觀察,一點作用都沒有
檔案為hello.c位於某個版本的kernel source下的drivers/hello目錄內,比如說/home/account/friendarm/kernel/linux-2.6.32.2/hello,底下有兩個檔案hello.c跟Makefile
Makefile只有兩行,一行指定compiler,另外一行就是module的相依行囉
編譯指令為
make -C /home/account/friendarm/kernel/linux-2.6.32.2 M='drivers/hello' modules
-C後面的參數為kernel source tree位置,後面則是要編譯的module的位置
我跟著是放到micro2440上面執行(如果在桌上型,Makefile內的CC可以省略,直接用系統內建就可以了),因為micro2440上面預設把所有訊息打開了,所以可以在console底下看到訊息輸出,不然一般應該在/var/log/messages檔案內
檔案為hello.c位於某個版本的kernel source下的drivers/hello目錄內,比如說/home/account/friendarm/kernel/linux-2.6.32.2/hello,底下有兩個檔案hello.c跟Makefile
Makefile只有兩行,一行指定compiler,另外一行就是module的相依行囉
編譯指令為
make -C /home/account/friendarm/kernel/linux-2.6.32.2 M='drivers/hello' modules
-C後面的參數為kernel source tree位置,後面則是要編譯的module的位置
我跟著是放到micro2440上面執行(如果在桌上型,Makefile內的CC可以省略,直接用系統內建就可以了),因為micro2440上面預設把所有訊息打開了,所以可以在console底下看到訊息輸出,不然一般應該在/var/log/messages檔案內
2012年6月28日 星期四
linux input subsystem
linux input的subsystem已經發展許久,為了簡化driver,系統發展出了input core的部分,不過也是因為這樣,使用者在使用方便之餘,已經搞不清楚到底事件的觸發是如何從source to destination了,還好linux是開放原始碼,所以trace source code還是可以瞧出端倪的
很多書本不知道是為了哪種理由,其實這部分大多很含糊的略過,只簡單提到使用input_register_device()去註冊由input_allocate_device()配置出來的device,然後跟著使用input_event()跟input_sync()完成工作,最多再給出個虛擬的滑鼠的範例,結束
看完之後我還是搞不懂,事件如何導向消化的地方,我可否自行寫一個輸入系統,再自行寫一個handler來處理呢?直覺上是可行的,但是如果不了解如何運作,恐怕也是很難做到
透過drivers/input/input.c的source code解析可以得到答案,不過網路上已經有人剖析得很清楚,我就直接借用連結過來吧
http://blog.163.com/wxiongn@126/blog/static/1178820382010724104617786/
http://blog.163.com/wxiongn@126/blog/static/117882038201072410502312/
很多書本不知道是為了哪種理由,其實這部分大多很含糊的略過,只簡單提到使用input_register_device()去註冊由input_allocate_device()配置出來的device,然後跟著使用input_event()跟input_sync()完成工作,最多再給出個虛擬的滑鼠的範例,結束
看完之後我還是搞不懂,事件如何導向消化的地方,我可否自行寫一個輸入系統,再自行寫一個handler來處理呢?直覺上是可行的,但是如果不了解如何運作,恐怕也是很難做到
透過drivers/input/input.c的source code解析可以得到答案,不過網路上已經有人剖析得很清楚,我就直接借用連結過來吧
http://blog.163.com/wxiongn@126/blog/static/1178820382010724104617786/
http://blog.163.com/wxiongn@126/blog/static/117882038201072410502312/
2012年6月14日 星期四
Linux之AIO
雖然這已經是很久之前就存在的技術,但是感覺在一般的程式設計書籍並不常見(應該說比例上),但是最近AIO卻是延伸到web server上變得火紅,從node.js可見一斑
主要是因為閱讀蒐集linux driver的資料的時候看到的,因為感覺LDD這本書有點看不懂,可能礙於實際開發經驗,以及程式設計背景知識,即使知道了如何寫出driver,但是感覺並無法實際充分利用。所以我也另外買了一本大陸作者的書籍"Linux 裝置驅動程式之開發詳解(第2版)",前面講解還算通暢,可是到了AIO左右已經開始有點不知所云,轉而向網路搜尋,找到了M. Tim Jones的著作,他並非亞洲人
http://www.ibm.com/developerworks/cn/linux/l-async/
發現原來該書的作者應該是瞟竊他人的著作!!??雖然後來唸到後面有看到一個URL指向該文章,也不知道這個網路文章(已經翻譯成中文)是否為書本作者翻譯的,但是完全不加以註明,讓人覺得書本文章是他自己寫的,書中一段文字幾乎跟網頁文字相同!!再加上範例也很像。
我發現難以理解主要是因為書本作者根本把一些重要的網頁圖片拿掉,如下面的圖片,明白點出blocking/nonblocking跟asynchronous/synchronous之間衍生的關係
建議對書中看得不大懂的讀者,轉向直接閱讀該網頁的內容,其中還有四個圖案講解kernel跟這四種IO之間的關係,簡明易懂,個人看了感覺大有收穫
主要是因為閱讀蒐集linux driver的資料的時候看到的,因為感覺LDD這本書有點看不懂,可能礙於實際開發經驗,以及程式設計背景知識,即使知道了如何寫出driver,但是感覺並無法實際充分利用。所以我也另外買了一本大陸作者的書籍"Linux 裝置驅動程式之開發詳解(第2版)",前面講解還算通暢,可是到了AIO左右已經開始有點不知所云,轉而向網路搜尋,找到了M. Tim Jones的著作,他並非亞洲人
http://www.ibm.com/developerworks/cn/linux/l-async/
發現原來該書的作者應該是瞟竊他人的著作!!??雖然後來唸到後面有看到一個URL指向該文章,也不知道這個網路文章(已經翻譯成中文)是否為書本作者翻譯的,但是完全不加以註明,讓人覺得書本文章是他自己寫的,書中一段文字幾乎跟網頁文字相同!!再加上範例也很像。
我發現難以理解主要是因為書本作者根本把一些重要的網頁圖片拿掉,如下面的圖片,明白點出blocking/nonblocking跟asynchronous/synchronous之間衍生的關係
建議對書中看得不大懂的讀者,轉向直接閱讀該網頁的內容,其中還有四個圖案講解kernel跟這四種IO之間的關係,簡明易懂,個人看了感覺大有收穫
2012年5月16日 星期三
Linux驅動程式--chapter 13
這一章節主要介紹USB,這也是linux中相當複雜的一部份,書中感覺也是概略性介紹,我在網路上爬了一些資源,先記錄下來,再回頭來寫摘要
相關資源:
http://www.ibm.com/developerworks/cn/linux/l-usb/index1.html
http://www.ibm.com/developerworks/cn/linux/l-usb/index2.html
http://blog.csdn.net/fudan_abc/article/details/6820580
http://kezeodsnx.pixnet.net/blog/post/27794098-%5B%E8%BD%89%E8%B2%BC%5D-linux-%E4%BD%BF%E7%94%A8-usb-%E8%A3%9D%E7%BD%AE%E7%AD%86%E8%A8%98-
http://linux.chinaitlab.com/driver/877123.html
http://lwn.net/Articles/143397/
相關資源:
http://www.ibm.com/developerworks/cn/linux/l-usb/index1.html
http://www.ibm.com/developerworks/cn/linux/l-usb/index2.html
http://blog.csdn.net/fudan_abc/article/details/6820580
http://kezeodsnx.pixnet.net/blog/post/27794098-%5B%E8%BD%89%E8%B2%BC%5D-linux-%E4%BD%BF%E7%94%A8-usb-%E8%A3%9D%E7%BD%AE%E7%AD%86%E8%A8%98-
http://linux.chinaitlab.com/driver/877123.html
http://lwn.net/Articles/143397/
Linux驅動程式
已經好久沒更新了:P,因為最近都忙著補充以前"流失"的硬體相關資訊,書本囫圇吞棗的已經看到chapter 13去了,但是都沒整理。發現這linux driver真的是個大課題,然而這本書很可惜無法面面俱到(如果要寫的詳細,我想上千頁應該是基本的吧),書本優點是已經將大多需要用到的原則以及重要的原件都提到,可是底下是不足的地方
我想把這本書看完之後,應該會在去找上一兩本linux driver的書籍來看吧,另外開始研究linux核心到底做了甚麼事,當然同時不可避免地必須了解硬體的一些相關資訊
- 後面章節的範例愈來愈缺乏實作性質。也就是大多屬於simple code的範疇,並無法很順利地跟實際開發相連
- 與硬體連接的部分沒有更多的敘述。我想這點應該是取捨的問題,不大算是不足,不過如果需要硬體底層的資訊,可能還要在網路或者其他書籍中尋找
- 與kernel相關的部分著墨不過多。driver往往與kernel相關,書中並沒有比較詳細的闡明兩者之間的關係,或許是作者想要在抽象層面做介紹,如果與kernel連結,又不綁定特定平台,恐怕書本真的太厚
- 後面章節流於形式介紹。可能作者對於驅動程式開發太熟悉,內容看起來都很類似steps 1, 2, ...,並每有很生動的場景(scenario),對於理解幫助有限
我想把這本書看完之後,應該會在去找上一兩本linux driver的書籍來看吧,另外開始研究linux核心到底做了甚麼事,當然同時不可避免地必須了解硬體的一些相關資訊
2012年4月21日 星期六
一些kernel的process/thread
ps -ef可以看到一堆kernel啟動的程式是使用[中括弧]括弧起來的,底下是我知到的有
ksoftirqd/0:軟體中斷的daemon,後面的號碼表示thread個數,比方說2cores 4threads
kworker:APIC的wait queue
kthreadd:kernel thread的parent daemon
kswapd0:swap daemon
migration:將process由某個core移動到另外一個core
其中有些process在ubuntu上面找不到
pdflush:將一些dirty pages作輸出
events/n:working queue
其實在過程中有看到kernel thread,我很好奇kernel thread在概念上跟thread有何差異?對於kernel thread跟process的對應關係又是如何也感到很好奇
參考資料
http://hi.baidu.com/nixsql/blog/item/c8f042ef2018863127979116.html
http://ubuntuforums.org/showthread.php?t=1630347
ksoftirqd/0:軟體中斷的daemon,後面的號碼表示thread個數,比方說2cores 4threads
kworker:APIC的wait queue
kthreadd:kernel thread的parent daemon
kswapd0:swap daemon
migration:將process由某個core移動到另外一個core
其中有些process在ubuntu上面找不到
pdflush:將一些dirty pages作輸出
events/n:working queue
其實在過程中有看到kernel thread,我很好奇kernel thread在概念上跟thread有何差異?對於kernel thread跟process的對應關係又是如何也感到很好奇
參考資料
http://hi.baidu.com/nixsql/blog/item/c8f042ef2018863127979116.html
http://ubuntuforums.org/showthread.php?t=1630347
2012年3月28日 星期三
Linux驅動程式--chapter 5
這章的重點是concurrency的問題,介紹的工具是semaphore、mutexes、spinlock、atomic operations、seqlock、Read-Copy-Update(RCU)
基本上快都是作業系統(OS)介紹過的東西,如果上過這個課程,對這些東西應該不陌生。雖然很想這樣結束,但是我要說,書中介紹的原則往往比教科書來的多,也來的更貼近實際狀況;另外也讓我體驗到driver有時候設計真的充滿了trade off
一般而言,我們希望使用semaphore因為會比spinlock容易(因為高階),但是spinlock會比較有效率,但是spinlock又可能引起沒效率,這種弔詭的說法有舉例
一般而言,spinlock使用的時候會取消cpu preemption的功能,希望目前的task趕緊完成工作,儘快釋放lock,很不幸很多function call會引發目前的工作釋放cpu,比方說kmalloc,而且沒有完整的列表。其次中斷也會引發目前task帶著lock且釋放出cpu,這時候可能會有很不幸的事情發生,那就是如果該中斷要求同一個spinlock,這就好玩了,他會因為拿不到,重新觸發,然後就一直浪費cpu的時間在等待task釋放cpu,但是可能又因為cpu preemption功能被關閉,本來的task進不到cpu產生了dead lock,如果不會dead lock也有可能cpu空轉上好一段時間。這也是為何說spin lock可能引起效率低落的原因。
作者給spin lock幾個建議:
對於種種同步的建議是:
atomic operation則適用於int以及bit運算,因為有時候我們希望有些簡單但是重要的count功能或者加減的功能是使用atomic operations,但是有點要認知到,就是運算的串聯次序本身並沒有被保證,也就是兩個以上task執行了各十個atomic operations,你並不能知道他們之間的次序是如何排列的。
至於詳細的API使用方式,可以參考書本
基本上快都是作業系統(OS)介紹過的東西,如果上過這個課程,對這些東西應該不陌生。雖然很想這樣結束,但是我要說,書中介紹的原則往往比教科書來的多,也來的更貼近實際狀況;另外也讓我體驗到driver有時候設計真的充滿了trade off
一般而言,我們希望使用semaphore因為會比spinlock容易(因為高階),但是spinlock會比較有效率,但是spinlock又可能引起沒效率,這種弔詭的說法有舉例
一般而言,spinlock使用的時候會取消cpu preemption的功能,希望目前的task趕緊完成工作,儘快釋放lock,很不幸很多function call會引發目前的工作釋放cpu,比方說kmalloc,而且沒有完整的列表。其次中斷也會引發目前task帶著lock且釋放出cpu,這時候可能會有很不幸的事情發生,那就是如果該中斷要求同一個spinlock,這就好玩了,他會因為拿不到,重新觸發,然後就一直浪費cpu的時間在等待task釋放cpu,但是可能又因為cpu preemption功能被關閉,本來的task進不到cpu產生了dead lock,如果不會dead lock也有可能cpu空轉上好一段時間。這也是為何說spin lock可能引起效率低落的原因。
作者給spin lock幾個建議:
- 使用spin lock不要有cpu preemption,這點kernel會處理,不至於太擔心
- 使用spin lock不要讓task sleep,要非常小心,因為如上所說function call & interrupt
- 使用spin lock要儘快釋放,免得schedule的功能"失去作用",因為spin lock取走了大部分cpu時間
對於種種同步的建議是:
- 儘量事前規劃好,省得事後debug很困難
- 對於鎖定的資源依序取得,可以避免一些dead lock的可能性
- 不要呼叫交互取得lock或者資源的function,這樣也很容易dead lock
atomic operation則適用於int以及bit運算,因為有時候我們希望有些簡單但是重要的count功能或者加減的功能是使用atomic operations,但是有點要認知到,就是運算的串聯次序本身並沒有被保證,也就是兩個以上task執行了各十個atomic operations,你並不能知道他們之間的次序是如何排列的。
至於詳細的API使用方式,可以參考書本
2012年3月27日 星期二
Linux驅動程式--chapter 4
這一章已經超過了單純driver,還包含到了kerenel debug的技巧,首先介紹一些在kernel hacking裡面的選項,為了debug有時候是必須打開的,接著介紹不同的技巧以及他們應用的時機
printk() : 最簡單最直接的一種,大致上分成八個level只有當設定的訊息的level小於console_loglevel的時候,訊息才會直接被送到console,作者還介紹了一個小工具程式,讓我們可以設定輸出的console。接著printk的問題就是如果在code裡面埋了一堆debugger information,要如何在編譯階段可以清理掉,作者定義了幾個macro來處理這件事情
/proc : 從2.6開始procfs被引入作為顯示系統狀態的vfs,如果老是使用printk在動作很多的時候,訊息量可以淹沒整個console畫面,反過來如果可以在適當時機將需要的資料拿出來看,這樣除錯就可以輕鬆許多,要在/proc底下註冊建立/proc檔案需要用到
struct proc_dir_entry *create_proc_read_entery(const char *name, mode_t mode, struct proc_dir_entry *base, read_proc_t *read_proc, void *data)
這一個function,可以註冊需要的路徑跟虛擬裝置,同時要提供可以讀取資料的函數(跟自己寫的module在同一檔案/空間之中),將目前module的狀態讓使用者可以用類似cat /proc/scullmem的方式取出。這種方式有個限制就是基本上是以page size,在運作,操作複雜結構的資訊並不是很容易,另外就是/proc已經開始有濫用的趨勢,/proc已經不只是讀取,還可以寫入,有點跟/dev一樣的問題。linux已經開始引入sysfs來處理這樣的問題,他要求更有架構性的資料
ioctl() : 最靈活的方式,但是程式設計師必須自行處理,在此章節作者也還不打算詳細介紹,他另外有個好處就是code獨立,不是類似printk會"汙染"到原本的code,所以忘記把它拿掉只是佔據記憶體空間:P
strace : 用來追蹤一些system call的次序,但是可讀性低於printk,有許多參數,如-o表示將輸出轉移到檔案內
strace -o /tmp/x1 ls -R > /dev/scull0
Ooops information : 當系統fault的時候(不是panic/死當),會產生類似coredump檔案的輸出,包含了CPU register等等information(P.S>我不大清楚它會產生到哪裡去@@a)
magic SysRq key : 一些特殊的組合鍵,讓使用者可以做一些事情,請參考書內內容
debugger tools : gdb、kdb、kgdb、Linux Trace Toolkit (LTT)、Dynamic Probes
裡面有提到一個有趣的User-Mode Linux,這是一個虛擬機器的機構,但是她不能讓使用者直接存取硬體
最後,其實作者的sample code的scull包含了chapter 3跟4,所以如果光看了chapter 3就去看code會感到怎麼還有很多多出來的東西
參考資料:
http://zh.wikipedia.org/wiki/Sysfs
http://blog.linux.org.tw/~asho/archives/001816.html
http://184.82.2.112/wordpress/?p=505
printk() : 最簡單最直接的一種,大致上分成八個level只有當設定的訊息的level小於console_loglevel的時候,訊息才會直接被送到console,作者還介紹了一個小工具程式,讓我們可以設定輸出的console。接著printk的問題就是如果在code裡面埋了一堆debugger information,要如何在編譯階段可以清理掉,作者定義了幾個macro來處理這件事情
/proc : 從2.6開始procfs被引入作為顯示系統狀態的vfs,如果老是使用printk在動作很多的時候,訊息量可以淹沒整個console畫面,反過來如果可以在適當時機將需要的資料拿出來看,這樣除錯就可以輕鬆許多,要在/proc底下註冊建立/proc檔案需要用到
struct proc_dir_entry *create_proc_read_entery(const char *name, mode_t mode, struct proc_dir_entry *base, read_proc_t *read_proc, void *data)
這一個function,可以註冊需要的路徑跟虛擬裝置,同時要提供可以讀取資料的函數(跟自己寫的module在同一檔案/空間之中),將目前module的狀態讓使用者可以用類似cat /proc/scullmem的方式取出。這種方式有個限制就是基本上是以page size,在運作,操作複雜結構的資訊並不是很容易,另外就是/proc已經開始有濫用的趨勢,/proc已經不只是讀取,還可以寫入,有點跟/dev一樣的問題。linux已經開始引入sysfs來處理這樣的問題,他要求更有架構性的資料
ioctl() : 最靈活的方式,但是程式設計師必須自行處理,在此章節作者也還不打算詳細介紹,他另外有個好處就是code獨立,不是類似printk會"汙染"到原本的code,所以忘記把它拿掉只是佔據記憶體空間:P
strace : 用來追蹤一些system call的次序,但是可讀性低於printk,有許多參數,如-o表示將輸出轉移到檔案內
strace -o /tmp/x1 ls -R > /dev/scull0
Ooops information : 當系統fault的時候(不是panic/死當),會產生類似coredump檔案的輸出,包含了CPU register等等information(P.S>我不大清楚它會產生到哪裡去@@a)
magic SysRq key : 一些特殊的組合鍵,讓使用者可以做一些事情,請參考書內內容
debugger tools : gdb、kdb、kgdb、Linux Trace Toolkit (LTT)、Dynamic Probes
裡面有提到一個有趣的User-Mode Linux,這是一個虛擬機器的機構,但是她不能讓使用者直接存取硬體
最後,其實作者的sample code的scull包含了chapter 3跟4,所以如果光看了chapter 3就去看code會感到怎麼還有很多多出來的東西
參考資料:
http://zh.wikipedia.org/wiki/Sysfs
http://blog.linux.org.tw/~asho/archives/001816.html
http://184.82.2.112/wordpress/?p=505
system log
sysklogd套件主要包含兩個訊息紀錄程式,一個是klogd(Kernel Log Daemon),另一個為 syslogd(System Log Daemon),兩個工具主要的不同在於klogd是紀錄Linux 核心訊息與Linux核心模組訊息,每當核心程式呼叫printk時,就可以由這個User-Mode的klogd程式來負責把此時的核心訊息紀錄下來,而syslogd則是負責User-Mode程式所需紀錄的系統訊息(例如紀錄在/var/log/messages的系統訊息)。
syslogd的控制可以由/etc/syslog.conf來控制,其次klogd是記錄在一個環狀的紀錄中,當紀錄太多的時候,新來的紀錄會覆蓋掉舊有的紀錄
2012年3月24日 星期六
Linux驅動程式--chapter 3
對於新手還真的有點辛苦的一章,一邊念著一邊看著原始碼,這一章節作者寫了個scull的character device的module,先利用了一些script(mknod)將這些虛擬裝置建構出來
首先替module本身與裝置連結,所以要註冊裝置的編號,在linux 2.6中使用major number跟minor number組成一個獨一無二的編號
MKDEV(scull_major, scull_minor)
跟著使用register_chrdev_region註冊數個裝置(依序,如果之前minor number是2, count是3,就會註冊2~4這幾個裝置),或者使用alloc_chrdev_region動態配置
因為作者使用記憶體取代實際可能為磁碟或者其他硬體裝得部份,所以他有些結構需要初始化,scull_devices這個變數對應每個虛擬裝置的相關資料
接著填充一些必要得功能,其中最重要的是struct file_operations這個結構,他包含了許多character device應該具備有的功能,如果沒有的話則保持NULL,如果使用者使用了不再表格內的function call,就會回傳錯誤。底下是一個範例
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};
該結構內並不完全都是function pointer,其中.owner就是表示這個module本身,THIS_MODULE是一個macro
另外可以看到就是read/write、llseek跟open/release這幾個常見的操作,但是為何不是close而是release,這是因為linux一般會對裝置紀錄一個count,表示有多少人或者process這在使用這個資源,只有當count為0的時候才會真真的close
接著必須對每個device作file_operations跟device的對應,使用cdev_init輸入兩個參數,分別是該裝置所需用到的資料結構(struct scull_dev)以及file_operations這個結構,作者假設有四個裝置,就分配要配置四份,但是file_operations這些操作是共用的
這裡有點好玩的是,並沒有強制規定同樣的file device必須有同樣的,甚至因為傳入的是指標,表示可以在半路抽換介面@@a,真是一個好玩的狀況!!
仔細觀察file_operations就會發見到,很多地方需要使用到struct file以及struct inode這兩個參數(由kernel傳入),inode裡面有個成員i_cdev,這裡面包含之前用cdev_init配給某個device的資料結構,需要透過struct scull_dev *dev = container_of(inode->i_cdev, struct scull_dev, cdev);這個巨集取出,這樣就可以知道process正在存取哪個device,並且使用相對應的資料空間
另外討論到記憶體的管理,如作者之前提過得,在kernel space並不能使用一般的libc函數,也就是malloc/free之類的記憶體配置不能使用,這裡必須使用kmalloc/kfree。另外值得注意的一點是如何在user space以及kernel space之間傳遞資料,這裡使用
unsigned long copy_to_user(void __user *to, const void *from, unsigned long count)
unsigned long copy_from_user(void *to, const void __user*from, unsigned long count)
在這兩個呼叫,kernel還必須要檢查適當地存取權限,還有處理使用者指向的記憶體可能是page fault的問題
所以整體來說,簡化的架構就是
首先替module本身與裝置連結,所以要註冊裝置的編號,在linux 2.6中使用major number跟minor number組成一個獨一無二的編號
MKDEV(scull_major, scull_minor)
跟著使用register_chrdev_region註冊數個裝置(依序,如果之前minor number是2, count是3,就會註冊2~4這幾個裝置),或者使用alloc_chrdev_region動態配置
因為作者使用記憶體取代實際可能為磁碟或者其他硬體裝得部份,所以他有些結構需要初始化,scull_devices這個變數對應每個虛擬裝置的相關資料
接著填充一些必要得功能,其中最重要的是struct file_operations這個結構,他包含了許多character device應該具備有的功能,如果沒有的話則保持NULL,如果使用者使用了不再表格內的function call,就會回傳錯誤。底下是一個範例
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};
該結構內並不完全都是function pointer,其中.owner就是表示這個module本身,THIS_MODULE是一個macro
另外可以看到就是read/write、llseek跟open/release這幾個常見的操作,但是為何不是close而是release,這是因為linux一般會對裝置紀錄一個count,表示有多少人或者process這在使用這個資源,只有當count為0的時候才會真真的close
接著必須對每個device作file_operations跟device的對應,使用cdev_init輸入兩個參數,分別是該裝置所需用到的資料結構(struct scull_dev)以及file_operations這個結構,作者假設有四個裝置,就分配要配置四份,但是file_operations這些操作是共用的
這裡有點好玩的是,並沒有強制規定同樣的file device必須有同樣的,甚至因為傳入的是指標,表示可以在半路抽換介面@@a,真是一個好玩的狀況!!
仔細觀察file_operations就會發見到,很多地方需要使用到struct file以及struct inode這兩個參數(由kernel傳入),inode裡面有個成員i_cdev,這裡面包含之前用cdev_init配給某個device的資料結構,需要透過struct scull_dev *dev = container_of(inode->i_cdev, struct scull_dev, cdev);這個巨集取出,這樣就可以知道process正在存取哪個device,並且使用相對應的資料空間
另外討論到記憶體的管理,如作者之前提過得,在kernel space並不能使用一般的libc函數,也就是malloc/free之類的記憶體配置不能使用,這裡必須使用kmalloc/kfree。另外值得注意的一點是如何在user space以及kernel space之間傳遞資料,這裡使用
unsigned long copy_to_user(void __user *to, const void *from, unsigned long count)
unsigned long copy_from_user(void *to, const void __user*from, unsigned long count)
在這兩個呼叫,kernel還必須要檢查適當地存取權限,還有處理使用者指向的記憶體可能是page fault的問題
所以整體來說,簡化的架構就是
- 註冊裝置
- 初始化相對應裝置的資料
- 完成file_operations表格
- 完成相對應操作的函數
- 最後記得要清理資料
2012年3月23日 星期五
linux kernel的相關資料
這裡只是剛好爬文到網路上一個不錯的文庫,紀錄一下
驅動程序編寫基本流程
Linux內核的ioctl函數學習
linux內核中struct file_operations結構體介紹
幾種設備的相關開發文件:
Linux音頻編程指南
嵌入式系統中LCD驅動的實現原理
因為linux驅動程式一書在這個部份寫得比較簡略的,直接翻code在對照文中所寫,就會有豁然開朗的感覺,畢竟第一次接觸linux較完整module(相對於第二章來說,第三章的範例也完整許多),其中也稍微解釋到我以前的疑惑,kernel space與user space的資料交換
驅動程序編寫基本流程
Linux內核的ioctl函數學習
linux內核中struct file_operations結構體介紹
幾種設備的相關開發文件:
Linux音頻編程指南
嵌入式系統中LCD驅動的實現原理
因為linux驅動程式一書在這個部份寫得比較簡略的,直接翻code在對照文中所寫,就會有豁然開朗的感覺,畢竟第一次接觸linux較完整module(相對於第二章來說,第三章的範例也完整許多),其中也稍微解釋到我以前的疑惑,kernel space與user space的資料交換
2012年3月21日 星期三
Linux驅動程式--chapter 2
這一章節簡單的給了個module的入門,一個只是用來insmod跟rmmod的moduule
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int __init hello_init(void){
printk(KERN_ALERT "Hello, world\n");
return 0;
}
static void __exit hello exit(void){
printk("KERN_ALERT "Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
與一般user space不同,由於module運行於kernel space,所以並不連結一般的lib,所以類似printf就不能使用,只能使用printk,KERN_ALERT是一個macro,後面沒有逗點,module_init點出insmod module就運行的function,相對的module_exit則是rmmod的時候
設計module必須考量到
在headers內定義了許多macro是供給modinfo用的
有些關鍵字是C語言內沒有的,例如__init跟__exit,這是給compiler用的,分別表明該函數是初始化以及結束時該呼叫的,另外還有__initdata與__exitdata表示初始配置資料的函數跟離開時後歸還空間的函數,往往用於hotplug
其他關於編譯的細節以及一些版本號碼控管有空再說,本身這是一章節談論到很多關於kernel module的觀念
這本書每章節後面都會簡單的summary這章節的內容,蠻有幫助的
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int __init hello_init(void){
printk(KERN_ALERT "Hello, world\n");
return 0;
}
static void __exit hello exit(void){
printk("KERN_ALERT "Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
與一般user space不同,由於module運行於kernel space,所以並不連結一般的lib,所以類似printf就不能使用,只能使用printk,KERN_ALERT是一個macro,後面沒有逗點,module_init點出insmod module就運行的function,相對的module_exit則是rmmod的時候
設計module必須考量到
- 是kernel space or user space(有些驅動程式是可以為user space,但是有好有壞)
- module變數的race condition
- module必須是reentrant
- module的lifetime幾乎等同kernel,從insmod到rmmod(往往就是開機到關機),中間變數一直存在,並不隨著被服務的process結束而結束
- module的記憶體有限,堆疊不可能配置太大的空間
- module往往跟目前服務的process有很大的關係,可以使用struct task_struct中的current指向目前的process
- module的相依性處理不完全在於程式,在於管理者必須知道使用modprobe來追蹤,但是這也表示module往往跟版本有很大的關係
在headers內定義了許多macro是供給modinfo用的
- MODULE_LICENSE : 是使用GPL or BSD還是其他
- MODULE_AUTHOR : 作者資訊
- MODULE_VERSION : 版本,通常表示支援的kernel version等等
- MODULE_DESCRIPTION : 描述
- MODULE_ALIAS : alias name
- MODULE_DEVICE_TABLE : 支援的device
有些關鍵字是C語言內沒有的,例如__init跟__exit,這是給compiler用的,分別表明該函數是初始化以及結束時該呼叫的,另外還有__initdata與__exitdata表示初始配置資料的函數跟離開時後歸還空間的函數,往往用於hotplug
其他關於編譯的細節以及一些版本號碼控管有空再說,本身這是一章節談論到很多關於kernel module的觀念
這本書每章節後面都會簡單的summary這章節的內容,蠻有幫助的
2012年3月20日 星期二
Linux驅動程式--chapter 1
玩嵌入式系統的人應該很難不動驅動程式有所牽連,不管是基於好奇也好,或者說萬般無奈為了某些功能也罷,驅動程式的確都是不可迴避的課題。講到這個,當然就屬於oreilly出版的linux驅動程式為一本入門的經典,接下來就是我拜讀的書本摘要,其實如果真的要鑽研linux驅動程式,這本書應該是放在你的架上的
驅動程式提供某些machanism,讓使用者依照某些policy來操作,所謂的machanism應該類似檔案操作的fopen, fwrite, fread, fclose,使用者要完成某項工作,例如讀取解析某些檔案,這些操作的程序比較類似policy。
linux將許多驅動程式變成了模組(module),好處是可以在隨時加入一些特性,主要依賴insmod與rmmod來引入與卸載模組。
模組大概可以依照硬體分成三類,character device、block device、queue,網路介面比較使用queue來對待
linux驅動程式強烈跟核心(kernel)相關,所以必須確定使用的核心版本,許多驅動程式在不同版本核心往往不保證相容性,因為kernel的API異動可能造成驅動程式的問題
因為現在已經新的開發大多已經使用linux 2.6以後的核心了,這也是這本書的第三版瞄準的目標,第二版是linux 2.4,2.4=>2.6架構上有很大的變動,所以舊版/二版的書基本上只能運作在2.4,要買書的讀者要注意
驅動程式提供某些machanism,讓使用者依照某些policy來操作,所謂的machanism應該類似檔案操作的fopen, fwrite, fread, fclose,使用者要完成某項工作,例如讀取解析某些檔案,這些操作的程序比較類似policy。
linux將許多驅動程式變成了模組(module),好處是可以在隨時加入一些特性,主要依賴insmod與rmmod來引入與卸載模組。
模組大概可以依照硬體分成三類,character device、block device、queue,網路介面比較使用queue來對待
linux驅動程式強烈跟核心(kernel)相關,所以必須確定使用的核心版本,許多驅動程式在不同版本核心往往不保證相容性,因為kernel的API異動可能造成驅動程式的問題
因為現在已經新的開發大多已經使用linux 2.6以後的核心了,這也是這本書的第三版瞄準的目標,第二版是linux 2.4,2.4=>2.6架構上有很大的變動,所以舊版/二版的書基本上只能運作在2.4,要買書的讀者要注意
訂閱:
文章 (Atom)



