Программирование в стандарте POSIX

       

Программа копирует строки со стандартного


#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/wait.h>
/* Программа копирует строки со стандартного ввода на стандартный вывод, */ /* "прокачивая" их через канал. */ /* Используются функции ввода/вывода нижнего уровня */
#define MY_PROMPT "Вводите строки\n" #define MY_MSG "Вы ввели: "
int main (void) { int fd [2]; char buf [1]; int new_line = 1; /* Признак того, что надо выдать сообщение MY_MSG */ /* перед отображением очередной строки */
/* Создадим безымянный канал */ if (pipe (fd) < 0) { perror ("PIPE"); exit (1); }
switch (fork ()) { case -1: perror ("FORK"); exit (2); case 0: /* Чтение из канала и выдачу на стандартный вывод */ /* реализуем в порожденном процессе. */ /* Необходимо закрыть дескриптор, предназначенный */ /* для записи в канал, иначе чтение не завершится */ /* по концу файла */ close (fd [1]); while (read (fd [0], buf, 1) == 1) { if (write (1, buf, 1) != 1) { perror ("WRITE TO STDOUT"); break; } } exit (0); }
/* Чтение со стандартного ввода и запись в канал */ /* возложим на родительский процесс. */ /* Из соображений симметрии закроем дескриптор, */ /* предназначенный для чтения из канала */ close (fd [0]); if (write (fd [1], MY_PROMPT, sizeof (MY_PROMPT) - 1) != sizeof (MY_PROMPT) - 1) { perror ("WRITE TO PIPE-1"); }
while (read (0, buf, 1) == 1) { /* Перед отображением очередной строки */ /* нужно выдать сообщение MY_MSG */ if (new_line) { if (write (fd [1], MY_MSG, sizeof (MY_MSG) - 1) != sizeof (MY_MSG) - 1) { perror ("WRITE TO PIPE-2"); break; } } if (write (fd [1], buf, 1) != 1) { perror ("WRITE TO PIPE-3"); break; } new_line = (buf [0] == '\n'); } close (fd [1]);
(void) wait (NULL); return (0); }
Листинг 8.1. Пример взаимодействия между процессами через канал с помощью функций ввода/вывода нижнего уровня.
Закрыть окно






#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <limits.h> #include <sys/wait.h> #include <assert.h>
/* Программа копирует строки со стандартного ввода на стандартный вывод, */ /* "прокачивая" их через канал. */ /* Используются функции буферизованного ввода/вывода */
int main (void) { int fd [2]; FILE *fp [2]; char line [LINE_MAX];
/* Создадим безымянный канал */ if (pipe (fd) < 0) { perror ("PIPE"); exit (1); }
/* Сформируем потоки по файловым дескрипторам канала */ assert ((fp [0] = fdopen (fd [0], "r")) != NULL); assert ((fp [1] = fdopen (fd [1], "w")) != NULL);
/* Отменим буферизацию вывода */ setbuf (stdout, NULL); setbuf (fp [1], NULL);
switch (fork ()) { case -1: perror ("FORK"); exit (2); case 0: /* Чтение из канала и выдачу на стандартный вывод */ /* реализуем в порожденном процессе. */ /* Необходимо закрыть поток, предназначенный для */ /* записи в канал, иначе чтение не завершится */ /* по концу файла */ fclose (fp [1]); while (fgets (line, sizeof (line), fp [0]) != NULL) { if (fputs (line, stdout) == EOF) { break; } }
exit (0); }
/* Чтение со стандартного ввода и запись в канал */ /* возложим на родительский процесс. */ /* Из соображений симметрии закроем поток, */ /* предназначенный для чтения из канала */ fclose (fp [0]); fputs ("Вводите строки\n", fp [1]); while (fgets (line, sizeof (line), stdin) != NULL) { if ((fputs ("Вы ввели: ", fp [1]) == EOF) || (fputs (line, fp [1]) == EOF)) { break; } } fclose (fp [1]);
(void) wait (NULL); return (0); }
Листинг 8.2. Пример взаимодействия между процессами через канал с помощью функций буферизованного ввода/вывода.
Закрыть окно




#include <stdio.h> int pclose (FILE *stream);
Листинг 8.3. Описание функции pclose().
Закрыть окно




#include <stdio.h> /* Программа печатает несколько первых строк треугольника Паскаля */ #define T_SIZE 16 int main (void) { FILE *outptr; long tp [T_SIZE]; /* Массив для хранения текущей строки треугольника */ int i, j;
/* Инициализируем массив, чтобы далее все элементы */ /* можно было считать и выводить единообразно */ tp [0] = 1; for (i = 1; i < T_SIZE; i++) { tp [i] = 0; }
/* Создадим канал с командой */ if ((outptr = popen ("lp", "w")) == NULL) { perror ("POPEN"); return (-1); }
(void) fprintf (outptr, "\nТреугольник Паскаля:\n");
for (i = 0; i < T_SIZE; i++) { /* Элементы очередной строки нужно считать от конца к началу */ /* Элемент tp [0] пересчитывать не нужно */ for (j = i; j > 0; j--) { tp [j] += tp [j - 1]; } /* Вывод строки треугольника в канал */ for (j = 0; j <= i; j++) { (void) fprintf (outptr, " %ld", tp [j]); } (void) fprintf (outptr, "\n"); }
return (pclose (outptr)); }
Листинг 8.4. Пример создания и использования канала для вывода данных.
Закрыть окно




#include <stdio.h> #include <limits.h> #include <assert.h>
#define MY_CMD "ls -l *.c"
int main (void) { FILE *inptr; char line [LINE_MAX];
assert ((inptr = popen (MY_CMD, "r")) != NULL);
while (fgets (line, sizeof (line), inptr) != NULL) { fputs (line, stdout); }
return (pclose (inptr)); }
Листинг 8.5. Пример создания и использования канала для ввода данных.
Закрыть окно




