你真的懂Linux內(nèi)核中的阻塞和異步通知機(jī)制嗎旋奢?(花了五天整理,墻裂推薦H蝗)

@[TOC]

阻塞/非阻塞簡介

??阻塞操作是指在執(zhí)行設(shè)備操作時(shí)至朗,若不能獲得資源,則掛起進(jìn)程直到滿足可操作的條件后再進(jìn)行操作剧浸。被掛起的進(jìn)程進(jìn)入睡眠狀態(tài)锹引,被從調(diào)度器的運(yùn)行隊(duì)列移走,直到等待的條件被滿足唆香。而非阻塞操作的進(jìn)程在不能進(jìn)行設(shè)備操作時(shí)嫌变,并不掛起,它要么放棄躬它,要么不停地查詢腾啥,直至可以進(jìn)行操作為止。

阻塞/非阻塞例程

??阻塞方式

int fd;
int data = 0;
fd = open("/dev/xxx_dev", O_RDWR); /* 阻塞方式打開 */
ret = read(fd, &data, sizeof(data)); /* 讀取數(shù)據(jù) */

??非阻塞方式

int fd;
int data = 0; 
fd = open("/dev/xxx_dev", O_RDWR | O_NONBLOCK); /* 非阻塞方式打開 */ 
ret = read(fd, &data, sizeof(data)); /* 讀取數(shù)據(jù) */

等待隊(duì)列簡介

??等待隊(duì)列是內(nèi)核中一個(gè)重要的數(shù)據(jù)結(jié)構(gòu)冯吓。阻塞方式訪問設(shè)備時(shí)倘待,如果設(shè)備不可操作,那么進(jìn)程就會(huì)進(jìn)入休眠狀態(tài)组贺。等待隊(duì)列就是來完成進(jìn)程休眠操作的一種數(shù)據(jù)結(jié)構(gòu)凸舵。

等待隊(duì)列相關(guān)函數(shù)

定義等待隊(duì)列

wait_queue_head_t my_queue;

??wait_queue_head_t是__wait_queue_head結(jié)構(gòu)體的一個(gè)typedef。

初始化等待隊(duì)列頭

void init_waitqueue_head(wait_queue_head_t *q)

??參數(shù)q就是要初始化的等待隊(duì)列頭失尖,也可以使用宏 DECLARE_WAIT_QUEUE_HEAD (name)來一次性完成等待隊(duì)列頭的定義的初始化啊奄。

定義并初始化一個(gè)等待隊(duì)列項(xiàng)

DECLARE_WAITQUEUE(name, tsk)

??name就是等待隊(duì)列項(xiàng)的名字,tsk表示這個(gè)等待隊(duì)列項(xiàng)屬于哪個(gè)任務(wù)進(jìn)程掀潮,一般設(shè)置為current菇夸,在 Linux內(nèi)核中 current相當(dāng)于一個(gè)全局變量,表示當(dāng)前進(jìn)程胧辽。因此宏DECLARE_WAITQUEUE就是給當(dāng)前正在運(yùn)行的進(jìn)程創(chuàng)建并初始化了一個(gè)等待隊(duì)列項(xiàng)峻仇。

將隊(duì)列項(xiàng)添加到等待隊(duì)列頭

void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

??q:等待隊(duì)列項(xiàng)要加入的等待隊(duì)列頭
??wait:要加入的等待隊(duì)列項(xiàng)
??返回值:無

將隊(duì)列項(xiàng)從等待隊(duì)列頭移除

void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)

??q:要?jiǎng)h除的等待隊(duì)列項(xiàng)所處的等待隊(duì)列頭
??wait:要?jiǎng)h除的等待隊(duì)列項(xiàng)。
??返回值:無

等待喚醒

void wake_up(wait_queue_head_t *q) 
void wake_up_interruptible(wait_queue_head_t *q)

