11.01.2015 Views

I/O Multiplexing: select and poll

I/O Multiplexing: select and poll

I/O Multiplexing: select and poll

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

I/O <strong>Multiplexing</strong>: <strong>select</strong> <strong>and</strong> <strong>poll</strong><br />

•Introduction<br />

•I/O models<br />

•<strong>select</strong> function<br />

•Rewrite str_cli function<br />

•Supporting batch input with shutdown function<br />

•Rewrite concurrent TCP echo server with <strong>select</strong><br />

•p<strong>select</strong> function: avoiding signal loss in race condition<br />

•<strong>poll</strong> function: <strong>poll</strong>ing more specific conditions than<br />

<strong>select</strong><br />

•Rewrite concurrent TCP echo server with <strong>poll</strong><br />

1<br />

Some materials in these slides are taken from Prof. Ying-Dar Lin with his permission of usage


Introduction<br />

•I/O multiplexing: to be notified, by kernel, if one or<br />

more I/O conditions are ready.<br />

•Scenarios in networking applications:<br />

–a client h<strong>and</strong>ling multiple descriptors (stdio/socket)<br />

–a client h<strong>and</strong>ling multiple sockets<br />

–a TCP server h<strong>and</strong>ling a listening socket <strong>and</strong> its<br />

connected sockets ( 兩 件 事 都 自 己 來 )<br />

–a server h<strong>and</strong>ling both TCP <strong>and</strong> UDP<br />

–a server h<strong>and</strong>ling multiple services <strong>and</strong> protocols<br />

2


I/O 動 作 如 何 進 行 <br />

• 一 般 process 無 法 直 接 對 I/O 裝 置 下 命 令 , 必 須<br />

透 過 system call 請 求 kernel 幫 忙 進 行 I/O 動 作<br />

•kernel 會 對 每 個 I/O 裝 置 維 護 一 個 buffer<br />

wait<br />

copy<br />

I/O Device Kernel’s buffer Process<br />

• 對 輸 入 而 言 , 等 待 (wait) 資 料 輸 入 至 buffer 需 要<br />

時 間 , 從 buffer 搬 移 (copy) 資 料 也 需 要 時 間 。<br />

• 根 據 等 待 模 式 不 同 ,I/O 動 作 可 分 為 五 種 模 式<br />

3


Five I/O Models<br />

•blocking I/O: blocked all the way<br />

•nonblocking I/O: if no data in buffer, immediate<br />

returns EWOULDBLOCK<br />

•I/O multiplexing (<strong>select</strong> <strong>and</strong> <strong>poll</strong>): blocked<br />

separately in wait <strong>and</strong> copy<br />

•signal driven I/O (SIGIO): nonblocked in wait<br />

but blocked in copy (signaled when I/O can be<br />

initiated)<br />

•asynchronous I/O (aio_): nonblocked all the way<br />

(signaled when I/O is complete)<br />

4


Comparison of Five I/O Models<br />

blocking nonblocking I/O multiplexing signal-driven asynchronous<br />

initiate check check establish initiate<br />

check SIGIO specify<br />

check h<strong>and</strong>ler signal/h<strong>and</strong>ler<br />

check<br />

check<br />

check ready notification<br />

initiate initiate<br />

blocked<br />

blocked<br />

blocked<br />

wait for data copy data<br />

blocked<br />

blocked<br />

complete complete complete complete notification<br />

synchronous I/O<br />

asynchronous I/O<br />

5


I/O <strong>Multiplexing</strong>: 使 用 <strong>select</strong><br />

• 本 章 要 做 的 是 I/O <strong>Multiplexing</strong>( 第 三 種 I/O<br />

model): 使 用 <strong>select</strong> system call<br />

•<strong>select</strong> 要 求 kernel 測 試 某 裝 置 是 否 滿 足<br />

我 們 設 定 的 條 件 。 若 滿 足 則 return, 否 則<br />

等 待 至 此 條 件 滿 足 時 為 止 (<strong>select</strong> 會 被<br />

block)<br />

•<strong>select</strong> 呼 叫 可 以 指 定 等 待 的 時 間 上 限<br />

•<strong>select</strong> 呼 叫 可 以 指 定 測 試 多 個 I/O 裝 置<br />

6


