天天看點

#導入Word文檔圖檔# Linux下核心與應用層異步通信方法​

12.1 簡介​

異步通知fasync應用于系統調用signal和sigaction函數,簡單的說,signal函數就是讓一個信号與與一個函數對應,每當接收到這個信号就會調用相應的函數。 ​

那麼什麼是異步通知?​

異步通知類似于中斷的機制,當裝置可操作時,裝置驅動函數發送一個信号給核心,告知核心有資料可操作,在條件不滿足之前,并不會造成阻塞。而不像之前學的阻塞型IO和poll,它們是調用函數進去檢查,條件不滿足時還會造成阻塞。

在實際應用中,在裝置已經準備好的時候,我們希望通知使用者程式裝置已經ok,使用者程式可以讀取了,這樣應用程式就不需要一直查詢該裝置的狀态,進而節約了資源。​

12.2 應用層信号捕獲​

12.2.1 函數接口​

檢視幫助:man signal​

#include <signal.h>​

typedef void (*sighandler_t)(int);​

sighandler_t signal(int signum, sighandler_t handler);​

函數原型​ sighandler_t signal(int signum, sighandler_t handler);​
函數功能​ 将一個給定的函數和一個特定的信号關聯​
函數參數​

signum:我們要進行處理的信号。系統的信号我們可以再終端鍵入 kill -l檢視(共64個)。其實這些信号時系統定義的宏。​

handler:設定處理信号的回調函數,和中斷服務函數類似​

使用範例:signal(SIGIO, mysignal);​

函數傳回值​ 函數指針​
函數頭檔案​ #include <signal.h>​

12.2.2 信号捕獲架構示例​

#include <stdio.h>​

#include <signal.h> /*信号處理相關的頭檔案*/​

/*1.2 信号處理函數*/​

void sighandler_app(int sem)​

{​

就是捕獲到的信号值​

printf("捕獲的信号: %d\n",sem);​

}​

int main(int argv,char*argc[])​

{​

信号綁定*/​

signal(SIGINT,sighandler_app);​

while(1)​

{​

sleep(10);​

...............................................................................................................​

}​

}​

12.2.3 信号發送示例​

(1) 終端指令發送信号​

指令行kill發送信号的方式:​

方式1 :kill -s 信号名 程序PID号​

方式2 :kill -信号名 程序PID号​

鍵盤上信号發送的快捷鍵:​

ctrl + \ 程式終止信号-信号代碼3​

ctrl + c 程式終止信号-信号代碼2​

(2) 代碼方式發送信号(system函數)​

使用system函數調用終端的shell指令!​

檢視幫助資訊:man system​

#include <stdlib.h>​

int system(const char *command);​

通過代碼方式發送信号示例:​

system(“kill -s 信号名 程序PID号”);​

(3) 調用kill函數發送信号​

檢視幫助:​

[root@xiaolong 2016-5-3]# man 1 kill​

[root@xiaolong 2016-5-3]# man 2 kill​

上面的數字編号就是表示檢視哪一頁的幫助資訊!​

kill函數原型:​

#include <sys/types.h>​

#include <signal.h>​

int kill(pid_t pid, int sig);​

字元串轉整數函數API函數原型:​

#include <stdlib.h>​

int atoi(const char *nptr);​

long atol(const char *nptr);​

C代碼方式發送信号示例:​

#include <stdio.h>​

#include <stdlib.h>​

#include <string.h>​

#include <signal.h>​

int main(int argc,char **argv)​

{​

int pid;​

int signal;​

if(argc!=3)​

{​

printf("傳參數格式:.app Signal信号 PID号 \n");​

return -1;​

}​

将字元串轉為整型傳回​

pid=atoi(argv[2]);将字元串轉為整型傳回​

發送信号​

return 0;​

}​

12.2.4 信号說明清單​

$ kill -l​

1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL​

5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE​

9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2​

13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD​

18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN​

22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ​

26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO​

30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1​

36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5​

40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9​

44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13​

48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13​

52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9​

56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5​

60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1​

64) SIGRTMAX​