#include <signal.h> int kill ( pid_t pid, int sig);
Листинг 8.6. Описание функции kill().
Закрыть окно




1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 32) SIGRTMIN 33) SIGRTMIN+1 34) SIGRTMIN+2 35) SIGRTMIN+3 36) SIGRTMIN+4 37) SIGRTMIN+5 38) SIGRTMIN+6 39) SIGRTMIN+7 40) SIGRTMIN+8 41) SIGRTMIN+9 42) SIGRTMIN+10 43) SIGRTMIN+11 44) SIGRTMIN+12 45) SIGRTMIN+13 46) SIGRTMIN+14 47) SIGRTMIN+15 48) SIGRTMAX-15 49) SIGRTMAX-14 50) SIGRTMAX-13 51) SIGRTMAX-12 52) SIGRTMAX-11 53) SIGRTMAX-10 54) SIGRTMAX-9 55) SIGRTMAX-8 56) SIGRTMAX-7 57) SIGRTMAX-6 58) SIGRTMAX-5 59) SIGRTMAX-4 60) SIGRTMAX-3 61) SIGRTMAX-2 62) SIGRTMAX-1 63) SIGRTMAX
Листинг 8.7. Возможный результат выполнения команды kill -l.
Закрыть окно




#include <signal.h> int raise (int sig);
Листинг 8.8. Описание функции raise().
Закрыть окно




#include <stdlib.h> void abort (void);
Листинг 8.9. Описание функции abort().
Закрыть окно




#include <signal.h> int sigaction ( int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);
Листинг 8.10. Описание функции sigaction().
Закрыть окно




save_traps=$(trap) . . . eval "$save_traps"
Листинг 8.11. Пример сохранения и восстановления способа обработки сигналов посредством специальной встроенной команды trap.
Закрыть окно




trap '$HOME/logout' EXIT
Листинг 8.12. Пример использования специальной встроенной команды trap.
Закрыть окно




trap "" PIPE echo "$INITLOG_ARGS -n $0 -s \"$1\" -e 1" >&21 trap - PIPE
Листинг 8.13. Пример использования специальной встроенной команды trap для защиты от ошибок, специфичных для каналов.
Закрыть окно




#include <signal.h> int sigemptyset (sigset_t *set); int sigfillset (sigset_t *set); int sigaddset (sigset_t *set, int signo); int sigdelset (sigset_t *set, int signo); int sigismember (const sigset_t *set, int signo);
Листинг 8.14. Описание функций для работы с наборами сигналов.
Закрыть окно




#include <signal.h> int sigprocmask ( int how, const sigset_t *restrict set, sigset_t *restrict oset);
Листинг 8.15. Описание функции sigprocmask().
Закрыть окно




#include <signal.h> int sigpending (sigset_t *set);
Листинг 8.16. Описание функции sigpending().
Закрыть окно




#include <signal.h> int sigwait (const sigset_t * restrict set, int *restrict sig);
Листинг 8.17. Описание функции sigwait().
Закрыть окно




#include <unistd.h> int pause (void);
Листинг 8.18. Описание функции pause().
Закрыть окно




#include <signal.h> int sigsuspend (const sigset_t *sigmask);
Листинг 8.19. Описание функции sigsuspend().
Закрыть окно




#include <unistd.h> #include <signal.h> #include <stdio.h>
void abort (void) { struct sigaction sact; sigset_t sset;
/* Вытолкнем буфера */ (void) fflush (NULL);
/* Снимем блокировку сигнала SIGABRT */ if ((sigemptyset (&sset) == 0) && (sigaddset (&sset, SIGABRT) == 0)) { (void) sigprocmask (SIG_UNBLOCK, &sset, (sigset_t *) NULL); }
/* Пошлем себе сигнал SIGABRT. */ /* Возможно, его перехватит функция обработки, */ /* и тогда вызывающий процесс может не завершиться */ raise (SIGABRT);
/* Установим подразумеваемую реакцию на сигнал SIGABRT */ sact.sa_handler = SIG_DFL; sigfillset (&sact.sa_mask); sact.sa_flags = 0; (void) sigaction (SIGABRT, &sact, NULL);
/* Снова пошлем себе сигнал SIGABRT */ raise (SIGABRT);
/* Если сигнал снова не помог, попробуем еще одно средство завершения */ _exit (127); }
int main (void) { printf ("Перед вызовом abort()\n"); abort (); printf ("После вызова abort()\n"); return 0; }
Листинг 8.20. Упрощенная реализация функции abort() как пример использования функций работы с сигналами.
Закрыть окно




#include <unistd.h> #include <stdio.h> #include <signal.h> #include <time.h>
/* Функция обработки сигнала SIGALRM. */ /* Она ничего не делает, но игнорировать сигнал нельзя */ static void signal_handler (int sig) { /* В демонстрационных целях распечатаем номер обрабатываемого сигнала */ printf ("Принят сигнал %d\n", sig); }
/* Функция для "засыпания" на заданное число секунд */ /* Результат равен разности между заказанной и фактической */ /* продолжительностью "сна" */ unsigned int sleep (unsigned int seconds) { time_t before, after; unsigned int slept; sigset_t set, oset; struct sigaction act, oact;
if (seconds == 0) { return 0; }
/* Установим будильник на заданное время, */ /* но перед этим блокируем сигнал SIGALRM */ /* и зададим свою функцию обработки для него */ if ((sigemptyset (&set) < 0) || (sigaddset (&set, SIGALRM) < 0) || sigprocmask (SIG_BLOCK, &set, &oset)) { return seconds; }
act.sa_handler = signal_handler; act.sa_flags = 0; act.sa_mask = oset; if (sigaction (SIGALRM, &act, &oact) < 0) { return seconds; }
before = time ((time_t *) NULL); (void) alarm (seconds);
/* Как атомарное действие восстановим старую маску сигналов */ /* (в надежде, что она не блокирует SIGALRM) */ /* и станем ждать доставки обрабатываемого сигнала */ (void) sigsuspend (&oset); /* сигнал доставлен и обработан */
after = time ((time_t *) NULL);
/* Восстановим прежний способ обработки сигнала SIGALRM */ (void) sigaction (SIGALRM, &oact, (struct sigaction *) NULL);
/* Восстановим первоначальную маску сигналов */ (void) sigprocmask (SIG_SETMASK, &oset, (sigset_t *) NULL);
return ((slept = after - before) > seconds ? 0 : (seconds - slept)); }
int main (void) { struct sigaction act;
/* В демонстрационных целях установим обработку прерывания с клавиатуры */ act.sa_handler = signal_handler; (void) sigemptyset (&act.sa_mask); act.sa_flags = 0; (void) sigaction (SIGINT, &act, (struct sigaction *) NULL);
printf ("Заснем на 10 секунд\n"); printf ("Проснулись, не доспав %d секунд\n", sleep (10)); return (0); }
Листинг 8.21. Упрощенная реализация функции sleep() как пример использования механизма сигналов.
Закрыть окно




