epollの使い方は? C言語の簡単な例



How Use Epoll Simple Example C Language



* epollの使い方は? C翻訳の完全な例

通常、ネットワークサーバーの実装では、各接続は個別のスレッドまたはプロセス権を使用します。高性能アプリケーションの場合、非常に多くのクライアント要求を処理する必要があるため、このアプローチはうまく機能しません。コンテキストの切り替えに必要な時間などのリソースの使用が、複数のクライアントの処理時間に影響を与えるためです。別の代替アプローチは、ノンブロッキングI / Oで別のスレッドを使用することです。これにより、ソケットからより多くのデータを読み書きできるようになり、通知の多くの準備が整った方法で通知されます。

この記事では、Linux対応の最良の通知方法であるLinuxepoll方法について説明します。完全に実装された単純なC言語プログラムを使用してTCPサーバーを作成します。 LinuxでのCプログラミングの経験がすでにあると仮定すると、プログラムのコンパイルと実行を知っており、マンページで使用されるC関数を確認できるようになります。

Linux 2.6のepollが導入されましたが、他のUnixライクなシステムには適用されません。同様のポーリングと選択機能および機能を提供します。
+ Select monitorは、一度に最大数のファイル記述子FD_SETSIZEを達成できます。これは通常、libcコンパイル時に指定された比較的少ない数が原因です。
+同時にポーリングできるファイル記述子の数は、他の要因の中でも特に制限されていません。すべての記述子をスキャンして、通知の準備ができているかどうかを確認するために毎回必要な数が増え、その時間計算量はO(n )、 遅い。

上記の制限なしで、線形スキャンを実行せずにepollします。したがって、効率が高く、多数のイベントを処理できます。

作成するepollインスタンスepoll_createまたはepoll_create1関数を返すことによるepollの例。 epoll_ctlは、例でepollを追加したり、監視されているファイル記述子を削除したりするために使用されます。 epoll_wait記述子は、イベントがリッスンするのを待機するために使用され、イベントの到着をブロックします。マンページの詳細を表示できます。

記述子がepollインスタンスに追加されると、レベルトリガー(トリガーレベル)とエッジトリガー(エッジトリガー)の2つのモードが追加されます。レベルトリガーモードを使用していて、データを読み取る準備ができている場合、epoll_wait常に準備ができているイベントを返します。データの読み取りが終了せず、epollインスタンスの呼び出しが、epoll_waitこの記述子を再度リッスンする場合、データはまだ読み取り可能であるため、再び返されます。エッジトリガーモードの場合、通知の準備ができているだけです。データZhaiをまだ読み取っていない場合は、readyイベントのためにepoll_wait epollインスタンスを再度呼び出して送信されたため、ブロックされます。

図epoll_ctlは次の構造epollイベントに送信されます。監視されている記述子ごとに、ユーザーのデータとして整数またはポインターに関連付けることができます。

typedef union epoll_data
{
void *ptr
int fd
__uint32_t u32
__uint64_t u64
} epoll_data_t

struct epoll_event
{
__uint32_t events /* Epoll events */
epoll_data_t data /* User data variable */
}


すぐにコードを書く練習をしてください。小さなTCPサーバーを実現します。サーバーが受信する出力データはすべて標準出力にソケットされます。まず、TCPソケットを作成してバインドするcreate_and_bind()関数を記述します。

static int
create_and_bind (char *port)
{
struct addrinfo hints
struct addrinfo *result, *rp
int s, sfd

memset (&hints, 0, sizeof (struct addrinfo))
hints.ai_family = AF_UNSPEC /* Return IPv4 and IPv6 choices */
hints.ai_socktype = SOCK_STREAM /* We want a TCP socket */
hints.ai_flags = AI_PASSIVE /* All interfaces */

s = getaddrinfo (NULL, port, &hints, &result)
if (s != 0)
{
fprintf (stderr, 'getaddrinfo: %s ', gai_strerror (s))
return -1
}

for (rp = result rp != NULL rp = rp->ai_next)
{
sfd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol)
if (sfd == -1)
continue

s = bind (sfd, rp->ai_addr, rp->ai_addrlen)
if (s == 0)
{
/* We managed to bind successfully! */
break
}

close (sfd)
}

if (rp == NULL)
{
fprintf (stderr, 'Could not bind ')
return -1
}

freeaddrinfo (result)

return sfd
}


コードセグメントIPv4またはIPv6標準ソケットを取得するための移植可能な方法を含むcreate_and_bind関数。ポートの文字列引数を受け入れ、ポートはargv [1 ] inから渡されます。getaddrinfo関数はグループaddrinfoを結果に返し、ヒントパラメータと互換性があります。 addrinfo構造は次のとおりです。

