POSIX
Input / Output multiplexing
Sök…
Introduktion
IO kan vara blockerande / icke-blockerande och synkront / asynkront. POSIX API tillhandahåller synkron blockerande API (t.ex. klassisk läs, skriv, skicka, recv samtal), synkron icke-blockerande API (samma funktioner, filbeskrivningar öppnade med O_NONBLOCK
flagga och IO-multiplexande samtal) och asynkoniska API (funktioner som börjar med aio_
).
Synkron API används vanligtvis med "en tråd / process per fd" -stil. Detta är fruktansvärt för resurser. Icke-blockerande API gör det möjligt att arbeta med en uppsättning fds i en tråd.
Opinionsundersökning
I det här exemplet skapar vi ett par anslutna uttag och skickar 4 strängar från en till en annan och skriver ut mottagna strängar till konsolen. Observera att antalet gånger vi kommer att ringa skicka kanske inte är lika med antalet gånger vi kallar recv
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#define BUFSIZE 512
int main()
{
#define CKERR(msg) {if(ret < 0) { perror(msg); \
close(sockp[0]); close(sockp[1]); exit(EXIT_FAILURE); } }
const char* strs_to_write[] = {"hello ", "from ", "other ", "side "};
int sockp[2] = {-1, -1};
ssize_t ret = socketpair (AF_UNIX, SOCK_STREAM, 0, sockp);
CKERR("Socket pair creation error")
struct pollfd pfds[2];
for(int i=0; i<2; ++i) {
pfds[i] = (struct pollfd){sockp[i], POLLIN|POLLOUT, 0};
fcntl(sockp[i], F_SETFL|O_NONBLOCK); // nonblocking fds are
// literally mandatory for IO multiplexing; non-portable
}
char buf[BUFSIZE];
size_t snt = 0, msgs = sizeof(strs_to_write)/sizeof(char*);
while(1) {
int ret = poll(pfds,
2 /*length of pollfd array*/,
5 /*milliseconds to wait*/);
CKERR("Poll error")
if (pfds[0].revents & POLLOUT && snt < msgs) {
// Checking POLLOUT before writing to ensure there is space
// available in socket's kernel buffer to write, otherwise we
// may face EWOULDBLOCK / EAGAIN error
ssize_t ret = send(sockp[0], strs_to_write[snt], strlen(strs_to_write[snt]), 0);
if(++snt >= msgs)
close(sockp[0]);
CKERR("send error")
if (ret == 0) {
puts("Connection closed");
break;
}
if (ret > 0) {
// assuming that all bytes were written
// if ret != %sent bytes number%, send other bytes later
}
}
if (pfds[1].revents & POLLIN) {
// There is something to read
ssize_t ret = recv(sockp[1], buf, BUFSIZE, 0);
CKERR("receive error")
if (ret == 0) {
puts("Connection closed");
break;
}
if (ret > 0) {
printf("received str: %.*s\n", (int)ret, buf);
}
}
}
close(sockp[1]);
return EXIT_SUCCESS;
}
Välj
Select är ett annat sätt att göra I / O-multiplexering. En av dess fördelar är ett existens i winsock API. I Linux ändrar du dessutom timeout för att återspegla den tid som inte sov; de flesta andra implementeringar gör inte detta. (POSIX.1 tillåter antingen beteende.)
Både undersökning och utvalda har alternativ för att välja och välja alternativ som gör det möjligt att hantera inkommande signaler under väntan på evenemang. Och båda blir långsamma med enorma mängder filbeskrivningar (hundra och mer), så det vore klokt att välja plattformsspecifikt samtal, t.ex. epoll
på Linux och kqueue
på FreeBSD. Eller byt till asynkron API (POSIX aio
t.ex. eller något specifikt som IO Completion Ports).
Välj samtal har följande prototyp:
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
fd_set
är en bitmaskerad matris med filbeskrivningar,
nfds
är det maximala antalet av alla filbeskrivningar i set + 1.
Arbetsstycke med select:
fd_set active_fd_set, read_fd_set;
FD_ZERO (&active_fd_set); // set fd_set to zeros
FD_SET (sock, &active_fd_set); // add sock to the set
// # define FD_SETSIZE sock + 1
while (1) {
/* Block until input arrives on one or more active sockets. */
read_fd_set = active_fd_set; // read_fd_set gets overriden each time
if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0) {
// handle error
}
// Service all file descriptors with input pending.
for (i = 0; i < FD_SETSIZE; ++i) {
if (FD_ISSET (i, &read_fd_set)) {
// there is data for i
}
}
Observera att på de flesta POSIX-implemetationer blockeras filbeskrivningar associerade med filer på disken. Så att skriva till fil, även om den här filen sattes i writefds
, skulle blockera tills alla byte inte kommer att dumpas till disken