#include <sys/ipc.h> key_t ftok (const char *path, int id);
Листинг 8.22. Описание функции ftok().
Закрыть окно




#include <sys/msg.h> int msgget (key_t key, int msgflg); int msgsnd (int msqid, const void *msgp, size_t msgsz, int msgflg); ssize_t msgrcv (int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); int msgctl (int msqid, int cmd, struct msqid_ds *buf);
Листинг 8.23. Описание функций для работы с очередями сообщений.
Закрыть окно




#include <stdio.h> #include <sys/ipc.h> #include <sys/msg.h>
/* Программа создает очередь сообщений. */ /* В командной строке задаются имя файла для ftok() */ /* и режим доступа к очереди сообщений */
#define FTOK_CHAR 'G'
int main (int argc, char *argv []) { key_t key; int msqid; int mode = 0;
if (argc != 3) { fprintf (stderr, "Использование: %s маршрутное_имя режим_доступа\n", argv [0]); return (1); }
if ((key = ftok (argv [1], FTOK_CHAR)) == (key_t) (-1)) { perror ("FTOK"); return (2); } (void) sscanf (argv [2], "%o", (unsigned int *) &mode);
if ((msqid = msgget (key, IPC_CREAT | mode)) < 0) { perror ("MSGGET"); return (3); }
return 0; }
Листинг 8.24. Пример программы, создающей очередь сообщений.
Закрыть окно




------ Message Queues -------- key msqid owner perms used- bytes messages 0x47034bac 163840 galat 644 0 0
Листинг 8.25. Возможный результат опроса статуса очередей сообщений.
Закрыть окно




struct msgbuf { long mtype; /* Тип сообщения */ char mtext [1]; /* Текст сообщения */ };
Листинг 8.26. Описание структурного типа для представления сообщений.
Закрыть окно




#define MAXSZTMSG 8192
struct mymsgbuf { long mtype; /* Тип сообщения */ char mtext [MAXSZTMSG]; /* Текст сообщения */ }; struct mymsgbuf msgbuf;
Листинг 8.27. Описание структуры для хранения сообщений.
Закрыть окно




#include <stdio.h> #include <sys/msg.h>
int main (int argc, char *argv []) { int msqid; struct msqid_ds msqid_ds;
if (argc != 3) { fprintf (stderr, "Использование: %s идентификатор_очереди максимальный_размер\n", argv [0]); return (1); }
(void) sscanf (argv [1], "%d", &msqid);
/* Получим исходное значение структуры данных */ if (msgctl (msqid, IPC_STAT, &msqid_ds) == -1) { perror ("IPC_STAT-1"); return (2); } printf ("Максимальный размер очереди до изменения: %ld\n", msqid_ds.msg_qbytes);
(void) sscanf (argv [2], "%d", (int *) &msqid_ds.msg_qbytes);
/* Попробуем внести изменения */ if (msgctl (msqid, IPC_SET, &msqid_ds) == -1) { perror ("IPC_SET"); }
/* Получим новое значение структуры данных */ if (msgctl (msqid, IPC_STAT, &msqid_ds) == -1) { perror ("IPC_STAT-2"); return (3); } printf ("Максимальный размер очереди после изменения: %ld\n", msqid_ds.msg_qbytes);
return 0; }
Листинг 8.28. Пример программы управления очередями сообщений.
Закрыть окно




