日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux网络编程 之 IO多路复用select(八)

發(fā)布時間:2024/2/28 linux 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux网络编程 之 IO多路复用select(八) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1. IO多路復(fù)用的概念

I/O多路復(fù)用是通過一種機(jī)制,可以監(jiān)視多個描述符,一旦某個描述符就緒(一般是讀就緒或者寫就緒),能夠通知程序進(jìn)行相應(yīng)的讀寫操作。

2. select接口

#include <sys/select.h>int select( int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
  • readfds,讀流集合
  • writefds,寫流集合
  • exceptfds,異常流集合
  • nfds,上面三種事件的最大的文件描述符+1
  • timeout 可等待的時間
struct timeval{long tv_sec;//secondlong tv_usec;//minisecond }

timeout有三種取值:

  • NULL,select一直阻塞,知道readfds、writefds、exceptfds集合中至少一個文件描述符可用才喚醒
  • 0,select不阻塞
  • timeout_value,select在timeout_value這個時間段內(nèi)阻塞

返回值

  • 0:超時
  • -1:出錯
  • 大于0:可用的文件描述符數(shù)量

另外,還有一組與fd_set 有關(guān)的操作

  • FD_SET(fd, _fdset),把fd加入_fdset集合中
  • FD_CLR(fd, _fdset),把fd從_fdset集合中清除
  • FD_ISSET(fd, _fdset),判定fd是否在_fdset集合中
  • FD_ZERO(_fdset),清除_fdset有描述符

3. select實(shí)現(xiàn)原理

select的實(shí)現(xiàn)依賴于設(shè)備的驅(qū)動函數(shù)poll,poll的功能是檢查設(shè)備的哪條條流可用(一個設(shè)備一般有三條流,讀流,寫流,設(shè)備發(fā)生異常的異常流),如果其中一條流可用,返回一個mask(表示可用的流),如果不可用,把當(dāng)前進(jìn)程加入設(shè)備的流等待隊(duì)列中,例如讀等待隊(duì)列、寫等待隊(duì)列,并返回資源不可用。

 select正是利用了poll的這個功能,首先讓程序員告知自己關(guān)心哪些io流(用文件描述符表示,也就是上文的readfds、writefds和exceptfds),并讓程序員告知自己這些流的范圍(也就是上文的nfds參數(shù))以及程序的容忍度(timeout參數(shù)),然后select會把她們拷貝到內(nèi)核,在內(nèi)核中逐個調(diào)用流所對應(yīng)的設(shè)備的驅(qū)動poll函數(shù),當(dāng)范圍內(nèi)的所有流也就是描述符都遍歷完之后,他會檢查是否至少有一個流發(fā)生了,如果有,就修改那三個流集合,把她們清空,然后把發(fā)生的流加入到相應(yīng)的集合中,并且select返回。如果沒有,就要取決于timeout,如果timeout等于0,說明不阻塞,讓出cpu,直接返回;如果timeout是NULL,則要一直阻塞,直到某個設(shè)備的某條流可用,就去喚醒阻塞在流上的進(jìn)程,這個時候,調(diào)用select的進(jìn)程重新開始遍歷范圍內(nèi)的所有描述符;如果timeout不是上面兩個值,就是在這個值的時間段阻塞,如果依然沒有某個流可用,那么就返回。

select實(shí)現(xiàn)原理圖

4. select的代碼實(shí)現(xiàn)

int do_select(int n, fd_set_bits *fds, s64 *timeout) {retval = 0; //retval用于保存已經(jīng)準(zhǔn)備好的描述符數(shù),初始為0for (;;) {unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;long __timeout;set_current_state(TASK_INTERRUPTIBLE); //將當(dāng)前進(jìn)程狀態(tài)改為TASK_INTERRUPTIBLE,可中斷inp = fds->in; outp = fds->out; exp = fds->ex;rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;for (i = 0; i < n; ++rinp, ++routp, ++rexp) { //遍歷每個描述符unsigned long in, out, ex, all_bits, bit = 1, mask, j;unsigned long res_in = 0, res_out = 0, res_ex = 0;const struct file_operations *f_op = NULL;struct file *file = NULL;in = *inp++; out = *outp++; ex = *exp++;all_bits = in | out | ex;if (all_bits == 0) {i += __NFDBITS; //all_bits的類型是unsigned long int ,大小為4個字節(jié)32位,all_bits=0,說明連續(xù)32個描述符(流)不在readdfs、writedfs、execptdfs集合中,所以i+=32,而__NFDBITS=32。continue;}for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) { //遍歷每個長字里的每個位int fput_needed;if (i >= n)break;if (!(bit & all_bits))continue;file = fget_light(i, &fput_needed);if (file) {f_op = file->f_op;MARK(fs_select, "%d %lld", i, (long long)*timeout);mask = DEFAULT_POLLMASK;if (f_op && f_op->poll)mask = (*f_op->poll)(file, retval ? NULL : wait);//調(diào)用設(shè)備的驅(qū)動poll函數(shù)fput_light(file, fput_needed);if ((mask & POLLIN_SET) && (in & bit)) {res_in |= bit; //如果是這個描述符可讀, 將這個位置位retval++; //返回描述符個數(shù)加1}if ((mask & POLLOUT_SET) && (out & bit)) {res_out |= bit;retval++;}if ((mask & POLLEX_SET) && (ex & bit)) {res_ex |= bit;retval++;}}}if (res_in)*rinp = res_in;if (res_out)*routp = res_out;if (res_ex)*rexp = res_ex;}wait = NULL;if (retval || !*timeout || signal_pending(current))//如果retval!=0,也就是有readdfs、writedfs、execptdfs至少有一個發(fā)生,跳出循環(huán)break;/*以下處理timeout參數(shù)*/__timeout = schedule_timeout(__timeout);if (*timeout >= 0)*timeout += __timeout;}__set_current_state(TASK_RUNNING);return retval; }

總結(jié)

以上是生活随笔為你收集整理的Linux网络编程 之 IO多路复用select(八)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。