??q:就是要喚醒的等待隊(duì)列頭邑商,這兩個(gè)函數(shù)會(huì)將這個(gè)等待隊(duì)列頭中的所有進(jìn)程都喚醒
??wake_up函數(shù)可以喚醒處于 TASK_INTERRUPTIBLE和 TASK_UNINTERRUPTIBLE狀態(tài)的進(jìn)程摄咆,而wake_ up_ interruptible函數(shù)只能喚醒處于 TASK_INTERRUPTIBLE狀態(tài)的進(jìn)程

等待事件

wait_event(wq, condition)

??等待以wq為等待隊(duì)列頭的等待隊(duì)列被喚醒,前提是 condition條件必須滿足(為真)人断,否則一直阻塞吭从。此函數(shù)會(huì)將進(jìn)程設(shè)置為TASK _UNINTERRUPTIBLE狀態(tài)

wait_event_timeout(wq, condition, timeout)

??功能和 wait_event類似,但是此函數(shù)可以添加超時(shí)時(shí)間恶迈,以 jiffies為單位涩金。此函數(shù)有返回值谱醇,如果返回0的話表示超時(shí)時(shí)間到,而且 condition為假步做。為1的話表示 condition為真副渴,也就是條件滿足了。

wait_event_interruptible(wq, condition)

??與 wait event函數(shù)類似全度,但是此函數(shù)將進(jìn)程設(shè)置為 TASK_INTERRUPTIBLE煮剧,就是可以被信號打斷

wait_event_interruptible_timeout(wq, condition, timeout)

??與 wait event timeout函數(shù)類似将鸵,此函數(shù)也將進(jìn)程設(shè)置為 TASK_INTERRUPTIBLE勉盅,可以被信號打斷

輪詢

??當(dāng)應(yīng)用程序以非阻塞的方式訪問設(shè)備時(shí)顶掉,會(huì)一遍一遍的去查詢我們的設(shè)備是否可以訪問草娜,這個(gè)查詢操作就叫做輪詢。內(nèi)核中提供了poll痒筒,epoll宰闰,select函數(shù)來處理輪詢操作。當(dāng)應(yīng)用程序在上層通過poll凸克,epoll议蟆,select函數(shù)來查詢設(shè)備時(shí)闷沥,驅(qū)動(dòng)程序中的poll萎战,epoll,select函數(shù)就要在底層實(shí)現(xiàn)查詢舆逃,如果可以操作的話蚂维,就會(huì)從讀取設(shè)備的數(shù)據(jù)或者向設(shè)備寫入數(shù)據(jù)。

select

??函數(shù)原型

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)

??nfds:要操作的文件描述符個(gè)數(shù)路狮。
??readifds虫啥、 writefds和 exceptfds:這三個(gè)指針指向描述符集合,這三個(gè)參數(shù)指明了關(guān)心哪些描述符奄妨、需要滿足哪些條件等等涂籽,這三個(gè)參數(shù)都是fd_set類型的, fd_set類型變量的每一個(gè)位都代表了一個(gè)文件描述符砸抛。 readfds用于監(jiān)視指定描述符集的讀變化评雌,也就是監(jiān)視這些文件是否可以讀取,只要這些集合里面有一個(gè)文件可以讀取,那么 seclect就會(huì)返回一個(gè)大于0的值表示文件可以讀取直焙。如果沒有文件可以讀取景东,那么就會(huì)根據(jù) timeout參數(shù)來判斷是否超時(shí)”际模可以將 reads設(shè)置為NULL斤吐,表示不關(guān)心任何文件的讀變化。 writefds和 reads類似,只是 writers用于監(jiān)視這些文件是否可以進(jìn)行寫操作和措。 exceptfds用于監(jiān)視這些文件的異常
??timeout:超時(shí)時(shí)間庄呈,當(dāng)我們調(diào)用 select函數(shù)等待某些文件描述符可以設(shè)置超時(shí)時(shí)間,超時(shí)時(shí)間使用結(jié)構(gòu)體 timeval表示派阱,結(jié)構(gòu)體定義如下所示:

struct timeval { 
long tv_sec; /* 秒 */
long tv_usec; /* 微妙 */ 
};