對 輸 入 裝 置 使 用 <strong>select</strong><br />

• 對 輸 入 裝 置 而 言 , 可 以 使 用 <strong>select</strong> 要 求<br />

kernel 測 試 某 裝 置 是 否 ready for reading<br />

• 當 此 裝 置 的 buffer 中 已 有 相 當 數 量 ( 可 設<br />

定 ) 的 輸 入 資 料 時 , 此 裝 置 即 是 ready for<br />

reading<br />

•<strong>select</strong> return 後 , 由 我 們 的 程 式 自 行 呼<br />

叫 其 它 的 system call 將 buffer 中 的 資 料 搬<br />

回 來 。<br />

7


對 輸 出 裝 置 使 用 <strong>select</strong><br />

• 對 輸 出 裝 置 而 言 , 可 以 使 用 <strong>select</strong> 要 求<br />

kernel 測 試 某 裝 置 是 否 ready for writing<br />

• 當 此 裝 置 的 buffer 中 已 有 相 當 空 間 ( 可 設<br />

定 ) 可 放 置 輸 出 資 料 時 , 此 裝 置 即 是 ready<br />

for writing<br />

•<strong>select</strong> return 後 , 由 我 們 的 程 式 自 行 呼<br />

叫 其 它 的 system call 將 欲 輸 出 資 料 搬 入<br />

buffer 中 。<br />

8


<strong>select</strong> Function<br />

#include <br />

#include <br />

int <strong>select</strong> (int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,<br />

const struct timeval *timeout);<br />

returns: positive count of ready descriptors, 0 on timeout, -1 on error<br />

struct timeval { (null: wait forever; 0: do not wait)<br />

long tv_sec; /*second */<br />

long tv_usec; /* microsecond */<br />

};<br />

readset, writeset, <strong>and</strong> exceptset specify the descriptors that<br />

we want the kernel to test for reading, writing, <strong>and</strong><br />

exception conditions, respectively.<br />

maxfdp1 is the maximum descriptor to be tested plus one.<br />

9


<strong>select</strong> Fucntion<br />

OR<br />

•We can call <strong>select</strong> <strong>and</strong> tell the kernel to<br />

return only when<br />

–any descriptor in {1, 4, 5} is ready for<br />

reading (buffer 中 已 有 資 料 )<br />

–any descriptor in {2, 7} is ready for writing<br />

–any descriptor in {1, 4} has an exception<br />

condition pending<br />

–after 10.2 seconds have elapsed<br />

Buffer<br />

中 已 有<br />

空 間<br />

裝 置 有 例<br />

外 狀 況<br />

10


Specifying Descriptor Values<br />

•We need to declare variables of data type fd_set <strong>and</strong><br />

use macros to manipulate these variables.<br />

fd_set --- implementation dependent<br />

four macros: void FD_ZERO(fd_set *fdset);<br />

void FD_SET(int fd, fd_set *fdset);<br />

void FD_CLR(int fd, fd_set *fdset);<br />

int FD_ISSET(int fd, fd_set *fdset);<br />

fd_set rset;<br />

FD_ZERO(&rset);<br />

FD_SET(1, &rset);<br />

FD_SET(4, &rset);<br />

FD_SET(5, &rset);<br />

maxfdp1 = 6<br />

Turn on bits for<br />

descriptors 1, 4, <strong>and</strong> 5<br />

11


Socket Ready Conditions for <strong>select</strong><br />

Condition readable writeable Exception<br />

enough data to read x<br />

read-half closed x<br />

new connection ready x<br />

writing space available<br />

x<br />

write-half closed<br />

x<br />

pending error x x<br />

TCP out-of-b<strong>and</strong> data<br />

x<br />

Low-water mark (enough data/space to read/write in<br />

socket receive/send buffer): default is 1/2048, may be set by<br />

SO_RCVLOWAT/SO_SNDLOWAT socket option<br />

Maximum number of descriptors for <strong>select</strong><br />

Redefine FD_SETSIZE <strong>and</strong> recompile kernel<br />

12


Low-Water Mark ( 低 水 位 )<br />

• 對 socket receive buffer 而 言<br />

– 如 收 到 data 量 不 足 low-water mark, socket is<br />

not ready for reading<br />