#include <unistd.h> #include <stdio.h> #include <limits.h> #include <string.h> #include <sys/wait.h> #include <sys/msg.h>
/* Программа копирует строки со стандартного ввода на стандартный вывод, */ /* "прокачивая" их через очередь сообщений */
#define FTOK_FILE "/home/galat" #define FTOK_CHAR "G"
#define MSGQ_MODE 0644
#define MY_PROMPT "Вводите строки\n" #define MY_MSG "Вы ввели: "
int main (void) { key_t key; int msqid; struct mymsgbuf { long mtype; char mtext [LINE_MAX]; } line_buf, msgbuf;
switch (fork ()) { case -1: perror ("FORK"); return (1); case 0: /* Чтение из очереди и выдачу на стандартный вывод */ /* реализуем в порожденном процессе. */ (void) execl ("./msq_child", "msq_child", FTOK_FILE, FTOK_CHAR, (char *) 0); perror ("EXEC"); return (2); /* execl() завершился неудачей */ }
/* Чтение со стандартного ввода и запись в очередь */ /* возложим на родительский процесс */
/* Выработаем ключ для очереди сообщений */ if ((key = ftok (FTOK_FILE, FTOK_CHAR [0])) == (key_t) (-1)) { perror ("FTOK"); return (3); }
/* Получим идентификатор очереди сообщений */ if ((msqid = msgget (key, IPC_CREAT | MSGQ_MODE)) < 0) { perror ("MSGGET"); return (4); }
/* Приступим к отправке сообщений в очередь */ msgbuf.mtype = line_buf.mtype = 1; strncpy (msgbuf.mtext, MY_PROMPT, sizeof (msgbuf.mtext)); if (msgsnd (msqid, (void *) &msgbuf, strlen (msgbuf.mtext) + 1, 0) != 0) { perror ("MSGSND-1"); return (5); } strncpy (msgbuf.mtext, MY_MSG, sizeof (msgbuf.mtext));
while (fgets (line_buf.mtext, sizeof (line_buf.mtext), stdin) != NULL) { if (msgsnd (msqid, (void *) &msgbuf, strlen (msgbuf.mtext) + 1, 0) != 0) { perror ("MSGSND-2"); break; } if (msgsnd (msqid, (void *) &line_buf, strlen (line_buf.mtext) + 1, 0) != 0) { perror ("MSGSND-3"); break; } }
/* Удалим очередь */ if (msgctl (msqid, IPC_RMID, NULL) == -1) { perror ("MSGCTL-IPC_RMID"); return (6); }
return (0); }
Листинг 8.29. Передающая часть программы работы с очередями сообщений.
Закрыть окно




#include <stdio.h> #include <limits.h> #include <sys/msg.h>
/* Программа получает сообщения из очереди */ /* и копирует их тела на стандартный вывод */
#define MSGQ_MODE 0644
int main (int argc, char *argv []) { key_t key; int msqid; struct mymsgbuf { long mtype; char mtext [LINE_MAX]; } msgbuf;
if (argc != 3) { fprintf (stderr, "Использование: %s имя_файла цепочка_символов\n", argv [0]); return (1); }
/* Выработаем ключ для очереди сообщений */ if ((key = ftok (argv [1], *argv [2])) == (key_t) (-1)) { perror ("CHILD FTOK"); return (2); }
/* Получим идентификатор очереди сообщений */ if ((msqid = msgget (key, IPC_CREAT | MSGQ_MODE)) < 0) { perror ("CHILD MSGGET"); return (3); }
/* Цикл приема сообщений и выдачи строк */ while (msgrcv (msqid, (void *) &msgbuf, sizeof (msgbuf.mtext), 0, 0) > 0) { if (fputs (msgbuf.mtext, stdout) == EOF) { break; } }
return 0; }
Листинг 8.30. Приемная часть программы работы с очередями сообщений.
Закрыть окно




#include <sys/sem.h> int semget ( key_t key, int nsems, int semflg); int semop (int semid, struct sembuf *sops, size_t nsops); int semctl (int semid, int semnum, int cmd, ...);
Листинг 8.31. Описание функций для работы с семафорами.
Закрыть окно




sembuf [0].sem_num = 1; sembuf [0].sem_flg = 0; sembuf [0].sem_op = -2;
sembuf [1].sem_num = 0; sembuf [1].sem_flg = IPC_NOWAIT; sembuf [1].sem_op = 0;
Листинг 8.32. Пример задания массива операций над семафорами.
Закрыть окно




union semun { int val; struct semid_ds *buf; unsigned short *array; } arg;
Листинг 8.33. Описание четвертого (дополнительного) аргумента функции semctl().
Закрыть окно




val = semctl (semid, semnum, GETVAL);
arg.val = ...; if (semctl (semid, semnum, SETVAL, arg) == -1) ...;
arg.array = ( unsigned short *) malloc (nsems * sizeof (unsigned short)); err = semctl (semid, 0, GETALL, arg);
for (i = 0; i < nsems; i++) arg.array [i] = ...; err = semctl (semid, 0, SETALL, arg);
lpid = semctl (semid, semnum, GETPID);
ncnt = semctl (semid, semnum, GETNCNT);
zcnt = semctl (semid, semnum, GETZCNT);
Листинг 8.34. Примеры управляющих действий над семафорами.
Закрыть окно