??當(dāng) timeout為NULL的時(shí)候就表示無限期的等待返回值抒痒。0,表示的話就表示超時(shí)發(fā)生颁褂,但是沒有任何文件描述符可以進(jìn)行操作故响;-1,發(fā)生錯(cuò)誤颁独;其他值彩届,可以進(jìn)行操作的文件描述符個(gè)數(shù)。
??操作fd_set變量的函數(shù)

void FD_ZERO(fd_set *set) 
void FD_SET(int fd, fd_set *set) 
void FD_CLR(int fd, fd_set *set) 
int FD_ISSET(int fd, fd_set *set)

??FD_ZERO用于將 fd set變量的所有位都清零誓酒, FD_SET用于將 fd_set變量的某個(gè)位置1樟蠕,也就是向 fd_set添加一個(gè)文件描述符,參數(shù)fd就是要加入的文件描述符靠柑。 FD_CLR用戶將 fd_set變量的某個(gè)位清零寨辩,也就是將一個(gè)文件描述符從 fd_set中刪除,參數(shù)fd就是要?jiǎng)h除的文件描述符歼冰。 FD_ISSET用于測試 fd_set的某個(gè)位是否置1靡狞,也就是判斷某個(gè)文件是否可以進(jìn)行操作,參數(shù)fd就是要判斷的文件描述符隔嫡。


void main(void) 
{  int ret, fd; /* 要監(jiān)視的文件描述符 */ 
    fd_set readfds; /* 讀操作文件描述符集 */
    struct timeval timeout; /* 超時(shí)結(jié)構(gòu)體 */ 
    fd = open("dev_xxx", O_RDWR | O_NONBLOCK); /* 非阻塞式訪問 */ 
    FD_ZERO(&readfds); /* 清除readfds */ 
    FD_SET(fd, &readfds); /* 將fd添加到readfds里面 */ 
     /* 構(gòu)造超時(shí)時(shí)間 */ 
     timeout.tv_sec = 0; 
     timeout.tv_usec = 500000; /* 500ms */ 
     ret = select(fd + 1, &readfds, NULL, NULL, &timeout); 
     switch (ret) { 
        case 0: /* 超時(shí) */ 
            printf("timeout!\r\n");
            break; 
        case -1: /* 錯(cuò)誤 */ 
            printf("error!\r\n"); 
            break; 
        default: /* 可以讀取數(shù)據(jù) */ 
        if(FD_ISSET(fd, &readfds))   /* 判斷是否為fd文件描述符 */ 
          {
               /* 使用read函數(shù)讀取數(shù)據(jù) */ 
          } 
         break; 
    } 
 }

poll

??在單個(gè)線程中甸怕, select函數(shù)能夠監(jiān)視的文件描述符數(shù)量有最大的限制,一般為1024腮恩,可以修改內(nèi)核將監(jiān)視的文件描述符數(shù)量改大梢杭,但是這樣會(huì)降低效率!這個(gè)時(shí)候就可以使用poll函數(shù)秸滴, poll函數(shù)本質(zhì)上和 select沒有太大的差別武契,但是poll函數(shù)沒有最大文件描述符限制,Linx應(yīng)用程序中poll函數(shù)原型如下所示:

int poll(struct pollfd *fds, nfds_t nfds, int timeout)

??函數(shù)參數(shù)和返回值含義如下
??fds:要監(jiān)視的文件描述符集合以及要監(jiān)視的事件荡含,為一個(gè)數(shù)組咒唆,數(shù)組元素都是結(jié)構(gòu)體 polled類型的, pollfd結(jié)構(gòu)體如下所示

struct pollfd 
{ 
    int fd; /* 文件描述符 文件描述符 文件描述符 */ 
    short events; /* 請求的事件 請求的事件 請求的事件 */
    short revents; /* 返回的事件 返回的事件 返回的事件 */ 
};