清單中,編号為 1 ~ 31 的信号為傳統 UNIX 支援的信号,是不可靠信号(非實時的),編号為32 ~ 63 的信号是後來擴充的,稱做可靠信号(實時信号)。不可靠信号和可靠信号的差別在于前者不支援排隊,可能會造成信号丢失,而後者不會。​

下面我們對編号小于 SIGRTMIN 的信号進行讨論。​

1) SIGHUP​

本信号在使用者終端連接配接(正常或非正常)結束時發出, 通常是在終端的控制程序結束時, 通知同一 session 内的各個作業, 這時它們與控制終端不再關聯。​

登入 Linux 時,系統會配置設定給登入使用者一個終端(Session)。在這個終端運作的所有程式,包括前台程序組和背景程序組,一般都屬于這個 Session。當使用者退出 Linux 登入時,前台程序組和背景有對終端輸出的程序将會收到 SIGHUP 信号。​

這個信号的預設操作為終止程序,是以前台程序組和背景有終端輸出的程序就會中止。不過可以捕獲這個信号,比如 wget 能捕獲 SIGHUP 信号,并忽略它,這樣就算退出了 Linux 登入,wget 也 能繼續下載下傳。此外,對于與終端脫離關系的守護程序,這個信号用于通知它重新讀取配置檔案。​

2) SIGINT​

程式終止(interrupt)信号, 在使用者鍵入 INTR 字元(通常是 Ctrl-C)時發出,用于通知前台進​

程組終止程序。​

3) SIGQUIT和 SIGINT 類似, 但由 QUIT 字元(通常是 Ctrl-\)來控制. 程序在因收到 SIGQUIT 退出時會産生 core 檔案, 在這個意義上類似于一個程式錯誤信号。​

4) SIGILL​

執行了非法指令. 通常是因為可執行檔案本身出現錯誤, 或者試圖執行資料段. 堆棧溢出時也有可能産生這個信号。​

5) SIGTRAP​

由斷點指令或其它 trap 指令産生. 由 debugger 使用。​

6) SIGABRT​

調用 abort 函數生成的信号。​

7) SIGBUS​

非法位址, 包括記憶體位址對齊(alignment)出錯。比如通路一個四個字長的整數, 但其位址不是 4 的倍數。它與 SIGSEGV 的差別在于後者是由于對合法存儲位址的非法通路觸發的(如通路不屬于自己存儲空間或隻讀存儲空間)。​

8) SIGFPE​

在發生緻命的算術運算錯誤時發出. 不僅包括浮點運算錯誤, 還包括溢出及除數為 0 等其它所有的算術的錯誤。​

9) SIGKILL​

用來立即結束程式的運作. 本信号不能被阻塞、處理和忽略。如果管理者發現某個程序終止不了,可嘗試發送這個信号。​

10) SIGUSR1留給使用者使用​

11) SIGSEGV​

試圖通路未配置設定給自己的記憶體, 或試圖往沒有寫權限的記憶體位址寫資料.​

12) SIGUSR2​

留給使用者使用​

13) SIGPIPE​

管道破裂。這個信号通常在程序間通信産生,比如采用 FIFO(管道)通信的兩個程序,讀管道沒打開或者意外終止就往管道寫,寫程序會收到 SIGPIPE 信号。此外用 Socket 通信的兩個程序,寫程序在寫 Socket 的時候,讀程序已經終止。​

14) SIGALRM​

時鐘定時信号, 計算的是實際的時間或時鐘時間. alarm 函數使用該信号.​

15) SIGTERM​

程式結束(terminate)信号, 與 SIGKILL 不同的是該信号可以被阻塞和處理。通常用來要求程式自己正常退出,shell 指令 kill 預設産生這個信号。如果程序終止不了,我們才會嘗試SIGKILL。​

17) SIGCHLD​

子程序結束時, 父程序會收到這個信号。如果父程序沒有處理這個信号,也沒有等待(wait)子程序,子程序雖然終止,但是還會在核心程序表中占有表項,這時的子程序稱為僵屍程序。這種情況我們應該避免(父程序或者忽略 SIGCHILD 信号,或者捕捉它,或者 wait 它派生的子程序,或者父程序先終止,這時子程序的終止自動由 init 程序來接管)。​