struct addrinfo
{
int ai_flags
int ai_family
int ai_socktype
int ai_protocol
size_t ai_addrlen
struct sockaddr *ai_addr
char *ai_canonname
struct addrinfo *ai_next
}


これらの構造をトラバースし、ソケットの作成とバインドを同時に実行できるようになるまで、構造の作成に使用します。成功すると、create_and_bind()はソケット記述子を返します。 -1の失敗が返されます。

次に、非ブロッキング関数にセットソケットを書き込むために使用しました。 make_socket_non_blocking()は、フラグO_NONBLOCK着信sfd記述子パラメーターを設定します。

static int
make_socket_non_blocking (int sfd)
{
int flags, s

flags = fcntl (sfd, F_GETFL, 0)
if (flags == -1)
{
perror ('fcntl')
return -1
}

flags |= O_NONBLOCK
s = fcntl (sfd, F_SETFL, flags)
if (s == -1)
{
perror ('fcntl')
return -1
}

return 0
}


イベントループを含む次のmain()関数。コードは次のとおりです。


#define MAXEVENTS 64

int
main (int argc, char *argv[])
{
int sfd, s
int efd
struct epoll_event event
struct epoll_event *events

if (argc != 2)
{
fprintf (stderr, 'Usage: %s [port] ', argv[0])
exit (EXIT_FAILURE)
}

sfd = create_and_bind (argv[1])
if (sfd == -1)
abort ()

s = make_socket_non_blocking (sfd)
if (s == -1)
abort ()

s = listen (sfd, SOMAXCONN)
if (s == -1)
{
perror ('listen')
abort ()
}

efd = epoll_create1 (0)
if (efd == -1)
{
perror ('epoll_create')
abort ()
}

event.data.fd = sfd
event.events = EPOLLIN | EPOLLET
s = epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &event)
if (s == -1)
{
perror ('epoll_ctl')
abort ()
}

/* Buffer where events are returned */
events = calloc (MAXEVENTS, sizeof event)

/* The event loop */
while (1)
{
int n, i

n = epoll_wait (efd, events, MAXEVENTS, -1)
for (i = 0 i {
if ((events[i].events & EPOLLERR) ||
(events[i].events & EPOLLHUP) ||
(!(events[i].events & EPOLLIN)))
{
/* An error has occured on this fd, or the socket is not
ready for reading (why were we notified then?) */
fprintf (stderr, 'epoll error ')
close (events[i].data.fd)
continue
}

else if (sfd == events[i].data.fd)
{
/* We have a notification on the listening socket, which
means one or more incoming connections. */
while (1)
{
struct sockaddr in_addr
socklen_t in_len
int infd
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]

in_len = sizeof in_addr
infd = accept (sfd, &in_addr, &in_len)
if (infd == -1)
{
if ((errno == EAGAIN) ||
(errno == EWOULDBLOCK))
{
/* We have processed all incoming
connections. */
break
}
else
{
perror ('accept')
break
}
}

s = getnameinfo (&in_addr, in_len,
hbuf, sizeof hbuf,
sbuf, sizeof sbuf,
NI_NUMERICHOST | NI_NUMERICSERV)
if (s == 0)
{
printf('Accepted connection on descriptor %d '
'(host=%s, port=%s) ', infd, hbuf, sbuf)
}

/* Make the incoming socket non-blocking and add it to the
list of fds to monitor. */
s = make_socket_non_blocking (infd)
if (s == -1)
abort ()

event.data.fd = infd
event.events = EPOLLIN | EPOLLET
s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &event)
if (s == -1)
{
perror ('epoll_ctl')
abort ()
}
}
continue
}
else
{
/* We have data on the fd waiting to be read. Read and
display it. We must read whatever data is available
completely, as we are running in edge-triggered mode
and won't get a notification again for the same
data. */
int done = 0

while (1)
{
ssize_t count
char buf[512]

count = read (events[i].data.fd, buf, sizeof buf)
if (count == -1)
{
/* If errno == EAGAIN, that means we have read all
data. So go back to the main loop. */
if (errno != EAGAIN)
{
perror ('read')
done = 1
}
break
}
else if (count == 0)
{
/* End of file. The remote has closed the
connection. */
done = 1
break
}

/* Write the buffer to standard output */
s = write (1, buf, count)
if (s == -1)
{
perror ('write')
abort ()
}
}

if (done)
{
printf ('Closed connection on descriptor %d ',
events[i].data.fd)

/* Closing the descriptor will make epoll remove it
from the set of descriptors which are monitored. */
close (events[i].data.fd)
}
}
}
}

free (events)

close (sfd)

return EXIT_SUCCESS
}


主な機能については言うまでもありません。

ダウンロード[url = https://banu.com/blog/2/how-to-use-epoll-a-complete-example-in-c/epoll-example.c] epoll-example.c [/ url]プログラム。