restrict pid, const char
#include <spawn.h> int posix_spawn (pid_t * restrict pid, const char *restrict path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *restrict attrp, char *const argv [restrict], char *const envp [restrict]); int posix_spawnp (pid_t *restrict pid, const char *restrict file, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *restrict attrp, char *const argv [restrict], char *const envp [restrict]); |
Листинг 3.1. Описание функций одношагового порождения процессов. |
Закрыть окно |
#include <spawn.h> int posix_spawn_file_actions_init (posix_spawn_file_actions_t *file_actions); int posix_spawn_file_actions_destroy (posix_spawn_file_actions_t *file_actions); int posix_spawn_file_actions_addclose (posix_spawn_file_actions_t *file_actions, int fildes); int posix_spawn_file_actions_addopen ( posix_spawn_file_actions_t * restrict file_actions, int fildes, const char *restrict path, int oflag, mode_t mode); int posix_spawn_file_actions_adddup2 (posix_spawn_file_actions_t *file_actions, int fildes, int newfildes); |
Листинг 3.2. Описание функций формирования и ликвидации объектов типа posix_spawn_file_actions_t. |
Закрыть окно |
#include <spawn.h> int posix_spawnattr_init ( posix_spawnattr_t *attr); int posix_spawnattr_destroy ( posix_spawnattr_t *attr); int posix_spawnattr_getflags ( const posix_spawnattr_t *restrict attr, short *restrict flags); int posix_spawnattr_setflags ( posix_spawnattr_t *attr, short flags); int posix_spawnattr_getpgroup ( const posix_spawnattr_t *restrict attr, pid_t *restrict pgroup); int posix_spawnattr_setpgroup ( posix_spawnattr_t *attr, pid_t pgroup); |
Листинг 3.3. Описание функций формирования и опроса атрибутных объектов порождаемых процессов. |
Закрыть окно |
#include <spawn.h> #include <sched.h> int posix_spawnattr_getschedparam ( const posix_spawnattr_t *restrict attr, struct sched_param *restrict schedparam); int posix_spawnattr_setschedparam ( posix_spawnattr_t * restrict attr, const struct sched_param *restrict schedparam); int posix_spawnattr_getschedpolicy ( const posix_spawnattr_t *restrict attr, int *restrict schedpolicy); int posix_spawnattr_setschedpolicy ( posix_spawnattr_t *attr, int schedpolicy); |
Листинг 3.4. Описание функций опроса и установки параметров и политики планирования в атрибутных объектах порождаемых процессов. |
Закрыть окно |
#include <spawn.h> #include <signal.h> int posix_spawnattr_getsigdefault ( const posix_spawnattr_t *restrict attr, sigset_t *restrict sigdefault); int posix_spawnattr_setsigdefault ( posix_spawnattr_t * restrict attr, const sigset_t *restrict sigdefault); int posix_spawnattr_getsigmask ( const posix_spawnattr_t *restrict attr, sigset_t *restrict sigmask); int posix_spawnattr_setsigmask ( posix_spawnattr_t *restrict attr, const sigset_t *restrict sigmask); |
Листинг 3.5. Описание функций опроса и установки подразумеваемой обработки и маски сигналов в атрибутных объектах порождаемых процессов. |
Закрыть окно |
typedef struct { short posix_attr_flags; pid_t posix_attr_pgroup; sigset_t posix_attr_sigmask; sigset_t posix_attr_sigdefault; int posix_attr_schedpolicy; struct sched_param posix_attr_schedparam; } posix_spawnattr_t; typedef char *posix_spawn_file_actions_t; int posix_spawn (pid_t *pid, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv [], char *const envp []); |
Листинг 3.6. Фрагмент возможного содержимого файла spawn.h. |
Закрыть окно |
int posix_spawn (pid_t *pid, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv [], char *const envp []) { /* Создадим новый процесс */ if ((*pid = fork()) == (pid_t) 0) { /* Порожденный процесс */ /* Позаботимся о группе процессов */ if (attrp->posix_attr_flags & POSIX_SPAWN_SETPGROUP) { /* Изменим унаследованную группу */ if (setpgid (0, attrp->posix_attr_pgroup) != 0) { /* Неудача */ exit (127); } } /* Позаботимся о действующих идентификаторах */ /* пользователя и группы */ if (attrp->posix_attr_flags & POSIX_SPAWN_RESETIDS) { /* В данном случае неудачи быть не может */ setuid (getuid ()); setgid (getgid ()); } /* Позаботимся о подразумеваемом способе */ /* обработки сигналов */ if (attrp->posix_attr_flags & POSIX_SPAWN_SETSIGDEF) { struct sigaction deflt; sigset_t all_signals; int s; deflt.sa_handler = SIG_DFL; deflt.sa_flags = 0; sigfillset (&all_signals); /* Цикл по всем сигналам */ for (s = 0; sigismember (&all_signals, s); s++) { if (sigismember (&attrp->posix_attr_sigdefault, s)) { if (sigaction (s, &deflt, NULL) == -1) { exit (127); } } } } /* Проконтролируем остальные атрибуты */ /* . . . */ /* Подменим образ процесса */ execve (path, argv, envp); exit (127); } else { /* Родительский (вызывающий) процесс */ if (*pid == (pid_t) (-1)) return errno; return 0; } } |
Листинг 3.7. Фрагмент возможной библиотечной реализации функции posix_spawn(). |
Закрыть окно |
/* Запуск процесса с произвольным идентификатором */ /* пользователя */ uid_t old_uid; uid_t new_uid = ...; old_uid = getuid (); setuid (new_uid); posix_spawn (...); setuid (old_uid); |
Листинг 3.8. Пример установки характеристики порожденного процесса, не принадлежащей к числу контролируемых стандартными средствами. |
Закрыть окно |
posix_spawn_file_actions_t file_actions; posix_spawn_file_actions_init ( &file_actions); posix_spawn_file_actions_addopen ( & file_actions, 1, "outfile", ...); posix_spawn_file_actions_adddup2 ( &file_actions, socket_pair [1], 0); posix_spawn_file_actions_addclose ( &file_actions, socket_pair [0]); posix_spawn_file_actions_addclose ( &file_actions, socket_pair [1]); posix_spawn (..., &file_actions, ...); posix_spawn_file_actions_destroy ( &file_actions); |
Листинг 3.9. Пример перенаправления стандартных ввода и вывода порождаемого процесса. |
Закрыть окно |
#include <spawn.h> #include <stdio.h> #include <sys/wait.h> #include <errno.h> #define N 10000 int main (void) { char *s_argv [] = {"dummy", NULL}; char *s_env [] = {NULL}; int i; for (i = 0; i < N; i++) { if ((errno = posix_spawn ( NULL, "./dummy", NULL, NULL, s_argv, s_env)) != 0) { perror ("POSIX_SPAWN"); return (errno); } (void) wait (NULL); } return 0; } |
Листинг 3.10. Пример программы, порождающей в цикле практически пустые процессы с помощью функции posix_spawn(). |
Закрыть окно |
real 34.37 user 12.01 sys 22.07 |
Листинг 3.11. Возможные результаты измерения времени работы программы, порождающей в цикле практически пустые процессы с помощью функции posix_spawn(). |
Закрыть окно |
#include <signal.h> int sigqueue ( pid_t pid, int signo, const union sigval value); |
Листинг 3.12. Описание функции sigqueue(). |
Закрыть окно |
#include <signal.h> int sigwaitinfo ( const sigset_t *restrict set, siginfo_t *restrict info); int sigtimedwait ( const sigset_t *restrict set, siginfo_t * restrict info, const struct timespec *restrict timeout); |
Листинг 3.13. Описание функций sigwaitinfo() и sigtimedwait(). |
Закрыть окно |
#include <signal.h> int sigaltstack (const stack_t * restrict ss, stack_t *restrict oss); |
Листинг 3.14. Описание функции sigaltstack(). |
Закрыть окно |
#include <stdlib.h> #include <stdio.h> #include <signal.h> . . . stack_t sighstk; . . . if ((sighstk.ss_sp = malloc( SIGSTKSZ)) == NULL) { perror ("malloc (SIGSTKSZ)"); /* Аварийное завершение */ } sighstk.ss_size = SIGSTKSZ; sighstk.ss_flags = 0; if (sigaltstack (&sighstk, (stack_t *) NULL) != 0) { perror ("SIGALTSTACK"); . . . } . . . |
Листинг 3.15. Типичная схема определения альтернативного стека. |
Закрыть окно |
#include <setjmp.h> int sigsetjmp ( sigjmp_buf env, int savemask); void siglongjmp (sigjmp_buf env, int val); |
Листинг 3.16. Описание функций sigsetjmp() и siglongjmp(). |
Закрыть окно |
/* * * * * * * * * * * * * * * * * * * * * * * */ /* Многопотоковый вариант обеда философов */ /* с использованием сигналов реального времени */ /* * * * * * * * * * * * * * * * * * * * * * * */ #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <signal.h> #include <setjmp.h> #include <pthread.h> #include <time.h> #include <errno.h> /* Число обедающих философов */ #define QPH 5 /* Время (в секундах) на обед */ #define FO 15 /* Длительность еды */ #define ernd (rand () % 3 + 1) /* Длительность разговора */ #define trnd (rand () % 5 + 1) /* Номер сигнала, используемого для захвата и освобождения вилок */ #define SIG_FORK SIGRTMIN /* Номер сигнала, используемого для информирования философа */ #define SIG_PHIL SIGINT static pthread_t pt_id [QPH]; /* Массив идентификаторов */ /* потоков - философов */ static int fork_busy [QPH] = {0, }; /* Состояние вилок */ static int phil_req [QPH] = {0, }; /* Невыполненные */ /* заявки на вилки */ static sigjmp_buf phil_env [QPH]; /* Массив буферов для */ /* нелокальных переходов */ static pid_t pid_wt; /* Идентификатор процесса,*/ /* контролирующего вилки */ static pthread_key_t phil_key; /* Ключ индивидуальных */ /* данных потоков-философов */ /* * * * * * * * * * * * * * * * * * * */ /* Функция обработки сигнала SIG_PHIL */ /* * * * * * * * * * * * * * * * * * * */ static void phil_eat (int signo) { int no; /* Номер философа, которому достался сигнал */ no = (int) pthread_getspecific (phil_key); if ((no > 0) && (no <= QPH)) { siglongjmp (phil_env [no – 1], signo); } } /* * * * * * * * * * * * * * * * * * * * * * */ /* Попытка выполнить заявку на захват вилок */ /* от философа номер no, если она есть */ /* * * * * * * * * * * * * * * * * * * * * * */ static void fork_lock (int no) { if (phil_req [no – 1] != 0) { /* Заявка есть. */ /* Вилки свободны? */ if ((fork_busy [no – 1] == 0) && (fork_busy [no % QPH] == 0)) { /* Выполним заявку */ fork_busy [no – 1] = fork_busy [no % QPH] = 1; phil_req [no – 1] = 0; (void) pthread_kill (pt_id [no – 1], SIG_PHIL); } } } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Стартовая функция потока, обслуживающего заявки на */ /* захват и освобождение вилок. */ /* Заявка передается в виде значения, ассоциированного */ /* с сигналом signo. */ /* Значение no > 0 запрашивает захват вилок для философа */ /* с номером no, no < 0 – освобождение вилок философа -no */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ void *start_waiter (void *signo) { siginfo_t sinfo; /* Структура для получения данных */ /* о сигнале */ int no; /* Номер философа, приславшего заявку */ sigset_t s_sgno; /* Маска ожидаемых сигналов */ pid_wt = getpid (); /* Сформируем маску ожидаемых сигналов */ if ((sigemptyset (&s_sgno) != 0) || (sigaddset (&s_sgno, (int) signo) != 0)) { perror ("SIGEMPTYSET/SIGADDSET"); return (NULL); } while (1) { if (sigwaitinfo (&s_sgno, &sinfo) != (int) signo) { return (NULL); } else { /* Поступила заявка. */ /* Посмотрим, что от нас хотят */ if ((no = sinfo.si_value.sival_int) > 0) { /* Заявка на захват вилок. */ /* Запомним ее ... */ phil_req [no – 1] = 1; /* ... и попробуем выполнить */ fork_lock (no); } else { /* Освобождение вилок */ no = -no; fork_busy [no – 1] = fork_busy [no % QPH] = 0; /* Попробуем выполнить заявки от соседей */ fork_lock (no % QPH + 1); fork_lock (no == 1 ? QPH : (no – 1)); } } /* Другие сигналы нас не интересуют */ } /* while (1) */ } /* * * * * * * * * * * * * * * * * * */ /* Стартовая функция потока-философа. */ /* Аргумент – номер философа */ /* * * * * * * * * * * * * * * * * * */ void *start_phil (void *no) { int fo; /* Время до конца обеда */ int t; /* Время очередного отрезка еды или беседы */ time_t tbe; /* Время, когда философу понадобились вилки */ union sigval sval; /* Значение посылаемого сигнала: */ /* (int) no – заказ вилок */ /* -(int) no – освобождение вилок */ /* Запомним значение аргумента в качестве */ /* индивидуальных данных потока */ (void) pthread_setspecific (phil_key, no); /* Подготовка к обеду */ fo = FO; if (sigsetjmp (phil_env [(int) no – 1], 1) != 0) { /* Сюда придем после нелокального перехода */ /* из обработчика сигнала SIG_PHIL. */ /* Философ просил вилки и получил их */ printf ("Философ %d ест\n", (int) no); t = ernd; sleep (t); /* Нужно вычесть времена еды и ожидания вилок */ fo -= (int) (time ((time_t *) NULL) – tbe) + t; /* Отдает вилки */ sval.sival_int = -((int) no); (void) sigqueue (pid_wt, SIG_FORK, sval); } while (fo > 0) { printf ("Философ %d беседует\n", (int) no); t = trnd; sleep (t); fo -= t; /* Пытается взять вилки */ tbe = time ((time_t *) NULL); sval.sival_int = (int) no; (void) sigqueue (pid_wt, SIG_FORK, sval); /* Пока вилки заняты, приходится беседовать... */ printf ("Философ %d беседует в ожидании вилок\n", (int) no); sleep (fo); fo = 0; } /* while */ printf ("Философ %d закончил обед\n", (int) no); return (NULL); } /* * * * * * * * * * */ /* Организация обеда */ /* * * * * * * * * * */ int main (void) { int no; /* Номер философа */ struct sigaction sact; /* Структура для обработки */ /* обычных сигналов */ pthread_t pt_wt; /* Идентификатор потока, */ /* управляющего вилками */ /* Блокируем сигнал SIG_FORK */ if ((sigemptyset (&sact.sa_mask) == 0) && (sigaddset (&sact.sa_mask, SIG_FORK) == 0)) { (void) pthread_sigmask (SIG_BLOCK, &sact.sa_mask, (sigset_t *) NULL); } /* Установим для сигнала SIG_FORK флаг SA_SIGINFO */ sact.sa_flags = SA_SIGINFO; sact.sa_sigaction = (void (*) (int, siginfo_t *, void *)) SIG_DFL; (void) sigaction (SIG_FORK, &sact, (struct sigaction *) NULL); /* Установим реакцию на сигнал SIG_PHIL */ sact.sa_handler = phil_eat; sigemptyset (&sact.sa_mask); sact.sa_flags = 0; (void) sigaction (SIG_PHIL, &sact, (struct sigaction *) NULL); /* Создадим поток, захватывающий и освобождающий вилки */ if ((errno = pthread_create (&pt_wt, NULL, start_waiter, (void *) SIG_FORK)) != 0) { perror ("PTHREAD_CREATE-1"); return (errno); } /* Создадим ключ индивидуальных данных */ if ((errno = pthread_key_create (&phil_key, (void (*) (void *)) NULL)) != 0) { perror ("PTHREAD_KEY_CREATE"); return (errno); } /* Все – к столу */ for (no = 1; no <= QPH; no++) { if ((errno = pthread_create (&pt_id [no – 1], NULL, start_phil, (void *) no)) != 0) { perror ("PTHREAD_CREATE"); return (no); } } /* Ожидание завершения обеда */ for (no = 1; no <= QPH; no++) { (void) pthread_join (pt_id [no – 1], NULL); } (void) pthread_key_delete (phil_key); /* Завершим поток, контролирующий вилки */ (void) pthread_cancel (pt_wt); (void) pthread_join (pt_wt, NULL); return 0; } |
Листинг 3.17. Пример реализации обеда философов с использованием сигналов реального времени. |
Закрыть окно |
Философ 1 беседует Философ 2 беседует Философ 3 беседует Философ 4 беседует Философ 5 беседует Философ 4 беседует в ожидании вилок Философ 4 ест Философ 2 беседует в ожидании вилок Философ 2 ест Философ 3 беседует в ожидании вилок Философ 4 беседует Философ 1 беседует в ожидании вилок Философ 5 беседует в ожидании вилок Философ 5 ест Философ 2 беседует Философ 3 ест Философ 5 беседует Философ 1 ест Философ 2 беседует в ожидании вилок Философ 4 беседует в ожидании вилок Философ 3 беседует Философ 4 ест Философ 5 беседует в ожидании вилок Философ 1 беседует Философ 2 ест Философ 2 беседует Философ 4 закончил обед Философ 1 беседует в ожидании вилок Философ 5 ест Философ 2 беседует в ожидании вилок Философ 2 ест Философ 3 беседует в ожидании вилок Философ 3 закончил обед Философ 1 закончил обед Философ 5 закончил обед Философ 2 закончил обед |
Листинг 3.18. Возможные результаты выполнения программы, реализующей обед философов с использованием сигналов реального времени. |
Закрыть окно |
#include <time.h> int clock_nanosleep ( clockid_t clock_id, int flags, const struct timespec *rqtp, struct timespec *rmtp); |
Листинг 3.19. Описание функции clock_nanosleep(). |
Закрыть окно |
/* * * * * * * * * * * * * * * * * * * */ /* Организация периодических процессов */ /* с помощью функции clock_nanosleep() */ /* * * * * * * * * * * * * * * * * * * */ #define _XOPEN_SOURCE 600 #include <time.h> #include <stdio.h> #include <unistd.h> /* * * * * * * * * * * * * * * * * * * * */ /* Сложение двух структур типа timespec */ /* * * * * * * * * * * * * * * * * * * * */ static void tmspc_add (struct timespec *a1, struct timespec *a2, struct timespec *res) { res->tv_sec = a1->tv_sec + a2->tv_sec + (a1->tv_nsec + a2->tv_nsec) / 1000000000; res->tv_nsec = (a1->tv_nsec + a2->tv_nsec) % 1000000000; } /* * * * * * * * * * * * * * * * * * * */ /* Организация периодического процесса */ /* * * * * * * * * * * * * * * * * * * */ int main (void) { struct timespec t_bp; /* Время начала очередного */ / *периода выполнения */ struct timespec prd = {1, 250000000}; /* Период */ /* выполнения: 1.25 сек */ clockid_t clk_id = CLOCK_REALTIME; /* Идентификатор */ /* используемых часов */ struct timespec t_tmp; int i; /* Запомним время начала выполнения */ (void) clock_gettime (clk_id, &t_bp); printf ("Начало выполнения: %ld сек %ld нсек\n", t_bp.tv_sec, t_bp.tv_nsec); for (i = 0; i < 8; i++) { /* Содержательные действия. */ /* Предполагается, что они укладываются в период */ sleep (1); /* Доспим до конца периода */ tmspc_add (&t_bp, &prd, &t_bp); (void) clock_nanosleep (clk_id, TIMER_ABSTIME, &t_bp, NULL); (void) clock_gettime (clk_id, &t_tmp); printf ("Конец периода: %ld сек %ld нсек\n", t_tmp.tv_sec, t_tmp.tv_nsec); } return 0; } |
Листинг 3.20. Пример применения функции clock_nanosleep() для организации периодического процесса. |
Закрыть окно |
Начало выполнения: 1079080828 сек 194254000 нсек Конец периода: 1079080829 сек 460021000 нсек Конец периода: 1079080830 сек 710023000 нсек Конец периода: 1079080831 сек 960020000 нсек Конец периода: 1079080833 сек 210021000 нсек Конец периода: 1079080834 сек 460023000 нсек Конец периода: 1079080835 сек 710021000 нсек Конец периода: 1079080836 сек 960094000 нсек Конец периода: 1079080838 сек 210022000 нсек |
Листинг 3.21. Возможные результаты выполнения программы, использующей функцию clock_nanosleep() для организации периодического процесса. |
Закрыть окно |
#include <signal.h> #include <time.h> int timer_create ( clockid_t clockid, struct sigevent *restrict evp, timer_t *restrict timerid); |
Листинг 3.22. Описание функции timer_create(). |
Закрыть окно |
#include <time.h> int timer_delete (timer_t timerid); |
Листинг 3.23. Описание функции timer_delete(). |
Закрыть окно |
#include <time.h> int timer_gettime (timer_t timerid, struct itimerspec *value); int timer_settime ( timer_t timerid, int flags, const struct itimerspec *restrict value, struct itimerspec *restrict ovalue); int timer_getoverrun (timer_t timerid); |
Листинг 3.24. Описание функций timer_gettime(), timer_settime() и timer_getoverrun(). |
Закрыть окно |
/* * * * * * * * * * * * * * * * * * * * * * * */ /* Многопотоковый вариант обеда философов */ /* с использованием сигналов реального времени */ /* и таймера для контроля длительности обеда */ /* * * * * * * * * * * * * * * * * * * * * * * */ #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <signal.h> #include <setjmp.h> #include <pthread.h> #include <time.h> #include <errno.h> /* Число обедающих философов */ #define QPH 5 /* Время (в секундах) на обед */ #define FO 15 /* Длительность еды */ #define ernd (rand () % 3 + 1) /* Длительность разговора */ #define trnd (rand () % 5 + 1) /* Номер сигнала, используемого для захвата */ /* и освобождения вилок */ #define SIG_FORK SIGRTMIN /* Номер сигнала, используемого таймером */ /* для окончания обеда */ #define SIG_DEND (SIGRTMIN + 1) /* Номер сигнала, используемого */ /* для информирования философа */ #define SIG_PHIL SIGINT static pthread_t pt_id [QPH]; /* Массив идентификаторов */ /* потоков-философов */ static int fork_busy [QPH] = {0, }; /* Состояние вилок */ static int phil_req [QPH] = {0, }; /* Невыполненные */ /* заявки на вилки */ static sigjmp_buf phil_env [QPH]; /* Массив буферов для */ /* нелокальных переходов */ static pid_t pid_wt; /* Идентификатор процесса, */ /* контролирующего вилки */ static pthread_key_t phil_key; /* Ключ индивидуальных */ /* данных потоков-философов */ /* * * * * * * * * * * * * * * * * * */ /* Функция обработки сигнала SIG_PHIL */ /* * * * * * * * * * * * * * * * * * */ static void phil_eat (int signo) { int no; /* Номер философа, которому достался сигнал */ no = (int) pthread_getspecific (phil_key); if ((no > 0) && (no <= QPH)) { siglongjmp (phil_env [no – 1], signo); } } /* * * * * * * * * * * * * * * * * * */ /* Деструктор индивидуальных данных */ /* потоков-философов */ /* * * * * * * * * * * * * * * * * * */ static void phil_destructor (void *no) { printf ("Философ %d закончил обед\n", (int) no); } /* * * * * * * * * * * * * * * * * * * * * * * */ /* Функция, вызываемая при срабатывании таймера, */ /* контролирующего длительность обеда */ /* * * * * * * * * * * * * * * * * * * * * * ** */ static void end_ph_dinner (union sigval dummy) { int i; for (i = 0; i < QPH; i++) { (void) pthread_cancel (pt_id [i]); } } /* * * * * * * * * * * * * * * * * * * * * */ /* Попытка выполнить заявку на захват вилок */ /* от философа номер no, если она есть */ /* * * * * * * * * * * * * * * * * * * * * */ static void fork_lock (int no) { if (phil_req [no – 1] != 0) { /* Заявка есть. */ /* Вилки свободны? */ if ((fork_busy [no – 1] == 0) && (fork_busy [no % QPH] == 0)) { /* Выполним заявку */ fork_busy [no – 1] = fork_busy [no % QPH] = 1; phil_req [no – 1] = 0; (void) pthread_kill (pt_id [no – 1], SIG_PHIL); } } } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Стартовая функция потока, обслуживающего заявки на */ /* захват и освобождение вилок. */ /* Заявка передается в виде значения, ассоциированного */ /* с сигналом signo. */ /* Значение no > 0 запрашивает захват вилок для философа */ /* с номером no, no < 0 – освобождение вилок философа -no */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ void *start_waiter (void *signo) { siginfo_t sinfo; /* Структура для получения данных */ /* о сигнале */ int no; /* Номер философа, приславшего */ /* заявку */ sigset_t s_sgno; /* Маска ожидаемых сигналов */ pid_wt = getpid (); /* Сформируем маску ожидаемых сигналов */ if ((sigemptyset (&s_sgno) != 0) || (sigaddset (&s_sgno, (int) signo) != 0)) { perror ("SIGEMPTYSET/SIGADDSET"); return (NULL); } while (1) { if (sigwaitinfo (&s_sgno, &sinfo) != (int) signo) { return (NULL); } else { /* Поступила заявка. */ /* Посмотрим, что от нас хотят */ if ((no = sinfo.si_value.sival_int) > 0) { /* Заявка на захват вилок. */ /* Запомним ее ... */ phil_req [no – 1] = 1; /* ... и попробуем выполнить */ fork_lock (no); } else { /* Освобождение вилок */ no = -no; fork_busy [no – 1] = fork_busy [no % QPH] = 0; /* Попробуем выполнить заявки от соседей */ fork_lock (no % QPH + 1); fork_lock (no == 1 ? QPH : (no – 1)); } } /* Другие сигналы нас не интересуют */ } /* while (1) */ } /* * * * * * * * * * * * * * * * * * */ /* Стартовая функция потока-философа. */ /* Аргумент – номер философа */ /* * * * * * * * * * * * * * * * * * */ void *start_phil (void *no) { union sigval sval; /* Значение посылаемого сигнала: */ /* (int) no – заказ вилок */ /* -(int) no – освобождение вилок */ /* Запомним значение аргумента в качестве */ /* индивидуальных данных потока */ (void) pthread_setspecific (phil_key, no); if (sigsetjmp (phil_env [(int) no – 1], 1) != 0) { /* Сюда придем после нелокального перехода */ /* из обработчика сигнала SIG_PHIL. */ /* Философ просил вилки и получил их */ printf ("Философ %d ест\n", (int) no); if (sleep (ernd) != 0) { return (NULL); } /* Отдает вилки */ sval.sival_int = -((int) no); (void) sigqueue (pid_wt, SIG_FORK, sval); } { /* Обед */ printf ("Философ %d беседует\n", (int) no); if (sleep (trnd) != 0) { return (NULL); } /* Пытается взять вилки */ sval.sival_int = (int) no; (void) sigqueue (pid_wt, SIG_FORK, sval); /* Пока вилки заняты, приходится беседовать... */ printf ("Философ %d беседует в ожидании вилок\n", (int) no); sleep (FO); } /* Конец обеда */ return (NULL); } /* * * * * * * * * * */ /* Организация обеда */ /* * * * * * * * * * */ int main (void) { int no; /* Номер философа */ struct sigaction sact; /* Структура для обработки */ /* обычных сигналов */ struct sigevent dend; /* Структура для генерации */ /* сигнала таймером */ timer_t dend_tmrid; /* Идентификатор таймера, */ /* контролирующего длительность */ /* обеда */ struct itimerspec dend_tmrsp = {{0, 0}, {FO, 0}}; /* Структура для взведения таймера, */ /* контролирующего длительность обеда */ pthread_t pt_wt; /* Идентификатор потока, управляющего */ /* вилками */ /* Блокируем сигнал SIG_FORK */ if ((sigemptyset (&sact.sa_mask) == 0) && (sigaddset (&sact.sa_mask, SIG_FORK) == 0)) { (void) pthread_sigmask (SIG_BLOCK, &sact.sa_mask, (sigset_t *) NULL); } /* Установим для сигнала SIG_FORK флаг SA_SIGINFO */ sact.sa_flags = SA_SIGINFO; sact.sa_sigaction = (void (*) (int, siginfo_t *, void *)) SIG_DFL; (void) sigaction (SIG_FORK, &sact, (struct sigaction *) NULL); /* Установим реакцию на сигнал SIG_PHIL */ sact.sa_handler = phil_eat; sigemptyset (&sact.sa_mask); sact.sa_flags = 0; (void) sigaction (SIG_PHIL, &sact, (struct sigaction *) NULL); /* Создадим поток, захватывающий и освобождающий вилки */ if ((errno = pthread_create (&pt_wt, NULL, start_waiter, (void *) SIG_FORK)) != 0) { perror ("PTHREAD_CREATE-1"); return (errno); } /* Создадим ключ индивидуальных данных */ if ((errno = pthread_key_create (&phil_key, phil_destructor)) != 0) { perror ("PTHREAD_KEY_CREATE"); return (errno); } /* Создадим и взведем таймер, */ /* контролирующий длительность обеда */ dend.sigev_notify = SIGEV_THREAD; dend.sigev_signo = SIG_DEND; dend.sigev_notify_function = end_ph_dinner; dend.sigev_notify_attributes = NULL; if (timer_create (CLOCK_REALTIME, &dend, &dend_tmrid) != 0) { perror ("TIMER_CREATE"); return (-1); } if (timer_settime (dend_tmrid, 0, &dend_tmrsp, NULL) != 0) { perror ("TIMER_SETTIME"); return (-1); } /* Все – к столу */ for (no = 1; no <= QPH; no++) { if ((errno = pthread_create (&pt_id [no – 1], NULL, start_phil, (void *) no)) != 0) { perror ("PTHREAD_CREATE"); return (no); } } /* Ожидание завершения обеда */ for (no = 1; no <= QPH; no++) { (void) pthread_join (pt_id [no – 1], NULL); } (void) pthread_key_delete (phil_key); (void) timer_delete (dend_tmrid); /* Поток, контролирующий вилки, */ /* можно не терминировать и не ждать */ return 0; } |
Листинг 3.25. Пример реализации обеда философов с использованием сигналов реального времени и таймера. |
Закрыть окно |
Философ 1 беседует Философ 2 беседует Философ 3 беседует Философ 4 беседует Философ 5 беседует Философ 4 беседует в ожидании вилок Философ 4 ест Философ 2 беседует в ожидании вилок Философ 2 ест Философ 3 беседует в ожидании вилок Философ 4 беседует Философ 1 беседует в ожидании вилок Философ 5 беседует в ожидании вилок Философ 5 ест Философ 2 беседует Философ 3 ест Философ 5 беседует Философ 1 ест Философ 2 беседует в ожидании вилок Философ 4 беседует в ожидании вилок Философ 3 беседует Философ 4 ест Философ 5 беседует в ожидании вилок Философ 1 беседует Философ 2 ест Философ 2 беседует Философ 1 беседует в ожидании вилок Философ 4 беседует Философ 1 ест Философ 2 беседует в ожидании вилок Философ 3 беседует в ожидании вилок Философ 3 ест Философ 1 беседует Философ 5 ест Философ 4 беседует в ожидании вилок Философ 5 беседует Философ 1 закончил обед Философ 2 закончил обед Философ 3 беседует Философ 4 закончил обед Философ 5 закончил обед Философ 3 беседует в ожидании вилок Философ 3 закончил обед |
Листинг 3.26. Возможные результаты выполнения программы, реализующей обед философов с использованием сигналов реального времени и таймера. |
Закрыть окно |