18) SIGCONT​

讓一個停止(stopped)的程序繼續執行. 本信号不能被阻塞. 可以用一個 handler 來讓程式在由 stopped 狀态變為繼續執行時完成特定的工作. 例如, 重新顯示提示符...​

19) SIGSTOP​

停止(stopped)程序的執行. 注意它和 terminate 以及 interrupt 的差別:該程序還未結束,隻是暫停執行. 本信号不能被阻塞, 處理或忽略.​

20) SIGTSTP​

停止程序的運作, 但該信号可以被處理和忽略. 使用者鍵入 SUSP 字元時(通常是 Ctrl-Z)發出這個信号​

21) SIGTTIN​

當背景作業要從使用者終端讀資料時, 該作業中的所有程序會收到 SIGTTIN 信号. 預設時這些程序會停止執行.​

22) SIGTTOU​

類似于 SIGTTIN, 但在寫終端(或修改終端模式)時收到.​

23) SIGURG​

有"緊急"資料或 out-of-band 資料到達 socket 時産生.​

24) SIGXCPU​

超過 CPU 時間資源限制. 這個限制可以由 getrlimit/setrlimit 來讀取/改變。​

25) SIGXFSZ​

當程序企圖擴大檔案以至于超過檔案大小資源限制。​

26) SIGVTALRM​

虛拟時鐘信号. 類似于 SIGALRM, 但是計算的是該程序占用的 CPU 時間.​

27) SIGPROF​

類似于 SIGALRM/SIGVTALRM, 但包括該程序用的 CPU 時間以及系統調用的時間.​

28) SIGWINCH​

視窗大小改變時發出.​

29) SIGIO​

檔案描述符準備就緒, 可以開始進行輸入/輸出操作.​

30) SIGPWR​

Power failure​

31) SIGSYS​

非法的系統調用。​

在以上列出的信号中,程式不可捕獲、阻塞或忽略的信号有:SIGKILL,SIGSTOP,不能恢複至預設動作的信号有:SIGILL,SIGTRAP​

  • 預設會導緻程序流産的信号有:​SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ​
  • 預設會導緻程序退出的信号有:​SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM​
  • 預設會導緻程序停止的信号有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU​
  • 預設程序忽略的信号有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH​

此外,SIGIO 在 SVR4 是退出,在 4.3BSD 中是忽略;SIGCONT 在程序挂起時是繼續,否則是忽略,不能被阻塞​

12.3 應用層異步通知功能設定​

使用者程式必須執行 2 個步驟來使能裝置檔案的異步通知功能。 首先, 需要指定一個程序作為裝置檔案的擁有者. 當一個程序使用 fcntl 系統調用發出 F_SETOWN 指令時, 可以将這個程序PID儲存在 filp->f_owner 成員裡,留着以後使用。 這一步是讓核心知道發送信号時該通知誰。​

為了真正使能異步通知, 使用者程式必須給裝置檔案設定 FASYNC 标志, 可以通過fcntl函數中的F_SETFL指令。​

在這 2 個調用已被執行後, 如果驅動端有資料需要處理, 裝置檔案(驅動端)可以向程序遞交一個 SIGIO 信号,​

信号發送後存儲在 filp->f_owner 成員中指定的程序(或者程序組)裡.​

示例步驟:使能異步的通知功能​

fp = open(argv[1],2);​

signal(SIGIO, mysignal);​

fcntl(fp,F_SETOWN,getpid());​

f_flags = fcntl(fp,F_GETFL);​

fcntl(fp,F_SETFL,f_flags|FASYNC);​

說明:FASYNC與O_ASYNC标志都表示是異步IO标志。在不同的系統裡表示不一樣。​

#導入Word文檔圖檔# Linux下核心與應用層異步通信方法​

12.4 驅動層異步通知接口​

12.4.1 驅動端操作步驟​