??fd是要監(jiān)視的文件描述符内颗,如果f無效的話那么 events監(jiān)視事件也就無效钧排,并且 revents返回0。 events是要監(jiān)視的事件均澳,可監(jiān)視的事件類型如下所示

POLLIN      //有數(shù)據(jù)可以讀取恨溜。
POLLPRI     //有緊急的數(shù)據(jù)需要讀取符衔。
POLLOUT     //可以寫數(shù)據(jù)POLLERR指定的文件描述符發(fā)生錯(cuò)誤POLLHUP指定的文件描述符掛起POLLNVAL無效的請求POLLRDNORM等同于 POLLIN

??revents:返回參數(shù),也就是返回的事件糟袁,有Linux內(nèi)核設(shè)置具體的返回事件判族。
??nfds:poll函數(shù)要監(jiān)視的文件描述符數(shù)量
??timeout:超時(shí)時(shí)間,單位為ms
??返回值:返回 revents域中不為0的 polled結(jié)構(gòu)體個(gè)數(shù)项戴,也就是發(fā)生事件或錯(cuò)誤的文件描述符數(shù)量形帮;0,超時(shí)周叮;-1辩撑,發(fā)生錯(cuò)誤,并且設(shè)置errno為錯(cuò)誤類型

void main(void)
{ 
   int ret; 
   int fd; /* 要監(jiān)視的文件描述符 */
   struct pollfd fds;
   fd = open(filename, O_RDWR | O_NONBLOCK); /* 非阻塞式訪問 */ 
    /* 構(gòu)造結(jié)構(gòu)體 */ 
   fds.fd = fd; 
   fds.events = POLLIN; /* 監(jiān)視數(shù)據(jù)是否可以讀取 */
   ret = poll(&fds, 1, 500); /* 輪詢文件是否可操作仿耽,超時(shí)500ms */ 
    if (ret)
     { /* 數(shù)據(jù)有效 */ 
      /* 讀取數(shù)據(jù) */ 
     } else if (ret == 0) 
     { 
         /* 超時(shí) */ 
     } else if (ret < 0) 
     {
          /* 錯(cuò)誤 */ 
     } 
 }

epoll

??傳統(tǒng)的 selcet和poll函數(shù)都會(huì)隨著所監(jiān)聽的fd數(shù)量的增加合冀,出現(xiàn)效率低下的問題,而且poll函數(shù)每次必須遍歷所有的描述符來檢查就緒的描述符项贺,這個(gè)過程很浪費(fèi)時(shí)間君躺。為此,epoll因運(yùn)而生开缎,epoll就是為處理大并發(fā)而準(zhǔn)備的棕叫,一般常常在網(wǎng)絡(luò)編程中使用epoll函數(shù)。應(yīng)用程序需要先使用 epoll_create函數(shù)創(chuàng)建一個(gè) epoll句柄奕删, epoll create函數(shù)原至如下.

int epoll_create(int size)

??函數(shù)參數(shù)和返回值含義如下:
??size俺泣;從 Linux2.6.8開始此參數(shù)已經(jīng)沒有意義了,隨便填寫一個(gè)大于0的值就可以
??返回值:epoll句柄急侥,如果為-1的話表示創(chuàng)建失敗,epoll句柄創(chuàng)建成功以后使用,epoll ctl函數(shù)向其中添加要監(jiān)視的文件描述符以及監(jiān)視的事ct函數(shù)原型如下所示

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

??函數(shù)參數(shù)和返回值含義如下
??epfd砌滞;要操作的epoll句柄侮邀,也就是使用 epoll_create函數(shù)創(chuàng)建的epoll句柄坏怪。
??p:表示要對epfd( epoll句柄)進(jìn)行的操作,可以設(shè)置為

EPOLL CTL ADD       //向印fd添加文件參數(shù)d表示的描述符EPOLL CTL MOD修改參數(shù)fd的 event事件绊茧。
EPOLL CTL DEL       //從f中刪除過l描述符

??fd:要監(jiān)視的文件描述
??event:要監(jiān)視的事件類型铝宵,為 epoll_event結(jié)構(gòu)體類型指針, epoll_event結(jié)構(gòu)體類型如下所

