一、多路復用
?每個進程都有一個描述符數組,這個數組的下標為描述符,
描述符的分類:
-
文件描述符:設備文件、管道文件
-
socket描述符
1.1 應用層:三套接口select、poll、epoll
select:位運算實現 監(jiān)控的描述符數量有限(32位機1024,64位機2048) 效率差
poll:鏈表實現,監(jiān)控的描述符數量不限 效率差
epoll:效率最高,監(jiān)控的描述符數量不限文章來源:http://www.zghlxwxcb.cn/news/detail-814889.html
select文章來源地址http://www.zghlxwxcb.cn/news/detail-814889.html
int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout); /* 功能:監(jiān)聽多個描述符,阻塞等待有一個或者多個文件描述符,準備就緒。 內核將沒有準備就緒的文件描述符,從集合中清掉了。 參數: nfds 最大文件描述符數 ,加1 readfds 讀文件描述符集合 writefds 寫文件描述符集合 exceptfds 其他異常的文件描述符集合 timeout 超時時間(NULL) 返回值:當timeout為NULL時返回0,成功:準備好的文件描述的個數 出錯:-1 ? 當timeout不為NULL時,如超時設置為0,則select為非阻塞,超時設置 > 0,則無描述符可被操作的情況下阻塞指定長度的時間 */ void FD_CLR(int fd, fd_set *set); //功能:將fd 從集合中清除掉 ? int ?FD_ISSET(int fd, fd_set *set); //功能:判斷fd 是否存在于集合中 void FD_SET(int fd, fd_set *set); //功能:將fd 添加到集合中 ? void FD_ZERO(fd_set *set); //功能:將集合清零 ? //使用模型: ? while(1) { ? ?/*得到最大的描述符maxfd*/ ? ?/*FD_ZERO清空描述符集合*/ /*將被監(jiān)控描述符加到相應集合rfds里 FD_SET*/ ? ?/*設置超時*/ ? ?ret = select(maxfd+1,&rfds,&wfds,NULL,NULL); ? ?if(ret < 0) ? { ? ? ? ?if(errno == EINTR)//錯誤時信號引起的 ? ? ? { ? ? ? continue; ? ? ? ? } ? ? ? ?else ? ? ? { ? ? ? ? ? ?break; ? ? ? } ? } ? ?else if(ret == 0) ? {//超時 ? ? ? ?//..... ? } ? ?else ? { //> 0 ret為可被操作的描述符個數 ? ? ? ?if(FD_ISSET(fd1,&rfds)) ? ? ? {//讀數據 ? ? ? ? ? ?//.... ? ? ? } ? ? ? ?if(FD_ISSET(fd2,&rfds)) ? ? ? {//讀數據 ? ? ? ? ? ?//.... ? ? ? } ? ? ? ?///..... ? ? ? ?if(FD_ISSET(fd1,&wfds)) ? ? ? {//寫數據 ? ? ? ? ? ?//.... ? ? ? } ? } } ?
1.2 驅動層:實現poll函數
void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p); /*功能:將等待隊列頭添加至poll_table表中 ?參數:struct file :設備文件 ?Wait_queue_head_t :等待隊列頭 ?Poll_table :poll_table表 */ ? /*該函數與select、poll、epoll_wait函數相對應,協(xié)助這些多路監(jiān)控函數判斷本設備是否有數據可讀寫*/ unsigned int xxx_poll(struct file *filp, poll_table *wait) //函數名初始化給struct file_operations的成員.poll { ? ?unsigned int mask = 0; ? ?/* ? 1. 將所有等待隊列頭加入poll_table表中 ? 2. 判斷是否可讀,如可讀則mask |= POLLIN | POLLRDNORM; ? 3. 判斷是否可寫,如可寫則mask |= POLLOUT | POLLWRNORM; ? ?*/ ? ? ? ?return mask; }
二、信號驅動
2.1 應用層:信號注冊+fcntl
signal(SIGIO, input_handler); //注冊信號處理函數 ? fcntl(fd, F_SETOWN, getpid());//將描述符設置給對應進程,好由描述符獲知PID ? oflags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, oflags | FASYNC);//將該設備的IO模式設置成信號驅動模式 ? void input_handler(int signum)//應用自己實現的信號處理函數,在此函數中完成讀寫 { ? ?//讀數據 } ? //應用模板 int main() { int fd = open("/dev/xxxx",O_RDONLY); ? fcntl(fd, F_SETOWN, getpid()); ? oflags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, oflags | FASYNC); ? signal(SIGIO,xxxx_handler); ? //...... } ? ? void xxxx_handle(int signo) {//讀寫數據 ? ? } ? ?
2.2 驅動層:實現fasync函數
/*設備結構中添加如下成員*/ struct fasync_struct *pasync_obj; ? /*應用調用fcntl設置FASYNC時調用該函數產生異步通知結構對象,并將其地址設置到設備結構成員中*/ static int hello_fasync(int fd, struct file *filp, int mode) //函數名初始化給struct file_operations的成員.fasync { struct hello_device *dev = filp->private_data; return fasync_helper(fd, filp, mode, &dev->pasync_obj); } ? /*寫函數中有數據可讀時向應用層發(fā)信號*/ if (dev->pasync_obj) ? ? ? kill_fasync(&dev->pasync_obj, SIGIO, POLL_IN); ? ? ? /*release函數中釋放異步通知結構對象*/ if (dev->pasync_obj) fasync_helper(-1, filp, 0, &dev->pasync_obj); ? int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **pp); /* 功能:產生或釋放異步通知結構對象 參數: 返回值:成功為>=0,失敗負數 */ ? void kill_fasync(struct fasync_struct **, int, int); /* 功能:發(fā)信號 參數: struct fasync_struct ** 指向保存異步通知結構地址的指針 int 信號?SIGIO/SIGKILL/SIGCHLD/SIGCONT/SIGSTOP int 讀寫信息POLLIN、POLLOUT */
到了這里,關于驅動開發(fā)--多路復用-信號的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!