Netzwerk-Programmiermodelle und I/O Optimierungen f£¼r Vortragsfahrplan 1....

download Netzwerk-Programmiermodelle und I/O Optimierungen f£¼r Vortragsfahrplan 1. Programmiermodelle I Asynchrone

of 42

  • date post

    28-Oct-2020
  • Category

    Documents

  • view

    0
  • download

    0

Embed Size (px)

Transcript of Netzwerk-Programmiermodelle und I/O Optimierungen f£¼r Vortragsfahrplan 1....

  • Netzwerk-Programmiermodelle und I/O Optimierungen für Unix

    Betriebssyteme

    Hagen Paul Pfeifer • Florian Westphal hagen@jauu.net fw@strlen.de

    http://www.protocol-laboratories.net

    14. Oktober 2006

    mailto:hagen@jauu.net fw@strlen.de

  • Vortragsfahrplan 1. Programmiermodelle

    I Asynchrone I/O, fork(), threads, select(), poll() /dev/poll, epoll, kqueue, kevent

    2. I/O Optimierungen I Zero-Copy, Read-Write, Mmap, Sendfile, Splice und Tee

    3. Kernelspace Optimierungen I NAPI

    I Dynamic Right Sizing

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 2/42

  • Kapitel 1 Programmiermodelle

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 3/42

  • Server/Client Programmiermodelle I fork(): Ein Prozess pro Client. Vorteile:

    • Einfache Programmierung (z.B.: Fehlerhandling mit _exit())

    • Gesamt-System weniger Fehleranfällig I Design ist von Nachteil, wenn:

    • . . . Prozesse untereinander kommunizieren müssen

    • . . . viele Clients (> 1000) zu erwarten sind I Overhead durch fork(),_exit(),waitpid()

    I Performance sehr vom Scheduler abhängig (Linux 2.4: O(n), 2.6: O(1))

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 4/42

  • Server/Client Programmiermodelle I Ein Thread pro Client. Vorteile:

    • Threaderzeugung meist schneller als fork()(Linux 2.6: ca. 116CPUZyklen)

    • Programmiermodell dem zu fork sehr ähnlich I Nachteile:

    • Bei gemeinsam genutzten Daten ist Synchronisation notwendig

    I Performance von Scheduler und Thread-Modell (m : n, 1 : 1) abhängig

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 5/42

  • Server/Client Programmiermodelle I Multiplexer: Ein Prozess/Thread für mehrere (oder alle)

    Clients. Zwei Möglichkeiten: 1. Level-Triggered: (”Readiness Notification”)

    • fd wird gemeldet, sobald er bereit ist: select, poll, Async I/O, etc.

    2. Edge-Triggered: (”Change Notification”) • fd wird nur gemeldet, wenn eine Zustandsänderung

    eintritt z.B. Linux RT-Signals; Optional: epoll, kqueue, Async I/O

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 6/42

  • Asynchrone I/O I Vorgehen:

    • struct aiocb mit Deskriptor+buffer+Länge

    • aio_read, aio_write etc. starten Schreib/Lesevorgang

    • I/O läuft ”im Hintergrund” ab, Mitteilung wann beendet entweder mittels Signal oder Polling (aio_suspend)

    Nachteil: Auch open() kann blockieren; aio_open nicht existent Alternative: Worker Threads

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 7/42

  • Multiplexing-Mechanismen: select I ”Der Klassiker:” 4.2 BSD (1983)

    int select(int nfds, fd_set *readfds, fd_set *writefds,

    fd_set *exceptfds, struct timeval *timeout);

    • Gewöhnungsbedürftiges API (nfds: Anzahl zu untersuchender Bits)

    • nur Deskriptoren kleiner FD_SETSIZE erlaubt

    • manche Systeme (Linux) erfordern O_NONBLOCK für alle fd

    I Am weitesten Verbreitet (Sogar unter Winsock verfügbar . . . )

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 8/42

  • Multiplexing-Mechanismen: poll I SVR3 (1986)

    struct pollfd {

    int fd;

    short events;

    short revents;

    };

    int poll(struct pollfd p[], nfds_t nfds, int timeout);

    • manche Systeme (Linux) erfordern O_NONBLOCK für alle fd

    • Array bei vielen Veränderungen der Größe ungünstig

    • pollfd[] muss bei jedem Syscall zweifach kopiert werden

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 9/42

  • Probleme I Probleme:

    • ”Doppelte Arbeit”: Applikation und Kernel verwalten dieselben Informationen

    • Lineare Suche im pollfds Array (poll)

    • Man erhält stets auch alle ”ereignislosen” Deskriptoren I Wünschenswert:

    • Man erhält nur noch Deskriptoren, die für die gewünschte Operation zu Verfügung stehen

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 10/42

  • SunOS: /dev/poll I /dev/poll:

    • setzt auf poll() auf, neue Gerätedatei /dev/poll

    • Behandlung mit ”alten” Syscalls: open, read, write I Vorgehen:

    • int fd = open("/dev/poll", O_RDWR);

    • pollfd Struktur(en) initialisieren und nach fd schreiben

    • via ioctl() auf Ereignisse warten

    • Relevante pollfd Strukturen werden in Userspace Buffer kopiert

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 11/42

  • Linux 2.6: epoll I Erster Patch: Linux 2.5.44 (2002)

    I Neues API: 3 neue Syscalls

    struct epoll_event{uint32_t events;

    epoll_data_t data;}

    I Level oder (optional) Edge Triggered

    I letztes close() auf fd entfernt diesen Automatisch

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 12/42

  • epoll: Vorgehen I int efd = epoll_create(hint);

    I epoll_event initialisieren; z.B epoll_event e = { .events = EPOLLIN };

    I hinzufügen: epoll_ctl(efd, EPOLL_CTL_ADD, fd, &e);

    I Mit epoll_wait auf Ereignisse warten

    I Edge-Triggered verhalten kann pro fd eingeschaltet werden: e.events |= EPOLLET;

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 13/42

  • *BSD: kqueue I FreeBSD 4.1 (2000), OpenBSD 3.5 (2004), NetBSD 2.0

    (Dez. 2004)

    struct kevent {

    uintptr_t ident; /* identifier for this event */

    short filter; /* filter for event */

    u_short flags; /* action flags for kqueue */

    u_int fflags; /* filter flag value */

    intptr_t data; /* filter data value */

    void *udata; /* opaque user data identifier */

    };

    I 2 Syscalls, ein Makro

    I Filterkonzept: u.a. für Timer und Signale

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 14/42

  • Vorgehen I int kfd = kqueue()

    I struct kevent initialisieren, z.B. EV_SET(&kev, fd, EV- FILT_READ,EV_ADD|EV_ENABLE, 0, 0, 0);

    I kevent()aktualisiert fd set und/oder wartet auf Ereignisse

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 15/42

  • Kevent I Linux hat kein einheitlichen Mechanismus auf Events zu

    reagieren

    I Ziel: möglichst viele Event so schnell wie möglich zu bearbeiten mit wenig System-Overhead

    I Probleme bei asynchronen I/O

    I Evgeniy Polyakov Patch - kevent

    I Kombination und Ideen aus AIO/kqueue Interface

    I Ringpuffer welcher gemmap()ed wird

    I kvent kann auf folgende Ereignisse reagieren: • Alles was poll() abdeckt KEVENT_POLL

    • Neue Netzwerk-Daten oder Verbindungen KEVENT_SOCKET

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 16/42

  • • inotify() Ereignisse KEVENT_INODE

    • Netzwerk asynchrone I/O Ereignisse KEVENT_NAIO

    • Timer Ereignisse KEVENT_TIMER I Erste Zahlen (httperf, 40K Verbindungen):

    • kevent: 3388.4 req/s

    • epoll und kevent_poll 2200-2500 req/s

    Hagen Paul Pfeifer • Florian Westphal Programmiermodelle - 17/42

  • Zusammenfassung I Alternativen gegeneinander abwägen

    • Protokoll, Zugriffsmuster

    • Betriebssystem(e), Portabilität I select: portabelste Schnittstelle, aber Problematisch bei

    vielen Deskriptoren

    I poll : Array; lineare Suche; Array muss verwaltet werden

    I Alternativen sind Systemspezifisch (Wrapperbibliotheken wie libevent exisitieren)

    I KISS – fork() kann Durchaus Mittel der Wahl sein!

    Hagen Paul Pfeifer • Florian Westphal I/O Optimierungen - 18/42

  • Kapitel 2 I/O Optimierungen

    Hagen Paul Pfeifer • Florian Westphal I/O Optimierungen - 19/42

  • Zero-Copy I Typische Lehrbuch-Serveranwendung:

    while((cnt = read(filefd, buf, buflen)) > 0) {

    char *bufptr = buf;

    do {

    ssize_t written = write(socket, bufptr, cnt);

    /* handle errors, if any ... */

    cnt -= written;

    bufptr += written;

    } while (cnt > 0);

    }

    Hagen Paul Pfeifer • Florian Westphal I/O Optimierungen - 20/42

  • Zero-Copy I Positiv: einfach und portabel

    I Negativ: nicht wirklich Performant 1. Kopieraktion hohe Kosten

    2. Syscalls verursachen weiteren Overhead

    3. Kontext-Wechsel: • Register sichern

    • Cache konsistent mit VM-Mappings, bei neuen VM-Mapping -> TLB flushen, (invXpg)

    • Kein neues VM-Mapping: Kernel-Thread oder US-Threads

    • Einige Architekturen müssen auch Cache flushen (x86: NULL-Makro)

    Hagen Paul Pfeifer • Florian Westphal I/O Optimierungen - 21/42

  • • Cold Caches

    4. Information Bonbon: (microseconds, lmbench, v2.6.10)

    cpu clock speed context switch

    pentium-M 1.8GHz 0.890

    dual-Xeon 2GHz 7.430

    Xscale 700MHz 108.2

    dual 970FX 1.8GHz 5.850

    ppc 7447 1GHz 1.720

    Hagen Paul Pfeifer • Florian Westphal I/O Optimierungen - 22/42

  • Read() - Write() I Read-Write näher betrachtet:

    I Speicher muss vier mal kopiert werden (nicht nötig)

    I Vier Kontext-Switches

    I Von Festplatte und nach NIC normalerweise via DMA

    Hagen Paul Pfeifer • Florian Westphal I/O Optimierungen - 23/42

  • Mmap() I Mmap-Writ