struct epoll_event 
{
    uint32_t events; /* epoll事件 */ 
    epoll_data_t data; /* 用戶數(shù)據(jù) 用戶數(shù)據(jù) */ 
};

??結(jié)構(gòu)體 epoll_event的 events成員變量表示要監(jiān)視的事件华畏,可選的事件如下所示

EPOLLIN         //有數(shù)據(jù)可以讀取EPOLLOUT可以寫數(shù)據(jù)
EPOLLPRI        //有緊急的數(shù)據(jù)需要讀取EPOLLERI指定的文件描述符發(fā)生錯(cuò)誤鹏秋。
EPOLLHUP        //指定的文件描述符掛起POLLET設(shè)置epo為邊沿觸發(fā),默認(rèn)觸發(fā)模式為水平觸發(fā)王
POLLONESHOT     //一次性的監(jiān)視亡笑,當(dāng)監(jiān)視完成以后還需要再次監(jiān)視某個(gè)fd侣夷,那么就需要將fd重新添加到 epoll 里面

??上面這些事件可以進(jìn)行“或”操作,也就是說可以設(shè)置監(jiān)視多個(gè)事件返回值:0仑乌,成功百拓;-1琴锭,失敗,并且設(shè)置errno的值為相應(yīng)的錯(cuò)誤碼衙传。一切都設(shè)置好以后應(yīng)用程序就可以通過 epoll_wait函數(shù)來等待事件的發(fā)生决帖,類似 select函數(shù)。 epoll_wait函數(shù)原型如下所示

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)

??函數(shù)參數(shù)和返回值含義如下
??epfd:要等待的 epoll
??events:指向 epoll_event結(jié)構(gòu)體的數(shù)組蓖捶,當(dāng)有事件發(fā)生的時(shí)候Iimx內(nèi)核會(huì)填寫 events地回,調(diào)用者可以根據(jù) events判斷發(fā)生了哪些事件。
??prevents:events數(shù)組大小俊鱼,必須大于0
??timeout:超時(shí)時(shí)間刻像,單位為ms返回值:0,超時(shí)并闲;-1绎速,錯(cuò)誤;其他值焙蚓,準(zhǔn)備就緒的文件描述符數(shù)量纹冤。
??epoll更多的是用在大規(guī)模的并發(fā)服務(wù)器上,因?yàn)樵谶@種場合下 select和poll并不適合购公。當(dāng)設(shè)計(jì)到的文件描述符(fd比較少的時(shí)候就適合用 selcet和pl本章我們就使用 sellect和poll這兩個(gè)函數(shù)

異步通知概念

??阻塞與非阻塞訪問萌京、poll函數(shù)提供了較好的解決設(shè)備訪問的機(jī)制,但是如果有了異步通知宏浩,整套機(jī)制則更加完整了知残。
??異步通知的意思是:一旦設(shè)備就緒,則主動(dòng)通知應(yīng)用程序比庄,這樣應(yīng)用程序根本就不需要查詢設(shè)備狀態(tài)求妹,這一點(diǎn)非常類似于硬件上“中斷”的概念,比較準(zhǔn)確的稱謂是“信號驅(qū)動(dòng)的異步I/O”佳窑。信號是在軟件層次上對中斷機(jī)制的一種模擬制恍,在原理上,一個(gè)進(jìn)程收到一個(gè)信號與處理器收到一個(gè)中斷請求可以說是一樣的神凑。信號是異步的净神,一個(gè)進(jìn)程不必通過任何操作來等待信號的到達(dá),事實(shí)上溉委,進(jìn)程也不知道信號到底什么時(shí)候到達(dá)鹃唯。
??阻塞I/O意味著一直等待設(shè)備可訪問后再訪問,非阻塞I/O中使用poll()意味著查詢設(shè)備是否可訪問瓣喊,而異步通知?jiǎng)t意味著設(shè)備通知用戶自身可訪問坡慌,之后用戶再進(jìn)行I/O處理。由此可見藻三,這幾種I/O方式可以相互補(bǔ)充洪橘。

