2012年2月12日 星期日

用qemu觀察interrupt以及製作system call

網路上的資料教我們怎樣做到這兩件事情,還頗為有趣,首先是觀察interrupt
interrupt事實上在硬體設備上是有限的,所以大多時候是必須要分享的,除此之外,還有目前更複雜的多核心中斷處理(想想,一個中斷發生,但是有多個核心,誰該去處理這件事情?)。因為共享,所以有時候在設備有接上主機的時候,在跟系統要求中斷,不要用的時候必須歸還,或者說暫時切換,這樣才讓系統在有限的資源內可以為多個硬體的中斷做服務
  • request_irq() :向系統註冊使用某一個 interrupt ,並指定負責處理該 interrupt 的 ISR 。 
  • free_irq() :告知系統釋放特定的 interrupt 。

有了以上的概念,跟著就是直接切入code,在上面印出資訊,至kernel source目錄,進入drivers/mmc/mmci.c這個source code,這是一個SD Card Host Controller driver,在
ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);之前加上printk,因為已經在kernel mode就不要再用printf,printf是給user mode用的
printk("\n***************Hello Interrupt*******************\n");
重新編譯kernel
make CROSS_COMPILE=arm-linux- ARCH=arm
接著把產生出來的kernel image放到之前busybox目錄,跟著執行,這些中斷就註冊上去,所以在開機訊息上可以看到,如下圖(我拼錯字了~囧,不管他)

===============
接下來自行建立一個system call,system call是系統提供給一般程式開發者作為有限存取kernel功能的一個介面,可以想成開發kernel的人提供給programmer的API,必須要做的事情有五件
  1. 製作system call的功能,大致上一般程式設計差不多,不過是kernel mode
  2. 註冊 system call 的名字
  3. 定義新 system call 的代碼
  4. 調整 Makefile ,使 systam call 被包含在 kernel 中
  5. 增加 system call 的 header file ,讓 user program 能夠 include
第二、三件事情對於一般程式設計師比較陌生,但是如果有看過system programming的書籍,大致上會提到過,底下要注意,大多目錄是相對於kernel source code目錄,在定義function name、system call number有一些prefix words要注意

第一步在arch/arm/kernel下建立mytestcall.c,內容是
#include <linux/linkage.h>
#include <linux/kernel.h>
asmlinkage void sys_mytestcall(){
   static int count = 0; 
   printk("mytestcall has been called for %d time(s)\n", ++count); 
}
看到用了asmlinkage的修飾子跟sys_這個prefix

第二步在arch/arm/kernel/calls.S下將創立的system call的名稱做註冊上去在
CALL(sys_set_mempolicy)
後面加上
CALL(sys_mytestcall)

第三步驟,號碼註冊上去,直接就取前面那個最小的+1即可,前面最小的是,include/asm-arm/unistd.h裡面,最小的是底下
#define __NR_set_mempolicy (__NR_SYSCALL_BASE+321)

第四步驟,在arch/arm/kernel/Makefile裡面,sys_mytestcall.o加到target後面,讓他可以被編譯,這時候就可以重新編譯kernel,指令跟前面一樣
make CROSS_COMPILE=arm-linux- ARCH=arm

第五件事情,是為了一般programmer提供header file,就如大家寫lib,總是要提供head file讓人家include,才可以做symbol以及link,命名include/linux/mytestcall.h
#include <linux/unistd.h>
#define __NR_mysyscall (__NR_SYSCALL_BASE+322)
#define mytestcall(void) syscall(__NR_mytestcall);




===============
以上事情完成就可以開始寫測試程式了,我是直接就在linux kernel source code目錄底下建立一個名為testmycall.c,內容很簡單
#include "linux/mysyscall.h" 
int main(){
    mytestcall();
    return 0; 
}
跟著編譯他,指令如下
arm-linux-gcc -I ./include -static testmycall.c -o testmycall

完成後把新的kernel放到busybox目錄下,把這個testmycall拷貝到busybox內的_install目錄底下,然後重新用cpio命令打包_install目錄成root image,用qemu開機,跟著直行就可以看到成果了
這裡可以看到有趣的現象,一般的程式在結束的時候,static variable也跟著被釋放掉,再次執行該程式就重新來過,但是這裡system call是屬於kernel mode,他生命週期一般跟第一次載入記憶體後,就待到觀及,所以再次呼叫的時候,裡面的static變數還是繼續加1變成了2

一般而言儘量避免使用這種static用法,因為system call有點類似global變數,如果使用了這樣的用法,很容易造成race condition的情形。

沒有留言:

張貼留言