–Default = 1 byte<br />

• 對 socket send buffer 而 言<br />

– 如 可 用 空 間 (available space) 不 足 low-water<br />

mark, socket is not ready for writing<br />

–Default = 2048 byte<br />

13


用 <strong>select</strong> 改 寫 str_cli<br />

• 上 一 章 的 str_cli 用 兩 個 system calls 分 別<br />

取 得 input<br />

–fgets 用 於 stdin 的 user input<br />

–readline 用 於 socket input<br />

均 為 Blocking I/O<br />

• 這 樣 沒 有 辦 法 同 時 等 待 兩 個 inputs。 因 此<br />

當 client 等 在 fgets 時 , 無 法 同 時 取 得<br />

socket input 進 來 的 資 料<br />

• 本 章 改 用 <strong>select</strong> 來 同 時 等 待 兩 個 inputs<br />

14


Ch. 5<br />

兩 個 版 本 的 比 較<br />

Ch. 6<br />

return<br />

呼 叫 fgets 從 stdin<br />

讀 取 使 用 者 的 輸 入<br />

yes<br />

EOF<br />

呼 叫 writen 將 輸 入<br />

資 料 輸 出 至 socket<br />

呼 叫 readline 從<br />

socket 讀 取 資 料<br />

no<br />

呼 叫 fputs 將 讀 取 的<br />

資 料 輸 出 至 stdout<br />

socket is<br />

readable<br />

呼 叫 readline 從<br />

socket 讀 取 資 料<br />

呼 叫 fputs 將 讀 取 的<br />

資 料 輸 出 至 stdout<br />

呼 叫 <strong>select</strong> 同 時 等<br />

待 socket 及 stdin<br />

return<br />

stdin is<br />

readable<br />

呼 叫 fgets 從 stdin<br />

讀 取 使 用 者 的 輸 入<br />

yes<br />

EOF<br />

呼 叫 writen 將 輸 入<br />

資 料 輸 出 至 socket<br />

15<br />

no


Rewrite str_cli Function with <strong>select</strong><br />

data or EOF<br />

TCP<br />

stdin<br />

error<br />

client<br />

socket<br />

EOF<br />

<strong>select</strong> for readability<br />

on either stdin or socket<br />

用 <strong>select</strong> 同 時 等 待 stdio 及<br />

socket input. 當 任 一 裝 置<br />

ready for reading 時 <strong>select</strong><br />

會 return<br />

當 <strong>select</strong> return 時 怎 知 道 是 哪<br />

些 裝 置 ready for reading 呢 <br />

RST data FIN<br />

用 FD_ISSET 測 試 傳 回 值<br />

readset, writeset, 和 exceptset 是 雙 向 變 數<br />

可 得 知 那 些 裝 置 是 ready<br />

(value-result argemnets)<br />

16


Rewrite str_cli Function with <strong>select</strong><br />

#include<br />

"unp.h"<br />

<strong>select</strong>/strcli<strong>select</strong>01.c<br />

void<br />

str_cli(FILE *fp, int sockfd)<br />

{<br />

int maxfdp1;<br />

fd_set rset;<br />

char sendline[MAXLINE], recvline[MAXLINE];<br />

FD_ZERO(&rset);<br />

for ( ; ; ) {<br />

放 在<br />

loop 內<br />

Why<br />

傳 回 檔 案 指 標 fp<br />

的 descriptor no<br />

FD_SET(fileno(fp), &rset);<br />

FD_SET(sockfd, &rset);<br />

maxfdp1 = max(fileno(fp), sockfd) + 1;<br />

Select(maxfdp1, &rset, NULL, NULL, NULL);<br />

只 測 試 read<br />

17


}<br />

}<br />

if (FD_ISSET(sockfd, &rset)) { /* socket is readable */<br />

}<br />

if (Readline(sockfd, recvline, MAXLINE) == 0)<br />

err_quit("str_cli: server terminated prematurely");<br />

Fputs(recvline, stdout);<br />

if (FD_ISSET(fileno(fp), &rset)) { /* input is readable */<br />

}<br />

用 FD_ISSET 測 試<br />

裝 置 是 否 ready<br />

用 FD_ISSET 測 試<br />

裝 置 是 否 ready<br />