對我們來說一個需要關注的主題是裝置驅動如何實作異步信号。​

下面列出了詳細的操作順序:​

當應用層發出 F_SETOWN指令時, 除了一個值被指派給 filp->f_owner,不會産生其他效果。​

當應用層發出F_SETFL指令時,會設定裝置驅動的FASYNC标志,這時驅動層的 fasync 方法函數會導緻被調用。 ​

當驅動資料需要處理時, 可以向所注冊異步通知的程序發出一個 SIGIO 信号。​

驅動端需要調用的2個函數對應下面的原型:​

int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp);​

fasync_helper函數被用來從相關的程序清單中添加或去除入口項。​

void kill_fasync(struct fasync_struct **fp, int sig, int band);​

當資料到達時可以使用kill_fasync函數發送信号來通知相關的程序。(一般在中斷處理函數裡調用)​

參數:​

  • band參數一般都是填: POLL_IN 。表示驅動端有資料可讀!​

可以填寫的選項:​

#define POLL_IN (__SI_POLL|1) /* 可用的資料輸入 */​

#define POLL_OUT (__SI_POLL|2) 輸出緩沖區可用 */​

#define POLL_MSG (__SI_POLL|3) 輸入消息可用 */​

#define POLL_ERR (__SI_POLL|4) /* i/o error */​

#define POLL_PRI (__SI_POLL|5) 高優先級輸入可用 */​

#define POLL_HUP (__SI_POLL|6) 裝置斷開連接配接 */​

  • int sig :向應用層發送的信号。(kill -l檢視)​
#導入Word文檔圖檔# Linux下核心與應用層異步通信方法​

預設情況下,裝置發送的信号時29,SIGIO信号,如果想換成其他信号,可以通過F_SETSIG指令進行。​

printf("fcntl=%d\n",fcntl(fd,F_SETSIG,2)); //設定成功傳回0.​

注意: 不能設定系統預定義之外的信号。​

正常情況下,程式裡加入F_SETSIG宏,編譯器時無法編譯過的。​

需要加上參數: -D_GNU_SOURCE​

arm-linux-gcc scanf_app.c -o app -D_GNU_SOURCE​

12.4.2 驅動異步通知代碼架構​

static struct fasync_struct * myfasync; //定義一個異步通知二維指針結構體​

//實作檔案操作集合的函數​

int xxxx_fasync (int fd, struct file * filp, int on)​

{​

/*添加異步通知的入口選項*/​

fasync_helper(fd,filp,on,&myfasync);​

return 0;​

}​

//中斷處理函數​

static void interrupu_fun(void)​

{​

發送信号​

}​

//release函數​

static int key_release (struct inode *inode , struct file *filp)​

{​

xxxx_fasync(-1, filp, 0) ; //裝置釋放時,需要将檔案從異步通知清單中删除​

return 0;​

}​

  • 發送的信号值分析:​

注意:kill_fasync函數中填的信号值沒有作用,真正發送的信号由以下結構體定義:​

#導入Word文檔圖檔# Linux下核心與應用層異步通信方法​
#導入Word文檔圖檔# Linux下核心與應用層異步通信方法​
#導入Word文檔圖檔# Linux下核心與應用層異步通信方法​

或者在應用層通過fcntl(fd,10,SIGBUS);函數告訴驅動應該發送什麼信号。這裡的10就是F_SETSIG宏,隻是有些編譯識别不到F_SETSIG這個宏,隻有直接寫10。SIGBUS表示将要給驅動設定的信号。設定以後,驅動裡調用kill_fasync函數将會固定發送SIGBUS信号。​

12.5 分析異步通知函數的調用流程​

相關路徑:\fs\fcntl.c ​

應用層的fcntl函數會調用驅動層的do_fcntl函數。​

do_fcntl函數的原型如下:​

/*系統調用函數--*/​

static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,​

struct file *filp)​