Linux信號

??異步通知的核心就是信號絮爷,在 arch/xtensa/include/uapi/asm/signal.h文件中定義了Linux所支持的所有信號

#define SIGHUP      1/* 終端掛起或控制進(jìn)程終止 */ 
#define SIGINT      2/* 終端中斷(Ctrl+C組合鍵) */ 
#define SIGQUIT     3 /* 終端退出(Ctrl+\組合鍵) */
#define SIGILL      4/* 非法指令 */ 
#define SIGTRAP     5/* debug使用,有斷點(diǎn)指令產(chǎn)生 */
#define SIGABRT     6/* 由abort(3)發(fā)出的退出指令 */ 
#define SIGIOT      6 /* IOT指令 */ 
#define SIGBUS      7 /* 總線錯(cuò)誤 */ 
#define SIGFPE      8 /* 浮點(diǎn)運(yùn)算錯(cuò)誤 */ 
#define SIGKILL     9 /* 殺死梨树、終止進(jìn)程 */ 
#define SIGUSR1     10 /* 用戶自定義信號1 */ 
#define SIGSEGV     11 /* 段違例(無效的內(nèi)存段) */
#define SIGUSR2     12 /* 用戶自定義信號2 */ 
#define SIGPIPE     13 /* 向非讀管道寫入數(shù)據(jù) */ 
#define SIGALRM     14 /* 鬧鐘 */
#define SIGTERM     15 /* 軟件終止 */
#define SIGSTKFLT   16 /* 棧異常 */
#define SIGCHLD     17 /* 子進(jìn)程結(jié)束 */
#define SIGCONT     18 /* 進(jìn)程繼續(xù) */
#define SIGSTOP     19 /* 停止進(jìn)程的執(zhí)行坑夯,只是暫停 */
#define SIGTSTP     20 /* 停止進(jìn)程的運(yùn)行(Ctrl+Z組合鍵) */ 
#define SIGTTIN     21 /* 后臺進(jìn)程需要從終端讀取數(shù)據(jù) */ 
#define SIGTTOU     22 /* 后臺進(jìn)程需要向終端寫數(shù)據(jù) */
#define SIGURG      23 /* 有"緊急"數(shù)據(jù) */
#define SIGXCPU     24 /* 超過CPU資源限制 */ 
#define SIGXFSZ     25 /* 文件大小超額 */ 
#define SIGVTALRM   26 /* 虛擬時(shí)鐘信號 */ 
#define SIGPROF     27 /* 時(shí)鐘信號描述 */
#define SIGWINCH    28 /* 窗口大小改變 */ 
#define SIGIO       29 /* 可以進(jìn)行輸入/輸出操作 */
#define SIGPOLL SIGIO 
 /* #define SIGLOS 29 */ 
#define SIGPWR      30 /* 斷點(diǎn)重啟 */ 
#define SIGSYS      31 /* 非法的系統(tǒng)調(diào)用 */ 
#define SIGUNUSED   31 /* 未使用信號 */

異步通知代碼

??我們使用中斷的時(shí)候需要設(shè)置中斷處理函數(shù),同樣的抡四,如果要在應(yīng)用程序中使用信號柜蜈,那么就必須設(shè)置信號所使用的信號處理函數(shù),在應(yīng)用程序中使用 signal函數(shù)來設(shè)置指定信號的處理函數(shù)指巡, signal函數(shù)原型如下所示

void (*signal(int signum, void (*handler))(int)))(int);

??該函數(shù)原型較難理解淑履,它可以分解為:

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler));

??第一個(gè)參數(shù)指定信號的值,第二個(gè)參數(shù)指定針對前面信號值的處理函數(shù)藻雪,若為SIG_IGN秘噪,表示忽略該信號;若為SIG_DFL勉耀,表示采用系統(tǒng)默認(rèn)方式處理信號指煎;若為用戶自定義的函數(shù),則信號被捕獲到后便斥,該函數(shù)將被執(zhí)行至壤。
??如果signal調(diào)用成功,它返回最后一次為信號signum綁定的處理函數(shù)的handler值枢纠,失敗則返回SIG_ERR像街。