if (Fgets(sendline, MAXLINE, fp) == NULL)<br />

return; /* all done */<br />

Writen(sockfd, sendline, strlen(sendline));<br />

<strong>select</strong>/strcli<strong>select</strong>01.c<br />

User 按 了<br />

Ctrl+D<br />

18


使 用 <strong>select</strong> 函 數 常 犯 的 兩 個 錯 誤<br />

‧ 忘 記 maxfdp1 是 descriptor 的 最 大 值 加 1<br />

‧ 忘 記 readset, writeset, 和 exceptset 是 雙 向<br />

變 數<br />

–<strong>select</strong> return 時 會 改 變 它 們 的 值<br />

– 因 此 再 次 呼 叫 <strong>select</strong> 時 別 忘 了 要 重 新 設 定<br />

這 些 變 數 的 內 容<br />

19


Redirecting Input on UNIX<br />

Systems<br />

•St<strong>and</strong>ard I/O - keyboard <strong>and</strong> screen<br />

•Input Redirection symbol (


Stop-<strong>and</strong>-Wait (Interactive) Mode<br />

• 原 來 版 本 的 str_cli 強 制 輪 流 處 理 stdin 輸 入<br />

與 socket 輸 入<br />

client<br />

keyin<br />

keyin<br />

socket<br />

in<br />

提 早 輸 入 並 不<br />

會 提 早 處 理<br />

socket<br />

in<br />

keyin<br />

不 管 多 晚 輸<br />

入 都 會 等 待<br />

socket<br />

in<br />

server<br />

time<br />

21


Batch Mode<br />

• 在 新 版 str_cli 使 用 input redirection<br />

client<br />

不 需<br />

等 待<br />

server<br />

tcpcli04 206.62.226.35 < file1.txt<br />

lines<br />

…<br />

EOF<br />

…<br />

…<br />

time<br />

檔 案 最 後 的 EOF<br />

會 使 fgets 傳 回<br />

NULL,str_cli<br />

會 因 此 return<br />

main 會 close 掉<br />

TCP connection,<br />

導 致 server 尚 未 回<br />

傳 的 資 料 遺 失<br />

將 stdin 重 導<br />

至 file1.txt<br />

就 算 不<br />

用 輸 入<br />

重 導 向<br />

也 有 此<br />

問 題 ,<br />

只 是 較<br />

不 嚴 重<br />

22


Solution: UseShutdown Instead<br />

ofClose<br />

•In str_cli, close write-half of TCP<br />

connection, by shutdown, while leaving<br />

read-half open.<br />

先 關 client 至 server 方 向 的 connection<br />

server 至 client 方 向 的 connection 暫 時 不 要 關<br />

等 到 server 的 資 料 全 部 送 回 來<br />

後 再 關 掉 server 至 client 方 向 的<br />

connection<br />

23


Function shutdown<br />

#include <br />

int shutdown(int sockfd, int howto);<br />

howto: SHUT_RD, SHUT_WR, SHUT_RDWR<br />

returns: 0 if OK, -1 on error<br />

•initiates TCP normal termination<br />

regardless of descriptor’s reference<br />

count<br />

•<strong>select</strong>ively closes one direction of the<br />

connection (SHUT_RD or SHUT_WR)<br />

24


Rewrite str_cli with <strong>select</strong> <strong>and</strong> shutdown<br />

#include<br />

"unp.h"<br />

<strong>select</strong>/strcli<strong>select</strong>02.c<br />

void<br />

str_cli(FILE *fp, int sockfd)<br />

{<br />

int<br />

fd_set rset;<br />

char<br />

maxfdp1, stdineof;<br />

判 斷 client 至 server 方 向 連<br />

結 是 否 已 斷 的 旗 標 變 數<br />

sendline[MAXLINE], recvline[MAXLINE];<br />

stdineof = 0;<br />

FD_ZERO(&rset);<br />

當 client 至 server 方 向 連 結<br />

for ( ; ; ) {<br />

未 斷 時 才 要 test stdin<br />

if (stdineof == 0)<br />

FD_SET(fileno(fp), &rset);<br />

FD_SET(sockfd, &rset);<br />

maxfdp1 = max(fileno(fp), sockfd) + 1;<br />

Select(maxfdp1, &rset, NULL, NULL, NULL);<br />

25


}<br />

client 至 server<br />

方 向 連 結 已 斷<br />

}<br />

if (FD_ISSET(sockfd, &rset)) { /* socket is readable */<br />

if (Readline(sockfd, recvline, MAXLINE) == 0) {<br />

if (stdineof == 1)<br />

return; /* normal termination */<br />

else<br />

err_quit("str_cli: server terminated prematurely");<br />

}<br />

Fputs(recvline, stdout);<br />

}<br />

if (FD_ISSET(fileno(fp), &rset)) { /* input is readable */<br />

if (Fgets(sendline, MAXLINE, fp) == NULL) {<br />

stdineof = 1;<br />

Shutdown(sockfd, SHUT_WR); /* send FIN */<br />

}<br />

FD_CLR(fileno(fp), &rset);<br />

continue;<br />

}<br />

Writen(sockfd, sendline, strlen(sendline));<br />

<strong>select</strong>/strcli<strong>select</strong>02.c<br />

只 斷 client 至<br />

server 方 向 連 結<br />

26


Concurrent TCP Echo Server with <strong>select</strong><br />

•A single server process using <strong>select</strong> to h<strong>and</strong>le<br />

any number of clients ( 不 fork child process)<br />

•Need to keep track of the clients by client[ ]<br />

(client descriptor array) <strong>and</strong> rset (read descriptor set)<br />

client[ ]<br />

[0] -1<br />

[1] 5<br />

[2] -1<br />

[FD_SETSIZE-1] -1<br />

maxi<br />

not<br />

used<br />

rset<br />

儲 存 已 open socket 的 descriptor<br />

stdin/stdout/stderr<br />

listening socket<br />

terminated client<br />

fd0 fd1 fd2 fd3 fd4 fd5<br />

0 0 0 1 0 1<br />

maxfd +1 = 6<br />

用 於 呼 叫 <strong>select</strong><br />

existing client<br />

27


Rewrite Concurrent TCP Echo Server with <strong>select</strong><br />

Initialization<br />

#include "unp.h"<br />

int main(int argc, char **argv)<br />

tcpcliserv/tcpserv<strong>select</strong>01.c<br />

{<br />

int<br />

i, maxi, maxfd, listenfd, connfd, sockfd;<br />

int<br />

nready, client[FD_SETSIZE];<br />

ssize_t n;<br />

fd_set<br />

rset, allset;<br />

char<br />

line[MAXLINE];<br />

socklen_t<br />

clilen;<br />

struct sockaddr_in cliaddr, servaddr;<br />

listenfd = Socket(AF_INET, SOCK_STREAM, 0);<br />

bzero(&servaddr, sizeof(servaddr));<br />

servaddr.sin_family = AF_INET;<br />

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);<br />

servaddr.sin_port = htons(SERV_PORT);<br />

28


Initialization (cont.)<br />

tcpcliserv/tcpserv<strong>select</strong>01.c<br />

Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));<br />