{​

long err = -EINVAL;​

switch (cmd) {​

case F_DUPFD:​

case F_DUPFD_CLOEXEC:​

if (arg >= rlimit(RLIMIT_NOFILE))​

break;​

err = alloc_fd(arg, cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0);​

if (err >= 0) {​

get_file(filp);​

fd_install(err, filp);​

}​

break;​

case F_GETFD: //擷取PID号​

err = get_close_on_exec(fd) ? FD_CLOEXEC : 0;​

break;​

case F_SETFD:​

err = 0;​

set_close_on_exec(fd, arg & FD_CLOEXEC);​

break;​

case F_GETFL:擷取檔案标志​

err = filp->f_flags;​

break;​

case F_SETFL:​

err = setfl(fd, filp, arg);​

break;​

case F_GETLK:​

err = fcntl_getlk(filp, (struct flock __user *) arg);​

break;​

case F_SETLK:​

case F_SETLKW:​

err = fcntl_setlk(fd, filp, cmd, (struct flock __user *) arg);​

break;​

case F_GETOWN:​

err = f_getown(filp);​

force_successful_syscall_return();​

break;​

case F_SETOWN: //設定OWN​

err = f_setown(filp, arg, 1);​

break;​

case F_GETOWN_EX:​

err = f_getown_ex(filp, arg);​

break;​

case F_SETOWN_EX:​

err = f_setown_ex(filp, arg);​

break;​

case F_GETSIG:​

err = filp->f_owner.signum;​

break;​

case F_SETSIG:​

/* arg == 0 restores default behaviour. */​

if (!valid_signal(arg)) {​

break;​

}​

err = 0;​

filp->f_owner.signum = arg;​

break;​

case F_GETLEASE:​

err = fcntl_getlease(filp);​

break;​

case F_SETLEASE:​

err = fcntl_setlease(fd, filp, arg);​

break;​

case F_NOTIFY:​

err = fcntl_dirnotify(fd, filp, arg);​

break;​

case F_SETPIPE_SZ:​

case F_GETPIPE_SZ:​

err = pipe_fcntl(filp, cmd, arg);​

break;​

default:​

break;​

}​

return err;​

}​

該函數内部主要是一個switch接口,根據傳入的指令判斷主要做什麼工作!​

12.6 異步通知示例代碼​

下面代碼通過按鍵驅動程式示範異步IO操作方法。​

12.6.1 應用層代碼示例​

#include <stdio.h>​

#include <sys/types.h>​

#include <sys/stat.h>​

#include <fcntl.h>​

#include <sys/select.h>​

#include <sys/time.h>​

#include <sys/types.h>​

#include <unistd.h>​

#include <sys/epoll.h>​

#include <stdlib.h>​

#include <signal.h>​

int fd;​

/*信号處理程式*/​

void sighandler(int num)​

{​

int data;​

read(fd,&data,4); //讀取按鍵值​

printf("data=0x%X\n",data);​

}​

int main(int argc,char **argv)​

{​

int f_flags;​

if(argc!=2)​

{​

printf("傳參格式:./app <裝置檔案的名稱>\n");​

}​

fd=open(argv[1],O_RDWR);​

if(fd<0)​

{​

printf("%s 驅動打開失敗!\n",argv[1]);​

return 0;​

}​

/*1. 綁定應用層将要捕獲的信号*/​

signal(SIGIO,sighandler);​

/*2. 設定驅動檔案描述符支援異步通知功能*/​

fcntl(fd,F_SETOWN,getpid()); //傳遞目前程序的PID給驅動檔案描述符​

f_flags = fcntl(fd,F_GETFL); //擷取目前檔案描述符的屬性​

fcntl(fd,F_SETFL,f_flags|FASYNC);//設定目前驅動支援異步IO屬性​

while(1)​

{​

}​

close(fd);​

return 0;​

}​

12.6.2 驅動層代碼示例​

#include <linux/kernel.h> //核心頭檔案​

#include <linux/module.h> //子產品​

#include <linux/fs.h>​

#include <linux/gpio.h>​

#include <mach/gpio.h>​

#include <plat/gpio-cfg.h>​

#include <linux/irq.h>​

#include <linux/interrupt.h>​

