Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Состояния процесса в МОС Rev / RUN READY WAIT 1 2 Пользовательская фаза Фаза ядра ОС 3 4 5
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Проблема Серверу необходимо обслуживать множество клиентов. Клиенту, как правило, приходится взаимодействовать с пользователем и с сервером. Каждая операция ввода-вывода может блокировать процесс. Необходимо обеспечить возможность выполнения операций ввода-вывода несколькими дескрипторами файлов* (в любой момент времени). * Everything in Unix is a file
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Способы организации вв/выв Синхронный ввод-вывод (блокирующий) Неблокирующий ввод-вывод С возможностью уведомления о состоянии готовности (level-triggered readiness notification) С возможностью уведомления об изменении состоянии готовности (readiness change notification) Асинхронный ввод-вывод (с уведомлением о завершении операции)
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Синхронный ввод/вывод Обслуживанием каждого дескриптора файла занимается отдельный процесс (отдельная нить). while (true) { client_sock=accept(serv_sock,…); fork(); close(client_sock); waitpid(…); } close(serv_sock); read(client_sock,…); write(client_sock,…); close(client_sock); exit(); client_sock serv_sock Особенности: не требуется мультиплексирование ввода-вывода проблемы синхронизации и взаимодействия большая ресурсоёмкость проблема C10k возможна параллельная обработка запросов (SMP), нет проблемы «длинных запросов»
i = 0; while (true) { len = read(client_socks[i],…); if (len == 0) { /* соединение закрыто */ } if (len < 0 && errno != EAGAIN) { /* ошибка */ } if (len > 0) serve_client(i); new_sock = accept(serv_sock,…); if (new_sock >= 0) { fcntl(new_sock, F_SETFL, O_NONBLOCK); /* добавляем нов. сокет в client_socks[] */ } … i = ++i % num_socks; } Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Неблокирующий ввод/вывод Системный вызов возвращается сразу с кодом ошибки EAGAIN (в BSD – EWOULDBLOCK). Необходимо периодически повторять вызов до тех пор, пока он не завершится успешно либо возникнет другая ошибка. client_sock Особенности: холостые циклы снижение производительности системы единовременно обслуживается только 1 клиент serv_sock
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Level-triggered notification Процесс запрашивает у ядра ОС, возможен ли ввод/вывод без блокировки для какого-либо дескриптора из набора. Если ни один из дескрипторов «не готов», вызов может блокировать процесс или вернуться сразу. while (true) { /* подготовка к вызову */ num = select(…); /* или poll(…) */ if (num > 0) { /* проход по набору дескрипторов, обслуживание «готовых» */ } Особенности: ограничение FD_SETSIZE для select() единовременно обслуживается только один клиент (неэффективно для SMP) client_sock serv_sock
ОС Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Edge-triggered notification Для дескрипторов устанавливается флаг оповещения о готовности по сигналу. При смене состояния дескриптора с «неготового» на «готовое», при котором возможен ввод/вывод без блокировки, процессу посылается сигнал. Дальнейшие оповещения не происходят, пока дескриптор снова не будет переведён в «неготовое» состояние. /* подготовка к вызову */ sigaction(…); while (true) { sigsuspend(…); } Особенности: проблемы синхронизации доступа к данным сложности отладки (код обработчика должен быть тщательно продуман) единовременно обслуживается только один клиент (неэффективно для SMP) client_sock serv_sock void io_handler(…) { /* получем номер дескриптора и обслуживаем соответствующего клиента */ } сигнал
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Асинхронный ввод/вывод Особенности: проблемы синхронизации доступа к данным сложности отладки (код обработчика должен быть тщательно продуман) единовременно обслуживается только один клиент (неэффективно для SMP) Операции ввода/вывода сразу возвращаются в вызвавший их процесс. О завер- шении операции ОС уведомляет процесс через сигнал (в Linux; сообщение – в Windows) либо через специальный сигнализирующий объект (Windows). В Linux интерфейс AIO не позволяет работать с сокетами. В Windows сокеты и файлы имеют разные механизмы асинхронного ввода/вывода. Для файлов и объектов, открываемых с помощью CreateFile(), асинхронный ввод/вывод реализуется при помощи Overlapped I/O, для сокетов – реализован в Winsock в виде отдельного набора функций.
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Работа с процессами Создание дочернего процесса #include pid_t fork(void); При ошибке функция возвращает -1, дочерний процесс при этом не создаётся. В родительский процесс эта функция возвращает PID нового процесса. В дочерний процесс эта функция возвращает 0. Пример: pid_t new_pid; new_pid=fork(); if (new_pid == -1) { /* ошибка */ } if (new_pid == 0) { /* дочерний процесс */ } else { /* родительский процесс */ }
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Работа с процессами Завершение процесса #include void exit(int status); Функция не возвращается в вызвавший её процесс. В качестве статуса выхода используются только младшие 8 бит status. Значение 0 (EXIT_SUCCESS) означает успешное завершение процесса. Ненулевое значение (обычно EXIT_FAILURE=1) передаётся родительскому процессу в том случае, если процесс завершается из-за возникновения ошибки. После выполнения exit() ОС может поступить следующим образом: если родительский процесс установил для сигнала SIGCHLD в качестве обработчика SIG_IGN или задал флаг SA_NOCLDWAIT, значение status отбрасывается, завершаемый процесс уничтожается; если родительский процесс ожидал завершения потомка при помощи wait() или подобной функции, он возобновляется и получает статус завершения status, завершаемый процесс уничтожается. в противном случае завершаемый процесс переводится в состояние zombie, в котором он представляет собой контейнер для хранения значения status до тех пор, пока родительский процесс не сделает wait().
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Работа с процессами Ожидание завершения дочернего процесса #include pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options); Результат: PID завершившегося потомка. При ошибке функции возвращают -1. Для waitpid() возможен результат 0, означающий, что ни один потомок не завершился. Ожидаем pid: -1 – любого потомка, >0 – конкретного потомка. status: адрес переменной для хранения статуса выхода потомка (м.б. NULL) options: WNOHANG – вернуть 0, если ни один потомок не завершился
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Работа с процессами Получение PID и PPID #include pid_t getpid(void); pid_t getppid(void);
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Работа с процессами Запуск программы #include int execve( const char *filename,/*имя файла*/ char *const argv[],/*аргументы ком.строки*/ char *const envp[]);/*среда окружения*/ При ошибке функция возвращает -1. В случае успеха функция не возвращается в вызвавший её процесс, при этом содержимое сегментов кода, стека и данных меняется со старого на новое. Запущенная программа имеет такой же PID и сохраняет открытыми все те файловые дескрипторы, для которых не был установлен флаг FD_CLOEXEC. Для filename поиск в PATH не осуществляется! В массивах argv и envp последний элемент должен быть NULL. argv[0] – имя программы. envp содержит пары значений переменных окружения key=value. Пример: char *const argv[] = {"ps", "aux", NULL}; char *const envp[] = {NULL}; if (execve("/bin/ps", argv, envp)) { /* ошибка*/ }
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Работа с процессами «Демонизация» (пример) #include pid_t pid; int main() { pid = fork(); if (pid == -1) { /* ошибка */ } if (pid > 0) { /* родительский процесс */ sleep(1); exit(EXIT_SUCCESS); } close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); setsid(); /* далее процесс работает как "демон" */ }
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ POSIX Threads Библиотека pthread (POSIX Threads) предоставляет средства работы с нитями (threads). При компиляции программ, использующих нити, необходимо указать редактору связей (компоновщику) на использование данной библиотеки: gcc –lpthread my_prog.c Основные функции #include int pthread_create( pthread_t *thread, pthread_attr_t *attr, void* (*start_routine)(void*), void *arg); void pthread_exit(void *retval); int pthread_join(pthread_t th, void **thread_return); int pthread_detach(pthread_t th); int pthread_cancel(pthread_t th); pthread_t pthread_self(void);
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ POSIX Threads Атрибуты нитей #include int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *attr); int pthread_attr_setaname(pthread_attr_t *attr,int value); aname=detachstate: PTHREAD_CREATE_JOINABLE, PTHREAD_CREATE_DETACHED aname=shedpolicy: (требуются привилегии рута) SCHED_OTHER (обычная), SCHED_RR (RT, round-robin), SCHED_FIFO (RT, fifo) aname=shedparam: (можно менять «на лету») целое число, по умолчанию 0. int pthread_setcancelstate(int state, int *oldstate); state: PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE int pthread_setcanceltype(int type, int *oldtype); type: PTHREAD_CANCEL_DEFERRED, PTHREAD_CANCEL_ASYNCHRONOUS
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ POSIX Threads Пример: #include pthread_t th; void* child_th(void*); int main() { pthread_create(&th, NULL, &child_th, NULL); /* родительская нить занимается своими делами */ pthread_join(th, NULL); return 0; } void* child_th(void *tmp) { /* дочерняя нить делает своё дело */ pthread_exit(NULL); }
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Мультиплексирование вв/выв Синхронное мультиплексирование ввода/вывода #include /* POSIX */ int select( int n,/*макс. номер дескриптора*/ fd_set *readfds,/*дескрипторы для чтения*/ fd_set *writefds,/*дескрипторы для записи*/ fd_set *exceptfds,/*дескрипторы для искл.*/ struct timeval *timeout);/*таймаут*/ При ошибке функция возвращает -1. В случае успеха функция возвращает количество дескрипторов, готовых к операциям без блокировки. Функция возвращает 0, если истекло время ожидания (timeout). Ошибки: EBADF – в набор включён недействительный дескриптор EINTR – вызов был прерван сигналом EINVAL – некорректные значения n или timeout ENOMEM – не хватает памяти для выполнения операции
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Мультиплексирование вв/выв Перед вызовом select() необходимо заполнить битовые поля fd_set. При успешном возврате из select() в этих битовых полях остаются дескрипторы, для которых readfds: возможно чтение без блокировки, в том числе ноль байт (EOF), для сокетов в режиме прослушивания – возможен ли accept() без блокировки writefds: возможна ли запись без блокировки, для сокетов в режиме соединения (connect) – соединение установлено exceptfds: для сокетов – наличие пакетов с пометкой URG (out-of-band data) Битовое поле fd_set может иметь максимальную длину FD_SETSIZE. Дескрипторы с номером больше FD_SETSIZE не могут быть помещены в битовое поле FD_SETSIZE (обычно FD_SETSIZE=1024). Аргумент n – это максимальный номер дескриптора + 1. Обычно в качестве этого параметра передают значение FD_SETSIZE.
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Мультиплексирование вв/выв timeout: Если timeout NULL, то select() может блокироваться на неопределённое время – до «готовности» одного из заданных в наборах дескрипторов либо до получения сигнала. #include /* или */ struct timeval { long tv_sec;/* секунды */ long tv_usec;/* микросекунды */ }; Если timeout.tv_sec=0 и timeout.tv_usec=0, то select() возвращается сразу же, информируя, есть ли «готовые» дескрипторы. В Linux при возврате из select() timeout содержит оставшееся до истечения таймаута время. В других системах такое поведение не предусмотрено. POSIX рекомендует считать, что при возврате из select() значение timeout не определено (т. е. его нельзя использовать повторно).
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Мультиплексирование вв/выв Манипулирование битовыми полями fd_set #include /* POSIX */ /* очистка набора fds: */ void FD_ZERO(fd_set *fds); /* добавление дескриптора fd в набор: */ void FD_SET(int fd, fd_set *fds); /* удаление дескриптора fd из набора: */ void FD_CLR(int fd, fd_set *fds); /* проверка на наличие дескриптора fd в наборе: */ int FD_ISSET(int fd, const fd_set *fds);
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Мультиплексирование вв/выв Пример: fd_set rfds; struct timeval tv; int n, i; while (1) { FD_ZERO(&rfds); for (i = 0; i < num; i++) FD_SET(fd[i], &rfds); tv.tv_sec = 5;/* ждать не более 5 сек */ tv.tv_usec = 0; n = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); if (n == -1) { /* ошибка */}; else if (n > 0) { for (i=0; i
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Мультиплексирование вв/выв Синхронное мультиплексирование ввода/вывода #include int poll( struct pollfd *ufds,/*массив ожидаемых событий*/ unsigned nfds,/*кол-во эл-тов в ufds*/ int timeout);/*таймаут в мс*/ При ошибке функция возвращает -1. В случае успеха функция возвращает количество дескрипторов, готовых к операциям без блокировки. Функция возвращает 0, если истекло время ожидания (timeout). Отрицательный timeout означает ждать «до победы».
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Мультиплексирование вв/выв struct pollfd { int fd;/*дескриптор файла*/ short events;/*запрашиваемое событие*/ short revents;/*произошедшее событие*/ }; events, revents: набор битовых масок, объединяемых OR POLLIN – возможно чтение без блокировки POLLPRI – появились пакеты с пометкой URG (out-of-band data) POLLOUT – возможна запись без блокировки POLLERR – произошла ошибка POLLHUP – разрыв связи POLLNVAL – некорректное значение fd
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Мультиплексирование вв/выв Пример: struct pollfd fds[num]; int n, i; while (1) { for (i = 0; i < num; i++) { fds[i].fd = fd[i]; fds[i].events = POLLIN; fds[i].revents = 0; } n = poll(fds, num, -1); if (n == -1) { /* ошибка */}; else if (n > 0) { for (i=0; i
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Разное Определить, сколько байт доступно для чтения без блокировки #include void ioctl(int fd, int request,...); request = FIONREAD void ioctl(int fd, FIONREAD, int *size); #include int fcntl(int fd, int cmd, long arg); Перевести дескриптор в неблокирующий режим fcntl(fd, F_SETFL, O_NONBLOCK); Включить оповещение о готовности по сигналу fcntl(fd, F_SETFL, O_ASYNC); fcntl(fd, F_SETSIG, signum); fcntl(fd, F_SETOWN, pid);
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Сигналы (общие сведения) Сигнал – это вид средств межпроцессного обмена, использующийся в Unix- подобных и/или POSIX-совместимых ОС. Сигнал представляет собой асинхронное оповещение о некотором событии, посылаемое процессу. Источники событий: действия пользователя (CTRL-C, CTRL-Z, CTRL-\) исключения процессора (деление на 0, GPF, …) вызов kill() событие от ядра (SIGPIPE, SIGIO, …) Реакция на сигнал: вызывается обработчик, установленный процессом (не для всех сигналов) вызывается стандартный обработчик сигнал игнорируется Процесс может блокировать сигнал (отложить обработку сигнала). Особенности: возможны race conditions, поэтому необходимо обеспечивать синхронизацию доступа к данным в программе сигнал может прервать системный вызов, что делать в таком случае – остаётся на усмотрение программиста («непрозрачный» рестарт)
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Сигналы ANSI C Установка обработчика сигнала #include sighandler_t signal(int signum, signahdler_t action); action: функция обработчика void HANDLER(int signum) SIG_DFL /* стандартный обработчик */ SIG_IGN /* игнорировать сигнал */ Функция возвращает предыдущий способ обработки указанного сигнала или SIG_ERR при ошибке. Ожидание сигнала #include int pause(void); Функция всегда возвращает -1, при этом errno=EINTR.
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Сигналы ANSI C Генерация сигнала #include int raise(int signum); int kill(pid_t pid, int signum) Функции возвращает 0 при успешном завершении или -1 при ошибке.
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Сигналы POSIX #include Установка обработчика сигнала int sigaction(int signum,/*номер сигнала*/ const struct sigaction *act,/*действие*/ struct sigaction *oldact);/*предыдущее действие*/ Установка маски блокируемых сигналов int sigprocmask(int how,/* SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK */ const sigset_t *set,/*набор сигналов*/ segset_t *oldset);/*предыдущий набор*/ Получить отложенные на данный момент сигналы int sigpending(sigset_t *set); Функции возвращают -1 при ошибке или 0 при успешном завершении.
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Сигналы POSIX struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); /*obsolete*/ }; Не следует задавать и sa_handler, и sa_sigaction – только что-нибудь одно. В дополнение к маске сигналов sa_mask блокируются сигналы того же типа. sa_flags: SA_ONESHOT (SA_RESETHAND) – при первом же вызове обработчика сменить действие для сигнала на SIG_DFL SA_RESTART - если сигнал приходит во время исполнения системного вызова, то вызов рестартует при нормальном возврате из обработчика (в противном случае будет EINTR) SA_NOMASK (SA_NODEFER) – сигналы этого типа не блокируются при вызове обработчика SA_SIGINFO – обработчик сигнала требует 3 аргумента, а не 1 (sa_sigaction)
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Сигналы POSIX Манипуляции с набором сигналов (sigset_t) #include Очистить набор сигналов set int sigemptyset(sigset_t *set); Внести все сигналы в набор set int sigfillset(sigset_t *set); Добавить сигнал signum к набору set int sigaddset(sigset_t *set, int signum); Удалить сигнал signum из набора set int sigdelset(sigset_t *set, int signum); Проверить, есть ли сигнал signum в наборе set (0 – нет, 1 – есть, -1 – ошибка) int sigismember(const sigset_t *set, int signum);
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Сигналы POSIX Функция обработчика сигналов void HANDLER(int signum, siginfo_t *sinfo, void *addinfo); struct siginfo_t { int si_signo;/*номер сигнала*/ int si_errno;/*код ошибки*/ int si_code;/*код (причина) сигнала*/ pid_t si_pid;/*PID процесса, пославшего сигнал*/ uid_t si_uid;/*UID процесса, пославшего сигнал*/ /*... */ int si_band;/*событие (аналог pollfd.revents)*/ int si_fd;/*дескриптор файла*/ };
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Сигналы POSIX Ожидание сигнала #include int sigsuspend(const sigset_t *mask); mask – маска блокируемых на время ожидания сигналов int sigwaitinfo(const sigset_t *set, siginfo_t *info); int sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec timeout); set – маска ожидаемых сигналов struct timespec { long tv_sec;/*секунды*/ long tv_nsec;/*наносекунды*/ }; Генерация сигнала int sigqueue(pid_t pid, int signum, const union sigval value);
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Сигналы RT Стандартные сигналы «сливаются» Если в то время, пока сигнал заблокирован или процесс не находился в состоянии RUN, приходит ещё один сигнал того же типа, после деблокирования сигнала обработчик этого сигнала будет вызван лишь один раз. Сигналы «реального времени»(kernel 2.4.x) Сигналы с номером SIGRTMIN signum SIGRTMAX могут образовывать очередь. Если в то время, пока сигнал заблокирован, приходит ещё один сигнал того же типа, после деблокирования сигнала обработчик этого сигнала будет вызван столько раз, сколько сигналов попало в очередь. Очередь сигналов может переполнится. При этом генерируется сигнал SIGIO.
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Мультиплексирование вв/выв #include (kernel 2.6.x) Интерфейс epoll предоставляет как возможность оповещения типа level- triggered, так и edge-triggered, причём способ работы с дескрипторами в последнем случае такой же, как и при традиционном синхронном мультиплексировании ввода-вывода. Создание дескриптора epoll int epoll_create(int size); Управление дескриптором epoll int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); Ожидание дескриптора epoll int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Мультиплексирование вв/выв Создание дескриптора epoll int epoll_create(int size); size - размер набора дескрипторов, обслуживаемых epoll (рекомендуемое, но не максимальное значение) При успешном завершении функция возвращает номер дескриптора epoll (положительное число). При ошибке – -1. Дескриптор, созданный вызовом epoll_create() должен быть закрыт при помощи close().
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Мультиплексирование вв/выв Управление дескриптором epoll int epoll_ctl(int epfd,/*дескриптор epoll*/ int op,/*операция*/ int fd,/*дескриптор файла*/ struct epoll_event *event);/*события*/ op: EPOLL_CTL_ADD, EPOLL_CTL_MOD, EPOLL_CTL_DEL struct epoll_event { __uint32_t events;/*события*/ epoll_data_t data;/*данные пользователя*/ }; events: EPOLLIN (чтение), EPOLLOUT (запись), EPOLLPRI (urgent), EPOLLERR (ошибка), EPOLLHUP (разрыв соединения), EPOLLET – оповещение типа edge-triggered, EPOLLONESHOT – доставка только одного оповещения. union epoll_data_t {/*Поле для хранения вспомог. данных*/ void *ptr;/*например, какого-нибудь указателя*/ int fd;/*или дескриптора файла*/ /*... */ };
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Мультиплексирование вв/выв Ожидание дескриптора epoll int epoll_wait(int epfd,/*дескриптор epoll*/ struct epoll_event *events,/*массив событий*/ int maxevents,/*размер массива*/ int timeout);/*таймаут (мс)*/ Перед вызовом epoll_wait() надо отвести место под массив событий events и указать в вызове максимальное кол-во событий maxevents > 0, которое может принять ваш массив. Поле events[i].events будет содержать фактически произошедшее событие, а поле events[i].data – указанные пользователем данные (указатель ptr, или файловый дексриптор fd, или …). Если timeout == -1, то ожидание событий будет с бесконечным таймаутом. Функция возвращает количество дескрипторов, по которым зафиксировано событие (кол-во заполненных элементов в массиве events). Ноль означает, что за время timeout (мс) запрошенных событий не возникло. При ошибке возвращается -1.
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Edge-triggered notification Особенности работы с оповещениями типа ET - «готовность» дескриптора может наступить до того, как будет включен механизм оповещения типа ET - дескриптор может остаться в «готовом» состоянии и после обработки события В этом случае последующая операция ожидания события на этом дескрипторе может быть заблокирована до бесконечности. Решение: - дескриптор должен быть в неблокирующем режиме - до того, как начать ожидание оповещения типа ET необходимо сделать read (write) и убедиться, что возвращается EAGAIN
Петрозаводский госуниверситет, А. В. Соловьев, 2007СЕТЕВЫЕ ТЕХНОЛОГИИ Мультиплексирование вв/выв Пример: struct epoll_event evs[num]; int n, i, ep; ep = epoll_create(num); if (ep == -1) { /*ошибка*/ } for (i=0; i