arg.buf = (struct semid_ds *) malloc (sizeof (struct semid_ds); err = semctl (semid, 0, IPC_STAT, arg); arg.buf->sem_perm.mode = 0644; err = semctl (semid, 0, IPC_SET, arg);
Листинг 8.35. Дополнительные примеры управляющих действий над семафорами.
Закрыть окно




#include <unistd.h> #include <stdio.h> #include <sys/sem.h> #include <sys/wait.h>
/* Программа-монитор обеда философов */
#define QPH 5
#define ARG_SIZE 20
int main (void) { int key; /* Ключ набора семафоров */ int semid; /* Идентификатор набора семафоров */ int no; /* Номер философа и/или вилки */ char ssemid [ARG_SIZE], sno [ARG_SIZE], sqph [ARG_SIZE];
/* Создание и инициализация набора семафоров */ /* (по семафору на вилку) */ key = ftok ("phdin.c", 'C'); if ((semid = semget (key, QPH, 0600 | IPC_CREAT)) < 0) { perror ("SEMGET"); return (1); } for (no = 0; no < QPH; no++) { if (semctl (semid, no, SETVAL, 1) < 0) { perror ("SETVAL"); return (2); } }
sprintf (ssemid, "%d", semid); sprintf (sqph, "%d", QPH);
/* Все - к столу */ for (no = 1; no <= QPH; no++) { switch (fork ()) { case -1: perror ("FORK"); return (3); case 0: sprintf (sno, "%d", no); execl ("./phil", "phil", ssemid, sqph, sno, (char *) 0); perror ("EXEC"); return (4); } }
/* Ожидание завершения обеда */ for (no = 1; no <= QPH; no++) { (void) wait (NULL); }
/* Удаление набора семафоров */ if (semctl (semid, 0, IPC_RMID) < 0) { perror ("SEMCTL"); return (5); }
return 0; }
Листинг 8.36. Процесс-монитор для обеда философов.
Закрыть окно




#include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/sem.h>
/* Процесс обеда одного философа */
#define ernd (rand () % 3 + 1) #define trnd (rand () % 5 + 1) #define FO 15
int main (int argc, char *argv []) { int semid; /* Идентификатор набора семафоров */ int qph; /* Число философов */ int no; /* Номер философа */ int t; /* Время очередного отрезка еды или беседы */ int fo; /* Время до конца обеда */ struct sembuf sembuf [2];
if (argc != 4) { fprintf (stderr, "Использование: %s идентификатор_набора_семафоров число_философов номер_философа \n", argv [0]); return (1); }
fo = FO; sscanf (argv [1], "%d", &semid); sscanf (argv [2], "%d", &qph); sscanf (argv [3], "%d", &no);
/* Выбор вилок */ sembuf [0].sem_num = no - 1; /* Левая */ sembuf [0].sem_flg = 0; sembuf [1].sem_num = no % qph; /* Правая */ sembuf [1].sem_flg = 0;
while (fo > 0) { /* Обед */
/* Философ говорит */ printf ("Философ %d беседует\n", no); t = trnd; sleep (t); fo -= t; /* Пытается взять вилки */ sembuf [0].sem_op = -1; sembuf [1].sem_op = -1; if (semop (semid, sembuf, 2) < 0) { perror ("SEMOP"); return (1); }
/* Ест */ printf ("Философ %d ест\n", no); t = ernd; sleep (t); fo -= t; /* Отдает вилки */ sembuf [0].sem_op = 1; sembuf [1].sem_op = 1; if (semop (semid, sembuf, 2) < 0) { perror ("SEMOP"); return (2); } }
printf ("Философ %d закончил обед\n", no); return 0; }
Листинг 8.37. Программа, описывающая обед одного философа.
Закрыть окно




/* Обедающие философы. Запуск: mudrecProc [-a | -p | -I -V] [-t число_секунд] имя_философа ... Опции: -t число_секунд - сколько секунд моделируется Стратегии захвата вилок: -a - сначала захватывается вилка с меньшим номером; -I - некорректная (но эффективная) интеллигентная стратегия: во время ожидания уже захваченная вилка кладется; -p - сначала захватывается нечетная вилка; -V - использован групповой захват семафоров. Пример запуска: mudrecProc -p -t 600 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z */ static char rcsid[] __attribute__((unused)) = \ "$Id: mudrecProc.c,v 1.7 2003/11/11 13:14:07 sambor Exp $";
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <fcntl.h> #include <time.h> #include <limits.h> #include <errno.h>
#include <sys/sem.h> #include <sys/msg.h>
union semun { int val; struct semid_ds *buf; unsigned short *array; } arg;
#define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)>(b)?(b):(a))
struct mudrec { long num; char *name; int left_fork, right_fork; int eat_time, wait_time, think_time, max_wait_time; int count; };
int Stop; /* Семафор для синхронизации выхода */
/* Различные дескрипторы */ int protokol [2] = {-1, -1}; #define pFdIn (protokol [1]) #define pFdOut (protokol [0])
int semFork; /* Вилки */
int from_fil; /* Очередь для возврата результатов */
/* Разные алгоритмы захвата вилок */ static void get_forks_simple (struct mudrec *this); static void get_forks_parity (struct mudrec *this); static void get_forks_maybe_infinit_time (struct mudrec *this); static void get_forks_use_groups (struct mudrec *this);
/* Используемый метод захвата вилок */ void (*get_forks) (struct mudrec *this) = get_forks_simple;
/* Возвращение вилок */ static void put_forks (struct mudrec *this);
/* * Философы */ void filosof (struct mudrec this) { char buffer [LINE_MAX]; int bytes;
if (fork ()) return;
srandom (getpid ()); /* Очень важно для процессов, иначе получим одно и то же! */ random (); random (); random (); random (); random (); random (); random (); random (); random (); random ();
/* Пока семафор Stop не поднят */ while (!semctl (Stop, 0, GETVAL)) { /* Пора подкрепиться */ { int wait_time, tm;
sprintf (buffer, "%s: хочет есть\n", this.name); bytes = write (pFdIn, buffer, strlen (buffer));
tm = time (0);
(*get_forks) (&this);
wait_time = time (0) - tm; /* Сколько времени получали вилки */ this.wait_time += wait_time; this.max_wait_time = max (wait_time, this.max_wait_time);
sprintf (buffer, "%s: ждал вилок %d сек\n", this.name, wait_time); bytes = write (pFdIn, buffer, strlen (buffer)); }
/* Может, обед уже закончился? */ if (semctl (Stop, 0, GETVAL)) { put_forks (&this); break; }
/* Едим */ { int eat_time = random () % 20 + 1;
sleep (eat_time);
this.eat_time += eat_time; this.count++; sprintf (buffer,"%s: ел %d сек\n", this.name, eat_time); bytes = write (pFdIn, buffer, strlen (buffer)); }
/* Отдаем вилки */ put_forks (&this);
if (semctl (Stop, 0, GETVAL)) break;
/* Размышляем */ { int think_time = random () % 10 + 1;
sleep (think_time);
this.think_time += think_time; } }
sprintf (buffer,"%s: уходит\n", this.name); bytes = write (pFdIn, buffer, strlen (buffer));
msgsnd (from_fil, &this, sizeof (this), 0); /* Отослали статистику своего обеда */
_exit (0); /* ВАЖНО (_): Нам не нужны преждевременные вызовы cleanup_ipc */ }
/* Кладем вилки одну за другой */ static void put_forks (struct mudrec *this) { struct sembuf tmp_buf;
tmp_buf.sem_flg = 0; tmp_buf.sem_op = 1; tmp_buf.sem_num = this->left_fork - 1; semop (semFork, &tmp_buf, 1);
tmp_buf.sem_flg = 0; tmp_buf.sem_op = 1; tmp_buf.sem_num = this->right_fork - 1; semop (semFork, &tmp_buf, 1); }
/* Берем вилки по очереди в порядке номеров */ static void get_forks_simple (struct mudrec *this) { struct sembuf tmp_buf;
int first = min (this->left_fork, this->right_fork); int last = max (this->left_fork, this->right_fork);
tmp_buf.sem_flg = SEM_UNDO; tmp_buf.sem_op = -1; tmp_buf.sem_num = first - 1; semop (semFork, &tmp_buf, 1);
tmp_buf.sem_flg = SEM_UNDO; tmp_buf.sem_op = -1; tmp_buf.sem_num = last - 1; semop (semFork, &tmp_buf, 1); }
/* Берем сначала нечетную вилку (если обе нечетные - то с большим номером) */ static void get_forks_parity (struct mudrec *this) { struct sembuf tmp_buf;
int left = this->left_fork, right = this->right_fork; int first = max ((left & 1) * 1000 + left, (right & 1) * 1000 + right) % 1000; int last = min ((left & 1) * 1000 + left, (right & 1) * 1000 + right) % 1000;
tmp_buf.sem_flg = SEM_UNDO; tmp_buf.sem_op = -1; tmp_buf.sem_num = first - 1; semop (semFork, &tmp_buf, 1);
tmp_buf.sem_flg = SEM_UNDO; tmp_buf.sem_op = -1; tmp_buf.sem_num = last - 1; semop (semFork, &tmp_buf, 1); }
/* Берем вилки по очереди, в произвольном порядке. * Но если вторая вилка не берется сразу, то кладем первую. * То есть философ не расходует вилочное время впустую. */ static void get_forks_maybe_infinit_time (struct mudrec *this) { struct sembuf tmp_buf;
int left = this->left_fork, right = this->right_fork;
for (;;) { tmp_buf.sem_flg = SEM_UNDO; /* Первую вилку берем с ожиданием */ tmp_buf.sem_op = -1; tmp_buf.sem_num = left - 1; semop (semFork, &tmp_buf, 1);
tmp_buf.sem_flg = SEM_UNDO | IPC_NOWAIT; /* Вторую - без ожидания */ tmp_buf.sem_op = -1; tmp_buf.sem_num = right - 1;
if (0 == semop (semFork, &tmp_buf, 1)) return; /* Успех */
tmp_buf.sem_flg = 0; /* Неуспех: возвращаем первую вилку */ tmp_buf.sem_op = 1; tmp_buf.sem_num = left - 1; semop(semFork,&tmp_buf,1);
tmp_buf.sem_flg = SEM_UNDO; /* Отдав первую, ждем вторую */ tmp_buf.sem_op = -1; tmp_buf.sem_num = right - 1; semop (semFork, &tmp_buf, 1);
tmp_buf.sem_flg = SEM_UNDO | IPC_NOWAIT; /* Берем первую вилку без ожидания */ tmp_buf.sem_op = -1; tmp_buf.sem_num = left - 1;
if (0 == semop (semFork, &tmp_buf, 1)) return; /* Успех */
tmp_buf.sem_flg = 0; /* Неуспех: отдаем вторую вилку, */ tmp_buf.sem_op = 1; /* чтобы ждать первую */ tmp_buf.sem_num = right - 1; semop (semFork, &tmp_buf, 1); } }
/* Хватаем обе вилки сразу, используя групповые операции */ static void get_forks_use_groups (struct mudrec *this) { struct sembuf tmp_buf [2];
tmp_buf[0].sem_flg = SEM_UNDO; tmp_buf[0].sem_op = -1; tmp_buf[0].sem_num = this->left_fork - 1; tmp_buf[1].sem_flg = SEM_UNDO; tmp_buf[1].sem_op = -1; tmp_buf[1].sem_num = this->right_fork - 1; semop (semFork, tmp_buf, 2); }
/* * Мелкие служебные функции. */ static void stop (int dummy) { struct sembuf tmp_buf;
tmp_buf.sem_flg = 0; tmp_buf.sem_op = 1; tmp_buf.sem_num = 0; semop (Stop, &tmp_buf, 1); }
void cleanup_ipc (void) { /* * Уничтожение семафоров. */ semctl (semFork, 1, IPC_RMID); semctl (Stop, 1, IPC_RMID);
/* То же с очередью */ msgctl (from_fil, IPC_RMID, NULL); }
static void usage (char name []) { fprintf (stderr,"Использование: %s [-a | -p | -I| -V] [-t число_секунд] имя_философа ...\n", name); exit (1); }
/* * Точка входа демонстрационной программы. */ int main (int argc, char *argv[]) { char buffer [LINE_MAX], *p; int i, n, c; int open_room_time = 300; union semun tmp_arg; int nMudr; struct sigaction sact;
while ((c = getopt (argc, argv, "apIVt:")) != -1) { switch (c) { case 'a': get_forks = get_forks_simple; break; case 'p': get_forks = get_forks_parity; break; case 'I': get_forks = get_forks_maybe_infinit_time; break; case 'V': get_forks = get_forks_use_groups; break; case 't': open_room_time = strtol (optarg, &p, 0); if (optarg [0] == 0 || *p != 0) usage (argv [0]); break; default: usage (argv [0]); } }
nMudr = argc - optind; if (nMudr < 2) usage (argv [0]); /* Меньше двух философов неинтересно ... */
/* * Создание канала для протокола обработки событий */ pipe (protokol);
/* * Создадим семафоры для охраны вилок */ semFork = semget (ftok (argv [0], 2), nMudr, IPC_CREAT | 0777); tmp_arg.val = 1; for (i=1; i <= nMudr; i++) semctl (semFork, i - 1, SETVAL, tmp_arg); /* Начальное значение 1 */
/* Прежде чем впускать философов, обеспечим окончание обеда */ Stop = semget (ftok (argv [0], 3), 1, IPC_CREAT | 0777); tmp_arg.val = 0; semctl (Stop, 0, SETVAL, tmp_arg); /* Начальное значение 0 */
/* Очередь для возврата результатов */ from_fil = msgget (ftok (argv [0], 4), IPC_CREAT | 0777);
atexit (cleanup_ipc); /* Запланировали уничтожение семафоров */ /* и других средств межпроцессного взаимодействия */
/* * Философы входят в столовую */ for (i = 0; i < nMudr; i++, optind++) { struct mudrec next;
memset (&next, 0, sizeof (next));
next.num = i + 1; /* Номер */ next.name = argv [optind]; /* Имя */
/* Указали, какими вилками пользоваться */ next.left_fork = i + 1; next.right_fork = i + 2; if (i == nMudr - 1) next.right_fork = 1; /* Последний пользуется вилкой первого */
filosof (next); }
/* Зададим реакцию на сигналы и установим будильник на конец обеда */ sact.sa_handler = stop; (void) sigemptyset (&sact.sa_mask); sact.sa_flags = 0; (void) sigaction (SIGINT, &sact, (struct sigaction *) NULL); (void) sigaction (SIGALRM, &sact, (struct sigaction *) NULL);
alarm (open_room_time);
/* * Выдача сообщений на стандартный вывод и выход после окончания обеда. */ close (pFdIn); /* Сами должны закрыть, иначе из цикла не выйдем! */ for (;;) { n = read (pFdOut, buffer, LINE_MAX); if ((n == 0) || ((n == -1) && (errno != EINTR))) break; for (i = 0; i < n; i++) putchar (buffer [i]); } close (pFdOut);
/* Распечатали сводную информацию */ { int full_eating_time = 0; int full_waiting_time = 0; int full_thinking_time = 0; for (i = 1; i <= nMudr; i++) { struct mudrec this; /* Получили статистику обеда */ msgrcv (from_fil, &this, sizeof (this), i, 0); /* За счет i получаем */ /* строго по порядку */ full_eating_time += this.eat_time; full_waiting_time += this.wait_time; full_thinking_time += this.think_time;
if (this.count > 0) { float count = this.count; float think_time = this.think_time / count; float eat_time = this.eat_time / count; float wait_time = this.wait_time / count;
printf ("%s: ел %d раз в среднем: думал=%.1f ел=%.1f ждал=%.1f (максимум %d)\n", this.name, this.count, think_time, eat_time, wait_time, this.max_wait_time); } else printf("%s: не поел\n", this.name); } { float total_time = (full_eating_time + full_waiting_time + full_thinking_time) / (float)nMudr;
printf (" Среднее число одновременно едящих = %.3f\n Среднее число одновременно ждущих = %.3f\n", full_eating_time / total_time, full_waiting_time / total_time); } }
/* Сообщим об окончании работы */ printf ("Конец обеда\n");
return 0; }
Листинг 8.38. Второй вариант решения задачи об обедающих философах.
Закрыть окно




-a: A: ел 2 раза в среднем: думал=3.5 ел=11.5 ждал=36.5 (максимум 73) B: ел 3 раза в среднем: думал=5.7 ел=7.7 ждал=20.0 (максимум 41) C: ел 3 раза в среднем: думал=5.7 ел=11.3 ждал=17.0 (максимум 33) D: ел 3 раза в среднем: думал=1.7 ел=16.7 ждал=15.7 (максимум 19) E: ел 1 раз в среднем: думал=10.0 ел=20.0 ждал=73.0 ( максимум 41) Среднее число одновременно едящих = 1.471 Среднее число одновременно ждущих = 2.980 -p: A: ел 3 раза в среднем: думал=3.7 ел=15.3 ждал=16.0 (максимум 34) B: ел 4 раза в среднем: думал=5.0 ел=13.8 ждал=8.2 (максимум 15) C: ел 3 раза в среднем: думал=6.7 ел=3.7 ждал=25.7 (максимум 27) D: ел 4 раза в среднем: думал=5.8 ел=8.5 ждал=13.8 (максимум 28) E: ел 3 раза в среднем: думал=5.3 ел=15.3 ждал=16.7 (максимум 29) Среднее число одновременно едящих = 1.761 Среднее число одновременно ждущих = 2.413
-I: A: ел 5 раз в среднем: думал=4.2 ел=9.4 ждал=6.6 (максимум 15) B: ел 3 раза в среднем: думал=6.3 ел=10.3 ждал=17.0 (максимум 31) C: ел 4 раза в среднем: думал=6.8 ел=7.0 ждал=12.2 (максимум 45) D: ел 3 раза в среднем: думал=4.3 ел=16.0 ждал=13.0 (максимум 16) E: ел 4 раза в среднем: думал=5.8 ел=8.5 ждал=10.8 (максимум 22) Среднее число одновременно едящих = 1.858 Среднее число одновременно ждущих = 2.125
-V: A: ел 5 раз в среднем: думал=5.6 ел=5.6 ждал=8.8 (максимум 17) B: ел 3 раза в среднем: думал=6.3 ел=10.3 ждал=16.7 (максимум 20) C: ел 4 раза в среднем: думал=4.8 ел=11.0 ждал=9.8 (максимум 18) D: ел 4 раза в среднем: думал=5.2 ел=12.0 ждал=8.8 (максимум 15) E: ел 4 раза в среднем: думал=5.2 ел=10.5 ждал=10.2 (максимум 20) Среднее число одновременно едящих = 1.892 Среднее число одновременно ждущих = 2.049
Листинг 8.39. Результаты моделирования поведения философов.
Закрыть окно




#include <sys/shm.h> int shmget ( key_t key, size_t size, int shmflg); void *shmat (int shmid, const void *shmaddr, int shmflg); int shmdt (const void *shmaddr); int shmctl (int shmid, int cmd, struct shmid_ds *buf);
Листинг 8.40. Описание функций для работы с разделяемыми сегментами памяти.
Закрыть окно




#include <unistd.h> #include <stdio.h> #include <sys/shm.h> #include <sys/sem.h> #include <sys/wait.h>
int main (void) { struct region { pid_t fpid; } *shm_ptr;
struct sembuf P = {0, -1, 0}; struct sembuf V = {0, 1, 0};
int shmid; int semid;
shmid = shmget (IPC_PRIVATE, sizeof (struct region), 0777); semid = semget (IPC_PRIVATE, 1, 0777); (void) semctl (semid, 0, SETVAL, 1);
switch (fork ()) { case -1: perror ("FORK"); return (1); case 0: if ((int) (shm_ptr = (struct region *) shmat (shmid, NULL, 0)) == (-1)) { perror ("CHILD-SHMAT"); return (2); }
if (semop (semid, &p, 1) != 0) { perror ("CHILD-SEMOP-P"); return (3); } printf ("Процесс-потомок вошел в критический интервал\n");
shm_ptr->fpid = getpid (); /* Монопольный доступ */
printf ("Процесс- потомок перед выходом из критического интервала\n"); if (semop (semid, &V, 1) != 0) { perror ("CHILD-SEMOP-V"); return (4); }
(void) shmdt (shm_ptr); return 0; }
if ((int) (shm_ptr = (struct region *) shmat (shmid, NULL, 0)) == (-1)) { perror ("PARENT-SHMAT"); return (2); }
if (semop (semid, &p, 1) != 0) { perror ("PARENT-SEMOP-P"); return (3); } printf ("Родительский процесс вошел в критический интервал\n");
shm_ptr->fpid = getpid (); /* Монопольный доступ */
printf ("Родительский процесс перед выходом из критического интервала\n"); if (semop (semid, &V, 1) != 0) { perror ("PARENT-SEMOP-V"); return (4); }
(void) wait (NULL);
printf ("Идентификатор родительского процесса: %d\n", getpid ()); printf ("Идентификатор процесса в разделяемой структуре: %d\n", shm_ptr->fpid);
(void) shmdt (shm_ptr);
(void) semctl (semid, 1, IPC_RMID); (void) shmctl (shmid, IPC_RMID, NULL);
return 0; }
Листинг 8.41. Пример работы с разделяемыми сегментами памяти.
Закрыть окно




Родительский процесс вошел в критический интервал Родительский процесс перед выходом из критического интервала Процесс-потомок вошел в критический интервал Процесс-потомок перед выходом из критического интервала Идентификатор родительского процесса: 2161 Идентификатор процесса в разделяемой структуре: 2162
Листинг 8.42. Возможный результат синхронизации доступа к разделяемым данным.
Закрыть окно




/* * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Реализация "виртуальной" памяти из одного сегмента. */ /* Используются разделяемые сегменты памяти */ /* и обработка сигнала SIGSEGV */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <stdlib.h> #include <stdio.h> #include <sys/stat.h> #include <sys/shm.h> #include <signal.h>
/* Константа, зависящая от реализации */ #define SHM_BASE_ADDR 0x40014000
static int shm_id = -1; static void *shm_addr;
/* Реакция на сигнал SIGSEGV. */ /* Создаем и присоединяем на чтение разделяемый сегмент, */ /* накрывающий переданный адрес. */ /* Если это не помогло, переприсоединяем сегмент на запись */ static void sigsegv_sigaction (int sig, siginfo_t *sig_info, void *addr) { struct shmid_ds shmid_ds;
if (shm_id == -1) { /* Сегмента еще нет. Создадим */ if ((shm_id = shmget (IPC_PRIVATE, SHMLBA, S_IRUSR)) == -1) { perror ("SHMGET"); exit (1); } /* Присоединим сегмент на чтение */ if ((int) (shm_addr = shmat (shm_id, sig_info->si_addr, SHM_RDONLY | SHM_RND)) == (-1)) { perror ("SHMAT-RDONLY"); exit (2); } return; } else { /* Сегмент уже есть, но обращение по адресу вызвало сигнал SIGSEGV. */ /* Значит, это была попытка записи, и сегмент нужно */ /* переприсоединить на запись, поменяв соответственно режим доступа */ if (shmctl (shm_id, IPC_STAT, &shmid_ds) == -1) { perror ("SHMCTL-IPC_STAT"); exit (3); } shmid_ds.shm_perm.mode |= S_IWUSR; if (shmctl (shm_id, IPC_SET, &shmid_ds) == -1) { perror ("SHMCTL-IPC_SET"); exit (4); } (void) shmdt (shm_addr); if (shmat (shm_id, shm_addr, 0) != shm_addr) { perror ("SHMAT-RDWD"); exit (5); } } }
int main (void) { char *test_ptr; struct sigaction sact;
/* Установим реакцию на сигнал SIGSEGV */ (void) sigemptyset (&sact.sa_mask); sact.sa_flags = SA_SIGINFO; sact.sa_sigaction = sigsegv_sigaction; (void) sigaction (SIGSEGV, &sact, (struct sigaction *) NULL);
/* Убедимся, что разделяемые сегменты инициализируются нулями */ test_ptr = (char *) (SHM_BASE_ADDR + 3); printf ("Результат попытки чтения до записи: %x\n", *test_ptr);
/* Попробуем записать */ *test_ptr = 'A'; printf ("Результат попытки чтения после записи: %x\n", *test_ptr);
return (shmctl (shm_id, IPC_RMID, NULL)); }
Листинг 8.43. Пример работы с разделяемыми сегментами памяти и сигналами.
Закрыть окно



Содержание раздела