#include <linux/miscdevice.h>​

#include <linux/uaccess.h>​

#include <linux/wait.h> /*等待隊列頭檔案*/​

#include <linux/sched.h>​

#include <linux/poll.h>​

#include <linux/signal.h>​

static int key_val=0;​

static struct fasync_struct *fapp;​

static int key_open(struct inode *my_inode, struct file *my_file)​

{​

printk("key_open調用成功!\n");​

return 0;​

}​

static ssize_t key_read(struct file *my_file, char __user *buff, size_t cnt, loff_t *loff)​

{​

unsigned long stat=copy_to_user(buff,&key_val,4); /*向應用層傳遞資料*/​

return 0;​

}​

static ssize_t key_write(struct file *my_file, const char __user *buff, size_t cnt, loff_t *loff)​

{​

printk("key_write調用成功!\n");​

return 0;​

}​

static int key_release(struct inode *my_inode, struct file *my_file)​

{​

/*2. 解除安裝異步通知*/​

fasync_helper(0,my_file,0,&fapp);​

printk("key_release調用成功!\n");​

return 0;​

}​

static int key_fasync(int fd, struct file *file, int on)​

{​

/*1. 處理異步通知的信号,添加到處理清單*/​

return fasync_helper(fd,file,on,&fapp);​

}​

static struct file_operations fileops= /*檔案操作接口*/​

{​

.open=key_open,​

.read=key_read,​

.write=key_write,​

.release=key_release,​

.fasync=key_fasync,​

};​

static struct miscdevice misc=​

{​

.minor=MISC_DYNAMIC_MINOR, /*自動配置設定次裝置号*/​

.name="key_drv",​

.fops=&fileops,​

};​

static struct KEY_INFO​

{​

char *irq_name;​

int gpio_num;​

int key_val;​

int key_irq; /*中斷号*/​

};​

static struct KEY_INFO key_info[4]=​

{​

{"kry_irq_1",EXYNOS4_GPX3(2),0x1},​

{"kry_irq_2",EXYNOS4_GPX3(3),0x2},​

{"kry_irq_3",EXYNOS4_GPX3(4),0x3},​

{"kry_irq_4",EXYNOS4_GPX3(5),0x4},​

};​

/*按鍵的中斷服務函數*/​

irqreturn_t key_irq_handler(int irq, void *dev)​

{​

struct KEY_INFO *p=(struct KEY_INFO *)dev;​

if(gpio_get_value(p->gpio_num)==0)​

{​

key_val=0x80|p->key_val;​

}​

else​

{​

key_val=0x80;​

}​

/*2. 向應用層發送信号*/​

kill_fasync(&fapp,SIGIO,POLL_IN);​

return IRQ_HANDLED;​

}​

static int __init tiny4412_hello_module_init(void)​

{​

int i;​

for(i=0;i<4;i++)​

{​

/*擷取中斷編号*/​

key_info[i].key_irq=gpio_to_irq(key_info[i].gpio_num);​

/*中斷的注冊函數*/​

if(request_irq(key_info[i].key_irq,key_irq_handler,IRQ_TYPE_EDGE_BOTH,key_info[i].irq_name,&key_info[i])!=0)​

{​

printk("%s 中斷注冊失敗!\n",key_info[i].irq_name);​

}​

else​

{​

printk("%s 中斷注冊成功\n",key_info[i].irq_name);​

}​

}​

misc_register(&misc); //注冊函數​

return 0;​

}​

static void __exit tiny4412_hello_module_cleanup(void)​

{​

/*中斷的登出*/​

int i;​

for(i=0;i<4;i++)​

{​

free_irq(key_info[i].key_irq,&key_info[i]);​

}​

中斷登出成功!\n");​

misc_deregister(&misc); //登出函數​

}​

module_init(tiny4412_hello_module_init); //驅動的入口,驅動安裝的時候調用​

module_exit(tiny4412_hello_module_cleanup); //驅動的的出口,驅動解除安裝的時候調用​

MODULE_LICENSE("GPL"); //驅動的許可證聲​

繼續閱讀