@[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)系方式