Listen(listenfd, LISTENQ);<br />

maxfd = listenfd;/* initialize */<br />

maxi = -1;/* index into client[] array */<br />

for (i = 0; i < FD_SETSIZE; i++)<br />

client[i] = -1;/* -1 indicates available entry */<br />

FD_ZERO(&allset);<br />

FD_SET(listenfd, &allset);<br />

測 試 listening socket 是<br />

否 ready for reading<br />

29


Loop<br />

for ( ; ; ) {<br />

<strong>select</strong> 的 傳 回 值 是 ready 的 裝 置 數 目<br />

rset = allset; /* structure assignment */<br />

nready = Select(maxfd+1, &rset, NULL, NULL, NULL);<br />

if (FD_ISSET(listenfd, &rset)) { /* new client connection */<br />

新<br />

連<br />

結<br />

放 入 第 一 個<br />

找 到 的 空 格<br />

如 果 沒 有 其<br />

它 ready 的<br />

descriptor<br />

}<br />

clilen = sizeof(cliaddr);<br />

connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);<br />

for (i = 0; i < FD_SETSIZE; i++)<br />

if (client[i] < 0) {<br />

client[i] = connfd; /* save descriptor */<br />

break;<br />

}<br />

if (i == FD_SETSIZE)<br />

err_quit("too many clients");<br />