驅(qū)動(dòng)中的信號處理

fasync_struct結(jié)構(gòu)體

??首先我們需要在驅(qū)動(dòng)程序中定義個(gè) fasync_struct結(jié)構(gòu)體指針變量, fasync_struct結(jié)構(gòu)體內(nèi)容如下

struct fasync_struct 
{ spinlock_t fa_lock; 
int magic; 
int fa_fd; 
struct fasync_struct *fa_next; 
struct file *fa_file; 
struct rcu_head fa_rcu; 
};

??一般將 fasync_struct結(jié)構(gòu)體指針變量定義到設(shè)備結(jié)構(gòu)體中晋渺,比如在xxx_dev結(jié)構(gòu)體中添加一個(gè) fasync_struct結(jié)構(gòu)體指針變量镰绎,結(jié)果如下所示

struct xxx_dev 
{ 
    struct device *dev; 
    struct class *cls;
    struct cdev cdev;
 ...... 
     struct fasync_struct *async_queue; /* 異步相關(guān)結(jié)構(gòu)體 */
 }; 

fasync函數(shù)

??如果要使用異步通知,需要在設(shè)備驅(qū)動(dòng)中實(shí)現(xiàn)file_ operations操作集中的 fasync函數(shù)木西,此函數(shù)格式如下所示:

int (*fasync) (int fd, struct file *filp, int on)

??fasync函數(shù)里面一般通過調(diào)用 fasync_helper函數(shù)來初始化前面定義的 fasync_struct結(jié)構(gòu)體指針畴栖, fasync_helper函數(shù)原型如下

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

??fasync_helper函數(shù)的前三個(gè)參數(shù)就是 fasync函數(shù)的那三個(gè)參數(shù),第四個(gè)參數(shù)就是要初始化的 fasync_ struct結(jié)構(gòu)體指針變量户魏。當(dāng)應(yīng)用程序通過結(jié)構(gòu)體指針變量驶臊。當(dāng)應(yīng)用程序通過“ fcntl(fd, F_SETFL, flags | FASYNC)”改變fasync標(biāo)記的時(shí)候,驅(qū)動(dòng)程序 file_operations操作集中的 fasync函數(shù)就會(huì)執(zhí)行叼丑。

struct xxx_dev 
{ 
    ......
    struct fasync_struct *async_queue; /* 異步相關(guān)結(jié)構(gòu)體 */ 
}; 
static int xxx_fasync(int fd, struct file *filp, int on)
{
     struct xxx_dev *dev = (xxx_dev)filp->private_data; 
     if (fasync_helper(fd, filp, on, &dev->async_queue) < 0) 
     return -EIO; 
     return 0; 
 } 
     static struct file_operations xxx_ops =
 { 
    ...... 
    .fasync = xxx_fasync,
      ...... 
 };

??在關(guān)閉驅(qū)動(dòng)文件的時(shí)候需要在file_ operations操作集中的 release函數(shù)中釋放 fasyn_fasync struct的釋放函數(shù)同樣為 fasync_helper, release函數(shù)參數(shù)參考實(shí)例如下

static int xxx_release(struct inode *inode, struct file *filp) 
 { 
    return xxx_fasync(-1, filp, 0); /* 刪除異步通知 */
 }
static struct file_operations xxx_ops =
 { 
    ...... 
    .release = xxx_release, 
 };

??第3行通過調(diào)用示例代碼 xxx_fasync函數(shù)來完成 fasync_struct的釋放工作,但是扛门,其最終還是通過 fasync_helper函數(shù)完成釋放工作鸠信。

kill_fasync函數(shù)