tcpcliserv/tcpserv<strong>select</strong>01.c<br />

加 進 <strong>select</strong> 要 test<br />

的 descriptor set<br />

不 會 block<br />

FD_SET(connfd, &allset); /* add new descriptor to set */<br />

if (connfd > maxfd)<br />

maxfd = connfd; /* for <strong>select</strong> */<br />

if (i > maxi)<br />

maxi = i; /* max index in client[] array */<br />

if (--nready


Loop (cont.)<br />

tcpcliserv/tcpserv<strong>select</strong>01.c<br />

}<br />

}<br />

}<br />

for (i = 0; i


前 頁 程 式 的 潛 在 問 題<br />

•<strong>select</strong> 在 socket 的 input buffer 有 資<br />

料 可 讀 時 即 會 return 此 socket 已 ready<br />

for reading<br />

•Readline 被 設 計 成 要 讀 到 以 換 行 字 元<br />

結 束 的 一 列 文 字 ( 或 是 socket 被 結 束 掉 ) 才<br />

會 return<br />

• 有 心 人 士 可 能 利 用 這 項 弱 點 進 行 攻 擊<br />

32


Denial of Service Attacks<br />

• 問 題 出 在 Server 程 式 可 能 block 在 Readline 中<br />

•Attack scenario:<br />

–a malicious client sends 1 byte of data (other<br />

than a newline) <strong>and</strong> sleep<br />

–server hangs until the malicious client either<br />

sends a newline or terminates<br />

–(server 要 讀 到 newline, Readline 才 能 return)<br />

33


Solutions to the Attack<br />

•When a server is h<strong>and</strong>ling multiple<br />

clients the server can never block in a<br />

function call related to a single client<br />

•Possible solutions:<br />

–Use nonblocking I/O (Ch. 15)<br />

–separate thread/process for each client<br />

–timeout on I/O operations (Sec. 13.2)<br />

34


<strong>poll</strong> Function: <strong>poll</strong>ing more specific<br />

conditions than <strong>select</strong><br />

結 構 陣 列 儲 存 位 址<br />

陣 列 中 的 資 料 數 目<br />

微 秒<br />

#include <br />

int <strong>poll</strong> (struct <strong>poll</strong>fd *fdarray, unsigned long ndfs, int timeout);<br />

returns: count of ready descriptors, 0 on timeout, -1 on error<br />

每 個 descriptor 要 test 的 條 件 是 用 <strong>poll</strong>fd 結 構 來 表 示<br />

這 些 <strong>poll</strong>fd 結 構 集 合 成 一 個 陣 列<br />

struct <strong>poll</strong>fd { 要 測 的 file descriptor number; -1 表 此 結 構 無 效<br />

int fd; /* a descriptor to <strong>poll</strong> */<br />

short events; /* events of interested fd, value argument */<br />

short revents; /* events that occurred on fd, result argument */<br />

};<br />

要 測 試 的 條 件<br />

真 正 測 到 的 事 件<br />

35


<strong>poll</strong> 函 數 中 的 事 件 設 定<br />

•events 與 revents 由 下 列 bit flag 組 成<br />

Constant events revents Description<br />

POLLIN x x normal or priority b<strong>and</strong> to read<br />

POLLRDNORM x x normal data to read<br />

POLLRDBAND x x priority b<strong>and</strong> data to read<br />

POLLPRI x x high-priority data to read<br />

POLLOUT x x normal data can be written<br />

POLLWRNORM x x normal data can be written<br />

POLLWRBAND x x priority b<strong>and</strong> data can be written<br />

POLLERR x an error has occurred<br />

POLLHUP x hangup has occurred<br />

POLLNVAL x descriptor is not an open file<br />

36


設 定 events 與 測 試 revents<br />

‧ 設 定 events: 使 用 bitwise OR<br />

struct <strong>poll</strong>fd client[OPEN_MAX];<br />

…<br />

client[0].events = POLLRDNORM | POLLRDBAND;<br />

‧ 測 試 revents: 使 用 bitwise AND<br />

struct <strong>poll</strong>fd client[OPEN_MAX];<br />

…<br />

if (client[0].revents & POLLRDNORM)<br />

…<br />

37


Three Classes of Data Identified<br />

by <strong>poll</strong><br />

•normal, priority b<strong>and</strong>, <strong>and</strong> high priority<br />

normal priority b<strong>and</strong> high priority<br />

All regular TCP data x<br />

All UDP data<br />

x<br />

TCP’s out-of-b<strong>and</strong> data<br />

x<br />

Half-closed TCP x<br />

Error for TCP<br />

x (or POLLERR)<br />

New connection x x<br />

38


Concurrent TCP Echo Server with <strong>poll</strong><br />

•When using <strong>select</strong>, the server maintains array client[ ]<br />

<strong>and</strong> descriptor set rset. When using <strong>poll</strong>, the server<br />

maintains array client of <strong>poll</strong>fd structure.<br />

•Program flow:<br />

–allocate array of <strong>poll</strong>fd structures<br />

–initialize (listening socket: first entry in client)<br />

(set POLLRDNORM in events)<br />

–call <strong>poll</strong>; check for new connection<br />

(check, in revents, <strong>and</strong> set, in events, POLLRDNORM)<br />

–check for data on an existing connection<br />

(check POLLRDNORM or POLLERR in revents)<br />

39


Rewrite Concurrent TCP Echo Server<br />

with <strong>poll</strong><br />

Initialization<br />

tcpcliserv/tcpserv<strong>poll</strong>01.c<br />

#include "unp.h"<br />

#include /* for OPEN_MAX */<br />

int main(int argc, char **argv)<br />

{<br />

int<br />

i, maxi, listenfd, connfd, sockfd;<br />

int<br />

nready;<br />

ssize_t n;<br />

char<br />

line[MAXLINE];<br />

結 構 陣 列<br />

socklen_t clilen;<br />

struct <strong>poll</strong>fd client[OPEN_MAX];<br />

struct sockaddr_in cliaddr, servaddr;<br />

listenfd = Socket(AF_INET, SOCK_STREAM, 0);<br />

bzero(&servaddr, sizeof(servaddr));<br />

servaddr.sin_family = AF_INET;<br />

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);<br />

servaddr.sin_port = htons(SERV_PORT);<br />

40


tcpcliserv/tcpserv<strong>poll</strong>01.c<br />

Initialization (cont.)<br />

Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));<br />