??當(dāng)設(shè)備可以訪問的時(shí)候,驅(qū)動(dòng)程序需要向應(yīng)用程序發(fā)出信號论寨,相當(dāng)于產(chǎn)生“中斷” kill_fasync函數(shù)負(fù)責(zé)發(fā)送指定的信號星立, kill_fasync函數(shù)原型如下所示

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

??函數(shù)參數(shù)和返回值含義如下:
??fasync struct 要操作的文件指針
??sig:要發(fā)送的信號
?? band:可讀時(shí)設(shè)置為 POLL IN爽茴,可寫時(shí)設(shè)置為 POLL OUT。
??返回值:無绰垂。

應(yīng)用程序?qū)Ξ惒酵ㄖ奶幚?/h2>

??應(yīng)用程序?qū)Ξ惒酵ㄖ奶幚戆ㄒ韵氯?br> ??1室奏、注冊信號處理函數(shù)應(yīng)用程序根據(jù)驅(qū)動(dòng)程序所使用的信號來設(shè)置信號的處理函數(shù),應(yīng)用程序使用 signal函數(shù)來設(shè)置信號的處理函數(shù)劲装。前面已經(jīng)詳細(xì)的講過了胧沫,這里就不細(xì)講了。
??2占业、將本應(yīng)用程序的進(jìn)程號告訴給內(nèi)核使用fcntl(fd, F_SETOWN, getpid)將本應(yīng)用程序的進(jìn)程號告訴給內(nèi)核
??3绒怨、開啟異步通知使用如下兩行程序開啟異步通知:

flags = fcntl(fd, F_GETFL); /* 獲取當(dāng)前的進(jìn)程狀態(tài)*/ 
fcntl(fd, F_SETFL, flags | FASYNC); /* 開啟當(dāng)前進(jìn)程異步通知功能 */

??重點(diǎn)就是通過 fcntl函數(shù)設(shè)置進(jìn)程狀態(tài)為 FASYNC,經(jīng)過這一步谦疾,驅(qū)動(dòng)程序中的 fasync函數(shù)就會(huì)執(zhí)行南蹂。

大家的鼓勵(lì)是我繼續(xù)創(chuàng)作的動(dòng)力,如果覺得寫的不錯(cuò)念恍,歡迎關(guān)注六剥,點(diǎn)贊,收藏峰伙,轉(zhuǎn)發(fā)仗考,謝謝!
有任何疑問均可以在主頁的個(gè)人介紹中找到我的聯(lián)系方式

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末词爬,一起剝皮案震驚了整個(gè)濱河市秃嗜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌顿膨,老刑警劉巖锅锨,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異恋沃,居然都是意外死亡必搞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門囊咏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恕洲,“玉大人,你說我怎么就攤上這事梅割∷冢” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵户辞,是天一觀的道長泌类。 經(jīng)常有香客問我,道長底燎,這世上最難降的妖魔是什么刃榨? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任弹砚,我火速辦了婚禮,結(jié)果婚禮上枢希,老公的妹妹穿的比我還像新娘桌吃。我一直安慰自己,他們只是感情好苞轿,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布茅诱。 她就那樣靜靜地躺著,像睡著了一般呕屎。 火紅的嫁衣襯著肌膚如雪让簿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天秀睛,我揣著相機(jī)與錄音尔当,去河邊找鬼。 笑死蹂安,一個(gè)胖子當(dāng)著我的面吹牛椭迎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播田盈,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼畜号,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了允瞧?” 一聲冷哼從身側(cè)響起简软,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎述暂,沒想到半個(gè)月后二打,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體牙瓢,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡稚伍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年罗洗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片艺配。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡察郁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出转唉,到底是詐尸還是另有隱情皮钠,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布酝掩,位于F島的核電站鳞芙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏期虾。R本人自食惡果不足惜原朝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望镶苞。 院中可真熱鬧喳坠,春花似錦、人聲如沸茂蚓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽聋涨。三九已至晾浴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牍白,已是汗流浹背脊凰。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留茂腥,地道東北人狸涌。 一個(gè)月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像最岗,于是被迫代替她去往敵國和親帕胆。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容