Listen(listenfd, LISTENQ);<br />

client[0].fd = listenfd;<br />

client[0].events = POLLRDNORM;<br />

for (i = 1; i < OPEN_MAX; i++)<br />

第 0 個 元 素 放<br />

listening socket<br />

client[i].fd = -1; /* -1 indicates available entry */<br />

maxi = 0; /* max index into client[] array */<br />

41


Loop<br />

for ( ; ; ) {<br />

nready = Poll(client, maxi+1, INFTIM);<br />

放 入 第 一 個<br />

找 到 的 空 格<br />

if (client[0].revents & POLLRDNORM) { /* new client connection */<br />

clilen = sizeof(cliaddr);<br />

connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);<br />

for (i = 1; i < OPEN_MAX; i++)<br />

if (client[i].fd < 0) {<br />

client[i].fd = connfd; /* save descriptor */<br />

break;<br />

}<br />

}<br />

bitwise AND<br />

tcpcliserv/tcpserv<strong>poll</strong>01.c<br />

wait forever<br />

不 會 block<br />

if (i == OPEN_MAX)<br />

for data socket<br />

err_quit("too many clients");<br />

client[i].events = POLLRDNORM;<br />

if (i > maxi)<br />

maxi = i; /* max index in client[] array */<br />

if (--nready


tcpcliserv/tcpserv<strong>poll</strong>01.c<br />

for (i = 1; i


Conclusion<br />

• 本 章 介 紹 如 何 用 I/O <strong>Multiplexing</strong> 來 取 代<br />

blocking I/O, 以 同 時 等 待 兩 個 以 上 的 I/O<br />

裝 置<br />

•I/O <strong>Multiplexing</strong> 可 用 <strong>select</strong> 或 <strong>poll</strong> 來<br />

達 成<br />

• 為 何 不 使 用 nonblocking I/O 呢 就 算<br />

Buffer 中 沒 資 料 也 可 立 即 return 耶 …<br />

return 後 要 做 啥 <br />

44

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!