Малый бизнес Идеи малого бизнеса Теория Малого бизнеса Рост малого бизнеса Малый бизнес - Основы Советы по малому бизнесу Малый бизнес - виды Малый бизнес - Гостиницы Малый бизнес - Торговля Малый бизнес - Транспорт Малый бизнес - Безопасность Малый бизнес - Питание Малый бизнес - Финансы Малый бизнес - Франчайзинг Малый бизнес - организация Малый бизнес - Начало Малый бизнес - Налоги Малый бизнес - Организация Малый бизнес - Развитие Малый бизнес - Рынок Малый бизнес - Семья Русский малый бизнес Малый бизнес - Россия Малый бизнес и государство Малый бизнес и рынок Конкуренция в малом бизнесе Консалтинг в малом бизнесе Маркетинг в малом бизнесе Менеджмент в малом бизнесе Малый бизнес - экономика Управление малым бизнесом Экономика малого бизнеса Малый бизнес - пакет Posix Малый бизнес в телефонии Малый бизнес - телефония Малый бизнес - телефония на Java |
|
|
#include |
| Листинг 1.1. Описание функции pthread_self(). |
| Закрыть окно |
|
#include |
| Листинг 1.2. Описание функции pthread_equal(). |
| Закрыть окно |
|
#include int pthread_attr_init ( pthread_attr_t *attr); int pthread_attr_destroy ( pthread_attr_t *attr); |
| Листинг 1.3. Описание функций pthread_attr_init() и pthread_attr_destroy(). |
| Закрыть окно |
|
#include int pthread_attr_getstack ( const pthread_attr_t * restrict attr, void **restrict stackaddr, size_t *restrict stacksize); int pthread_attr_setstack ( pthread_attr_t *attr, void *stackaddr, size_t stacksize); |
| Листинг 1.4. Описание функций pthread_attr_getstack() и pthread_attr_setstack(). |
| Закрыть окно |
|
#include int pthread_attr_getguardsize ( const pthread_attr_t * restrict attr, size_t *restrict guardsize); int pthread_attr_setguardsize ( pthread_attr_t *attr, size_t guardsize); |
| Листинг 1.5. Описание функций pthread_attr_getguardsize() и pthread_attr_setguardsize(). |
| Закрыть окно |
|
#include int pthread_attr_getschedparam ( const pthread_attr_t *restrict attr, struct sched_param *restrict param); int pthread_attr_setschedparam ( pthread_attr_t * restrict attr, const struct sched_param *restrict param); |
| Листинг 1.6. Описание функций pthread_attr_getschedparam() и pthread_attr_setschedparam(). |
| Закрыть окно |
|
#include int pthread_attr_getschedpolicy ( const pthread_attr_t * restrict attr, int *restrict policy); int pthread_attr_setschedpolicy ( pthread_attr_t *attr, int policy); |
| Листинг 1.7. Описание функций pthread_attr_getschedpolicy() и pthread_attr_setschedpolicy(). |
| Закрыть окно |
|
#include int pthread_attr_getscope ( const pthread_attr_t * restrict attr, int *restrict contentionscope); int pthread_attr_setscope ( pthread_attr_t *attr, int contentionscope); |
| Листинг 1.8. Описание функций pthread_attr_getscope() и pthread_attr_setscope(). |
| Закрыть окно |
|
#include int pthread_attr_getinheritsched ( const pthread_attr_t * restrict attr, int *restrict inheritsched); int pthread_attr_setinheritsched ( pthread_attr_t *attr, int inheritsched); |
| Листинг 1.9. Описание функций pthread_attr_getinheritsched() и pthread_attr_setinheritsched(). |
| Закрыть окно |
|
#include int pthread_attr_getdetachstate ( const pthread_attr_t *attr, int *detachstate); int pthread_attr_setdetachstate ( pthread_attr_t *attr, int detachstate); |
| Листинг 1.10. Описание функций pthread_attr_getdetachstate() и pthread_attr_setdetachstate(). |
| Закрыть окно |
|
#include int pthread_getschedparam ( pthread_t thread, int *restrict policy, struct sched_param *restrict param); int pthread_setschedparam ( pthread_t thread, int policy, const struct sched_param *param); |
| Листинг 1.11. Описание функций pthread_getschedparam() и pthread_setschedparam(). |
| Закрыть окно |
|
#include |
| Листинг 1.12. Описание функции pthread_setschedprio(). |
| Закрыть окно |
|
#include int pthread_getconcurrency (void); int pthread_setconcurrency (int new_level); |
| Листинг 1.13. Описание функций pthread_getconcurrency() и pthread_setconcurrency(). |
| Закрыть окно |
|
#include int pthread_getcpuclockid ( pthread_t thread_id, clockid_t *clock_id); |
| Листинг 1.14. Описание функции pthread_getcpuclockid(). |
| Закрыть окно |
|
#include |
| Листинг 1.15. Описание функции pthread_sigmask(). |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Программа опрашивает атрибуты потоков управления */ /* и изменяет некоторые из них */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ #define _XOPEN_SOURCE 600 #include int main (void) { pthread_t ct_id; /* Идентификатор текущего потока управления */ pthread_attr_t patob; /* Атрибутный объект для создания потоков управления */ int res; /* Переменная для запоминания результатов "потоковых" функций */ void *stackaddr; /* Начало стека как атрибут потока управления */ size_t atrsize; /* Размеры как атрибуты потока управления */ /* Структура с параметрами планирования */ struct sched_param shdprm; char *spname; /* Названия политики планирования, области */ /* планирования конкуренции и т.п. */ printf ("Идентификатор текущего потока управления: %lx\n", (ct_id = pthread_self ())); if ((errno = pthread_attr_init (&patob)) != 0) { perror ("PTHREAD_ATTR_INIT"); return (errno); } printf ("Значения, установленные системой " "в атрибутном объекте\n"); if ((errno = pthread_attr_getstack (&patob, &stackaddr, &atrsize)) != 0) { perror ("PTHREAD_ATTR_GETSTACK"); return (errno); } printf ("Адрес начала стека: %p\n", stackaddr); printf ("Размер стека: %d\n", atrsize); assert (pthread_attr_getguardsize (&patob, &atrsize) == 0); printf ("Размер защитной области: %d\n", atrsize); assert (pthread_attr_getschedparam (&patob, &shdprm) == 0); assert (pthread_attr_getschedpolicy (&patob, &res) == 0); switch (res) { case SCHED_FIFO: spname = "Планирование по очереди"; break; case SCHED_RR: spname = "Циклическое планирование"; break; case SCHED_OTHER: spname = "Прочее планирование"; break; default: spname = "Неизвестная политика планирования"; } printf ("Политика планирования: %s\n", spname); printf ("Приоритет планирования: %d\n", shdprm.sched_priority); assert (pthread_attr_getscope (&patob, &res) == 0); switch (res) { case PTHREAD_SCOPE_SYSTEM: spname = "Система"; break; case PTHREAD_SCOPE_PROCESS: spname = "Процесс"; break; default: spname = "Неизвестная область планирования " "конкуренции"; } printf ("Область планирования конкуренции: %s\n", spname); assert (pthread_attr_getinheritsched (&patob, &res) == 0); switch (res) { case PTHREAD_INHERIT_SCHED: spname = "Наследуются у родительского потока"; break; case PTHREAD_EXPLICIT_SCHED: spname = "Извлекаются из атрибутного объекта"; break; default: spname = "Устанавливаются неизвестным образом"; } printf ("Атрибуты планирования: %s\n", spname); assert (pthread_attr_getdetachstate (&patob, &res) == 0); switch (res) { case PTHREAD_CREATE_JOINABLE: spname = "Присоединяемые"; break; case PTHREAD_CREATE_DETACHED: spname = "Обособленные"; break; default: spname = "Неизвестные"; } printf ("Потоки управления создаются как: %s\n", spname); /* Изменим значения атрибутов планирования и уровня */ /* параллелизма */ shdprm.sched_priority = 1; if ((errno = pthread_setschedparam (ct_id, SCHED_RR, &shdprm)) != 0) { perror ("PTHREAD_SETSCHEDPARAM"); } if ((errno = pthread_setconcurrency (8192)) != 0) { perror ("PTHREAD_SETCONCURRENCY"); } printf ("\nТекущие значения атрибутов потоков управления\n"); assert (pthread_getschedparam (ct_id, &res, &shdprm) == 0); switch (res) { case SCHED_FIFO: spname = "Планирование по очереди"; break; case SCHED_RR: spname = "Циклическое планирование"; break; case SCHED_OTHER: spname = "Прочее планирование"; break; default: spname = "Неизвестная политика планирования"; } printf ("Политика планирования: %s\n", spname); printf ("Приоритет планирования: %d\n", shdprm.sched_priority); printf ("Уровень параллелизма: %d\n", pthread_getconcurrency()); return 0; } |
| Листинг 1.16. Пример программы, опрашивающей и изменяющей значения атрибутов потоков управления. |
| Закрыть окно |
|
Идентификатор текущего потока управления: 400 Значения, установленные системой в атрибутном объекте Адрес начала стека: 0xffe01000 Размер стека: 2093056 Размер защитной области: 4096 Политика планирования: Прочее планирование Приоритет планирования: 0 Область планирования конкуренции: Система Атрибуты планирования: Извлекаются из атрибутного объекта Потоки управления создаются как: Присоединяемые Текущие значения атрибутов потоков управления Политика планирования: Циклическое планирование Приоритет планирования: 1 Уровень параллелизма: 8192 |
| Листинг 1.17. Возможные результаты работы программы, опрашивающей и изменяющей значения атрибутов потоков управления. |
| Закрыть окно |
|
Идентификатор текущего потока управления: f31ae0 Значения, установленные системой в атрибутном объекте Политика планирования: Планирование по очереди Приоритет планирования: 100 Область планирования конкуренции: Процесс Атрибуты планирования: Извлекаются из атрибутного объекта Потоки управления создаются как: Присоединяемые Текущие значения атрибутов потоков управления Политика планирования: Циклическое планирование Приоритет планирования: 1 |
| Листинг 1.18. Возможные результаты работы программы, опрашивающей и изменяющей значения атрибутов потоков управления, для операционной системы реального времени oc2000. |
| Закрыть окно |
|
#include pthread_once_t once_control = PTHREAD_ONCE_INIT; int pthread_once ( pthread_once_t *once_control_ptr, void (*init_routine) (void)); |
| Листинг 1.19. Описание функции pthread_once(). |
| Закрыть окно |
|
#include int pthread_key_create ( pthread_key_t *key_ptr, void (*destructor) (void *)); int pthread_key_delete ( pthread_key_t key); |
| Листинг 1.20. Описание функций pthread_key_create() и pthread_key_delete(). |
| Закрыть окно |
|
#include void *pthread_getspecific ( pthread_key_t key); int pthread_setspecific ( pthread_key_t key, const void *value); |
| Листинг 1.21. Описание функций pthread_getspecific() и pthread_setspecific(). |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Программа запоминает в качестве индивидуальных данных */ /* потока управления время начала активных операций */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ #include /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Деструктор индивидуальных данных, в роли которых */ /* выступает указатель на структуру типа timeval. */ /* Поскольку она не содержит указателей, достаточно */ /* освободить занимаемую ею память */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ static void data_destructor (void *p) { free (p); } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Функция создания ключа индивидуальных данных, */ /* ассоциирующая с ним деструктор, освобождающий память */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ static void create_data_key (void) { (void) pthread_key_create (&data_key, data_destructor); } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Функция инициализации индивидуальных данных. */ /* Запрашивает астрономическое время */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ void *start_func (void) { struct timeval *tmvl_ptr; /* Запомним астрономическое время начала операций */ /* потока управления */ if ((tmvl_ptr = (struct timeval *) malloc (sizeof (struct timeval))) == NULL) { return (NULL); } (void) gettimeofday (tmvl_ptr, NULL); /* Создадим ключ индивидуальных данных, перепоручив */ /* вызов pthread_key_create() функции pthread_once() */ (void) pthread_once (&key_once, create_data_key); (void) pthread_setspecific (data_key, tmvl_ptr); return (tmvl_ptr); } /* * * * * * * * * * * * * * * * * * * * * * * */ /* Функция main() вызывает функцию инициализации */ /* и запрашивает индивидуальные данные потока */ /* управления */ /* * * * * * * * * * * * * * * * * * * * * * * */ int main (void) { struct timeval *tmvl_ptr; if (start_func () == NULL) { return (1); } if ((tmvl_ptr = (struct timeval *) pthread_getspecific (data_key)) != NULL) { printf ("Время начала операций потока управления: " "%ld сек, %ld мсек\n", tmvl_ptr->tv_sec, tmvl_ptr->tv_usec); } else { printf ("Отсутствуют индивидуальные данные потока " "управления.\n"); printf ("Время начала операций неизвестно\n"); return (2); } return 0; } |
| Листинг 1.22. Пример программы, формирующей и опрашивающей индивидуальные данные потоков управления. |
| Закрыть окно |
| Время начала операций потока управления: 1075707670 сек, 584737 мсек |
| Листинг 1.23. Возможные результаты работы программы, формирующей и опрашивающей индивидуальные данные потоков управления. |
| Закрыть окно |
|
#include |
| Листинг 1.24. Описание функции pthread_create(). |
| Закрыть окно |
|
#include |
| Листинг 1.25. Описание функции pthread_atfork(). |
| Закрыть окно |
|
#include |
| Листинг 1.26. Описание функции pthread_exit(). |
| Закрыть окно |
|
#include |
| Листинг 1.27. Описание функции pthread_join(). |
| Закрыть окно |
|
#include void pthread_cleanup_push ( void (*routine) (void *), void *arg); void pthread_cleanup_pop (int execute); |
| Листинг 1.28. Описание функций pthread_cleanup_push() и pthread_cleanup_pop(). |
| Закрыть окно |
|
#define pthread_cleanup_push (rtn, arg) { \ struct _pthread_handler_rec \ __cleanup_handler, \ **__head; \ __cleanup_handler.rtn = rtn; \ __cleanup_handler.arg = arg; \ (void) pthread_getspecific \ (_pthread_handler_key, &__head); \ __cleanup_handler.next = *__head; \ *__head = &__cleanup_handler; #define pthread_cleanup_pop (ex) \ *__head = __cleanup_handler.next; \ if (ex) (*__cleanup_handler.rtn) \ (__cleanup_handler.arg); \ } |
| Листинг 1.29. Возможная реализация функций pthread_cleanup_push() и pthread_cleanup_pop() как макросов. |
| Закрыть окно |
|
#include |
| Листинг 1.30. Описание функции pthread_cancel(). |
| Закрыть окно |
|
#include int pthread_setcancelstate ( int state, int *oldstate); int pthread_setcanceltype ( int type, int *oldtype); void pthread_testcancel (void); |
| Листинг 1.31. Описание функций pthread_setcancelstate(), pthread_setcanceltype(), pthread_testcancel(). |
| Закрыть окно |
|
#include |
| Листинг 1.32. Описание функции pthread_kill(). |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * */ /* Программа демонстрирует генерацию */ /* и доставку сигналов */ /* потокам управления */ /* * * * * * * * * * * * * * * * * */ #include /* * * * * * * * * * * * * * / /* Функция обработки сигнала */ /* * * * * * * * * * * * * * / static void signal_handler (int dummy) { printf ("Идентификатор потока, обрабатывающего сигнал: %lx\n", pthread_self ()); } /* * * * * * * * * * * * * * * * * * * */ /* Стартовая функция потока управления, */ /* которому будет направлен сигнал */ /* * * * * * * * * * * * * * * * * * * */ static void *thread_start (void *dummy) { printf ("Идентификатор нового потока управления: %lx\n", pthread_self ()); while (1) { sleep (1); } return (NULL); } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Функция main() задает способ обработки сигнала SIGINT, */ /* создает поток управления и посылает ему сигнал */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ int main (void) { pthread_t thread_id; struct sigaction act; /* Установим реакцию на сигнал SIGINT */ act.sa_handler = signal_handler; (void) sigemptyset (&act.sa_mask); act.sa_flags = 0; (void) sigaction (SIGINT, &act, (struct sigaction *) NULL); if ((errno = pthread_create (&thread_id, NULL, thread_start, NULL)) != 0) { perror ("PTHREAD_CREATE"); return (errno); } printf ("Идентификатор созданного потока управления: %lx\n", thread_id); (void) pthread_kill (thread_id, SIGINT); printf ("После вызова pthread_kill()\n"); sleep (1); printf ("Выспались...\n"); return (0); } |
| Листинг 1.33. Пример использования механизма сигналов в многопотоковой программе. |
| Закрыть окно |
| Идентификатор созданного потока управления: 402 После вызова pthread_kill() Идентификатор потока, обрабатывающего сигнал: 402 Идентификатор нового потока управления: 402 Выспались... |
| Листинг 1.34. Возможные результаты работы многопотоковой программы, использующей механизм сигналов. |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Программа демонстрирует взаимодействие сигналов */ /* и ожидания завершения потока управления */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ #include /* * * * * * * * * * * * * * */ /* Функция обработки сигнала */ /* * * * * * * * * * * * * * */ static void signal_handler (int dummy) { printf ("Идентификатор потока, обрабатывающего сигнал: %lx\n", pthread_self ()); } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Стартовая функция создаваемого потока управления */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ static void *thread_start (void *thread_id) { printf ("Идентификатор нового потока управления: %lx\n", pthread_self ()); (void) pthread_kill ((pthread_t) thread_id, SIGINT); return ((void *) pthread_self ()); } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Функция main() задает способ обработки сигнала SIGINT, */ /* создает поток управления и ожидает его завершения */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ int main (void) { pthread_t thread_id; struct sigaction act; void *pv; /* Установим реакцию на сигнал SIGINT */ act.sa_handler = signal_handler; (void) sigemptyset (&act.sa_mask); act.sa_flags = 0; (void) sigaction (SIGINT, &act, (struct sigaction *) NULL); if ((errno = pthread_create (&thread_id, NULL, thread_start, (void *) pthread_self ())) != 0) { perror ("PTHREAD_CREATE"); return (errno); } printf ("Идентификаторы начального и созданного потоков " "управления: " "%lx %lx\n", pthread_self (), thread_id); /* Дождемся завершения созданного потока управления */ if ((errno = pthread_join (thread_id, &pv)) != 0) { perror ("PTHREAD_JOIN"); return (errno); } printf ("Статус завершения созданного потока " "управления: %p\n", pv); return (0); } |
| Листинг 1.35. Пример программы, обрабатывающей сигнал во время ожидания завершения потока управления. |
| Закрыть окно |
| Идентификаторы начального и созданного потоков управления: 400 402 Идентификатор нового потока управления: 402 Идентификатор потока, обрабатывающего сигнал: 400 Статус завершения созданного потока управления: 0x402 |
| Листинг 1.36. Возможные результаты работы программы, обрабатывающей сигнал во время ожидания завершения потока управления. |
| Закрыть окно |
|
#include |
| Листинг 1.37. Описание функции pthread_detach(). |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Программа процесса (будем называть его серверным), */ /* принимающего запросы на установления соединения и */ /* запускающего потоки управления для их обслуживания */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ #include /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Стартовая (и единственная) функция потоков управления, */ /* обслуживающих запросы на копирование строк, */ /* поступающих из сокета */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ void *srv_thread_start (void *ad) { FILE *fpad; /* Поток данных, соответствующий */ /* дескриптору ad */ char line [LINE_MAX]; /* Буфер для принимаемых строк */ /* Структура для записи адреса */ struct sockaddr_in sai; /* Длина адреса */ socklen_t sai_len = sizeof (struct sockaddr_in); /* Опросим адрес партнера по общению (передающего сокета) */ if (getpeername ((int) ad, (struct sockaddr *) &sai, &sai_len) < 0) { perror ("GETPEERNAME"); return (NULL); } /* По файловому дескриптору ad сформируем */ /* буферизованный поток данных */ if ((fpad = fdopen ((int) ad, "r")) == NULL) { perror ("FDOPEN"); return (NULL); } /* Цикл чтения строк из сокета */ /* и выдачи их на стандартный вывод */ while (fgets (line, sizeof (line), fpad) != NULL) { printf ("Вы ввели и отправили с адреса %s, " "порт %d :", inet_ntoa (sai.sin_addr), ntohs (sai.sin_port)); fputs (line, stdout); } /* Закрытие соединения */ shutdown ((int) ad, SHUT_RD); (void) fclose (fpad); return (NULL); } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* В функции main() принимаются запросы на установление */ /* соединения и запускаются потоки управления для их обслуживания */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ int main (void) { int sd; /* Дескриптор слушающего сокета */ int ad; /* Дескриптор приемного сокета */ /* Буфер для принимаемых строк */ struct addrinfo hints = {AI_PASSIVE, AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL}; /* Указатель – выходной аргумент getaddrinfo */ struct addrinfo *addr_res; int res; /* Результат getaddrinfo */ pthread_attr_t patob; /* Атрибутный объект для создания */ /* потоков управления */ pthread_t adt_id; /* Идентификатор обслуживающего потока управления */ /* Создадим слушающий сокет */ if ((sd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { perror ("SOCKET"); return (1); } /* Привяжем этот сокет к адресу сервиса spooler */ /* на локальном хосте */ if ((res = getaddrinfo (NULL, "spooler", &hints, &addr_res)) != 0) { fprintf (stderr, "GETADDRINFO: %s\n", gai_strerror (res)); return (2); } if (bind (sd, addr_res->ai_addr, addr_res->ai_addrlen) < 0) { perror ("BIND"); return (3); } /* Можно освободить память, которую запрашивала */ /* функция getaddrinfo() */ freeaddrinfo (addr_res); /* Пометим сокет как слушающий */ if (listen (sd, SOMAXCONN) < 0) { perror ("LISTEN"); return (4); } /* Инициализируем атрибутный объект потоков управления */ if ((errno = pthread_attr_init (&patob)) != 0) { perror ("PTHREAD_ATTR_INIT"); return (errno); } /* Потоки управления будем создавать обособленными */ (void) pthread_attr_setdetachstate (&patob, PTHREAD_CREATE_DETACHED); /* Цикл приема соединений и запуска */ /* обслуживающих потоков управления */ while (1) { /* Примем соединение. */ /* Адрес партнера по общению нас */ /* в данном случае не интересует */ if ((ad = accept (sd, NULL, NULL)) < 0) { perror ("ACCEPT"); return (6); } /* Запустим обслуживающий поток управления */ if ((errno = pthread_create (&adt_id, &patob, srv_thread_start,(void *) ad)) != 0) { perror ("PTHREAD_CREATE"); return (errno); } } return (0); } |
| Листинг 1.38. Пример многопотоковой программы, обслуживающей запросы на копирование строк, поступающих через сокеты. |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Программа вызывает функции обработки в рамках */ /* порождаемых потоков управления и контролирует время */ /* их выполнения с помощью интервального таймера */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ #include /* Период интервального таймера (в секундах) */ #define IT_PERIOD 1 static pthread_t cthread_id; /* Идентификатор текущего */ /* потока управления, */ /* обрабатывающего данные */ static int in_proc_data = 0; /* Признак активности */ /* потока обработки данных */ static double s; /* Результат функций */ /* обработки данных */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Функция обработки срабатывания таймера реального */ /* времени (сигнал SIGALRM) */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ static void proc_sigalrm (int dummy) { if (in_proc_data) { /* Не имеет значения, какой поток обрабатывает сигнал */ /* и заказывает терминирование (быть может, себя) */ (void) pthread_cancel (cthread_id); } } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Обработчик завершения потока управления. */ /* Сбрасывает признак активности потока обработки данных */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ static void proc_data_cleanup_handler (void *arg) { in_proc_data = (int) arg; } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Стартовая функция потока управления, обрабатывающего */ /* данные. Аргумент – указатель на функцию обработки данных */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ void *start_func (void *proc_data_func) { /* Поместим в стек обработчик завершения */ pthread_cleanup_push (proc_data_cleanup_handler, 0); in_proc_data = 1; /* Время пошло ... */ /* На время выполнения функции обработки данных установим */ /* асинхронный тип терминирования, иначе оно не сработает */ (void) pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); /* Выполним функцию обработки данных */ ((void (*) (void)) (proc_data_func)) (); /* Установим отложенный тип терминирования, */ /* иначе изъятие обработчика из стека */ /* будет небезопасным действием */ (void) pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, NULL); /* Выполним обработчик завершения и удалим его из стека */ pthread_cleanup_pop (1); return (NULL); } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Первая функция обработки данных (вычисляет ln (2)) */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ static void proc_data_1 (void) { double d = 1; int i; s = 0; for (i = 1; i <= 100000000; i++) { s += d / i; d = -d; } } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Вторая функция обработки данных (вычисляет sqrt (2))*/ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ static void proc_data_2 (void) { s = 1; do { s = (s + 2 / s) * 0.5; } while ((s * s – 2) > 0.000000001); } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Функция main() задает способ обработки сигнала SIGALRM, */ /* взводит периодический таймер реального времени */ /* и запускает в цикле потоки обработки данных */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ int main (void) { /* Массив указателей на функции обработки данных */ void (*fptrs []) (void) = {proc_data_1, proc_data_2, NULL}; /* Указатель на указатель на */ /* текущую функцию обработки данных */ void (**tfptr) (void); void *pstat; /* Статус завершения потока */ /* обработки данных */ struct itimerval itvl; struct sigaction sact; int i; /* Установим реакцию на сигнал SIGALRM */ sact.sa_handler = proc_sigalrm; sact.sa_flags = 0; (void) sigemptyset (&sact.sa_mask); if (sigaction (SIGALRM, &sact, NULL) < 0) { perror ("SIGACTION"); return (1); } /* Сделаем таймер реального времени периодическим */ itvl.it_interval.tv_sec = IT_PERIOD; itvl.it_interval.tv_usec = 0; /* Цикл запуска потоков обработки данных. */ /* Выполним его дважды */ for (i = 0; i < 2; i++) { for (tfptr = fptrs; *tfptr != NULL; tfptr++) { /* Взведем интервальный таймер реального времени */ itvl.it_value.tv_sec = IT_PERIOD; itvl.it_value.tv_usec = 0; if (setitimer (ITIMER_REAL, &itvl, NULL) < 0) { perror ("SETITIMER"); return (2); } /* Создадим поток обработки данных, */ /* затем дождемся его завершения */ if ((errno = pthread_create (&cthread_id, NULL, start_func, (void *) *tfptr)) != 0) { perror ("PTHREAD_CREATE"); return (errno); } if ((errno = pthread_join (cthread_id, &pstat)) != 0) { perror ("PTHREAD_JOIN"); return (errno); } if (pstat == PTHREAD_CANCELED) { printf ("Частичный результат функции " "обработки данных: %g\n", s); } else { printf ("Полный результат функции " "обработки данных: %g\n", s); } } } return 0; } |
| Листинг 1.39. Пример многопотоковой программы, осуществляющей обработку данных с контролем времени. |
| Закрыть окно |
| Частичный результат функции обработки данных: 0.693147 Полный результат функции обработки данных: 1.41421 Частичный результат функции обработки данных: 0.693147 Полный результат функции обработки данных: 1.41421 |
| Листинг 1.40. Возможные результаты работы многопотоковой программы, осуществляющей обработку данных с контролем времени. |
| Закрыть окно |
|
#include #define N 10000 int main (void) { int i; for (i = 0; i < N; i++) { switch (fork ()) { case -1: perror ("FORK"); return (1); case 0: /* Порожденный процесс */ (void) execl ("./dummy", "dummy", (char *) 0); exit (0); default: /* Родительский процесс */ (void) wait (NULL); } } return 0; } |
| Листинг 1.41. Пример программы, порождающей в цикле практически пустые процессы. |
| Закрыть окно |
| int main (void) { return 0; } |
| Листинг 1.42. Содержимое файла dummy.c |
| Закрыть окно |
| real 34.97 user 12.36 sys 22.61 |
| Листинг 1.43. Возможные результаты измерения времени работы программы, порождающей в цикле практически пустые процессы (вариант с вызовом execl()). |
| Закрыть окно |
| real 11.49 user 2.38 sys 9.11 |
| Листинг 1.44. Возможные результаты измерения времени работы программы, порождающей в цикле практически пустые процессы (вариант без вызова execl()). |
| Закрыть окно |
|
#include #define N 10000 static void *thread_start (void *arg) { pthread_exit (arg); } int main (void) { pthread_t thread_id; int i; for (i = 0; i < N; i++) { if ((errno = pthread_create ( &thread_id, NULL, thread_start, NULL)) != 0) { perror ("PTHREAD_CREATE"); return (errno); } if ((errno = pthread_join ( thread_id, NULL)) != 0) { perror ("PTHREAD_JOIN"); return (errno); } } return (0); } |
| Листинг 1.45. Пример программы, порождающей в цикле потоки управления. |
| Закрыть окно |
| real 2.08 user 0.52 sys 1.56 |
| Листинг 1.46. Возможные результаты измерения времени работы программы, порождающей в цикле потоки управления. |
| Закрыть окно |


![]() | © 2003-2007 INTUIT.ru. Все права защищены. |
|
#include int pthread_mutex_init ( pthread_mutex_t * restrict mutex, const pthread_mutexattr_t *restrict attr); int pthread_mutex_destroy ( pthread_mutex_t *mutex); pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; |
| Листинг 2.1. Описание функций инициализации и разрушения мьютексов. |
| Закрыть окно |
|
#include int pthread_mutex_lock ( pthread_mutex_t *mutex); int pthread_mutex_trylock ( pthread_mutex_t *mutex); int pthread_mutex_unlock ( pthread_mutex_t *mutex); |
| Листинг 2.2. Описание функций захвата и освобождения мьютексов. |
| Закрыть окно |
|
#include |
| Листинг 2.3. Описание функции захвата мьютексов с ограниченным ожиданием. |
| Закрыть окно |
|
#include int pthread_mutex_getprioceiling ( const pthread_mutex_t *restrict mutex, int *restrict prioceiling); int pthread_mutex_setprioceiling ( pthread_mutex_t * restrict mutex, int prioceiling, int *restrict old_ceiling); |
| Листинг 2.4. Описание функций опроса и установки атрибутов мьютекса. |
| Закрыть окно |
|
#include int pthread_mutexattr_init ( pthread_mutexattr_t *attr); int pthread_mutexattr_destroy ( pthread_mutexattr_t *attr); |
| Листинг 2.5. Описание функций инициализации и разрушения атрибутных объектов мьютексов. |
| Закрыть окно |
|
#include int pthread_mutexattr_gettype ( const pthread_mutexattr_t *restrict attr, int *restrict type); int pthread_mutexattr_settype ( pthread_mutexattr_t *attr, int type); int pthread_mutexattr_getpshared ( const pthread_mutexattr_t *restrict attr, int *restrict pshared); int pthread_mutexattr_setpshared ( pthread_mutexattr_t *attr, int pshared); int pthread_mutexattr_getprotocol ( const pthread_mutexattr_t *restrict attr, int *restrict protocol); int pthread_mutexattr_setprotocol ( *attr, int protocol); int pthread_mutexattr_getprioceiling ( const pthread_mutexattr_t *restrict attr, int *restrict prioceiling); int pthread_mutexattr_setprioceiling ( pthread_mutexattr_t *attr, int prioceiling); |
| Листинг 2.6. Описание функций опроса и установки атрибутов мьютекса в атрибутных объектах. |
| Закрыть окно |
|
#ifndef g_MALLOC #define g_MALLOC /* Количество размеров (в словах типа size_t), */ /* для которых поддерживаются */ /* разные списки памяти */ #define DIF_SIZES 8 /* Размер пула памяти */ #define POOL_SIZE 65536 /* Указатель на кусок памяти нулевого размера */ #define g_NULL ((void *) (-1)) /* Первое поле следующей структуры нужно /* для всех кусков памяти, а второе – */ /* только для провязки свободных.*/ /* При отведении памяти адрес второго */ */ поля выдается как результат */ typedef struct listi { size_t length; struct listi *pnext; } *list_of_mem; extern void *g_malloc (size_t); extern void g_free (void *); #endif |
| Листинг 2.7. Заголовочный файл g_malloc.h для функций выделения и освобождения памяти в многопотоковой среде. |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * */ /* Функции выделения и освобождения памяти */ /* * * * * * * * * * * * * * * * * * * * * */ #include static char mem_pool [POOL_SIZE] = {0, }; /* Размер занятой части пула памяти */ /* Списки свободного пространства */ /* (по одному на каждый размер */ /* от 1 до DIF_SIZES) */ static size_t cur_pool_size = 0; static list_of_mem short_lists [DIF_SIZES] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; /* Список больших */ /* свободных кусков (превосходящих DIF_SIZES) */ /* Разные мьютексы для разных */ /* групп списков свободного пространства */ static list_of_mem big_list = NULL; static pthread_mutex_t short_lists_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t big_list_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t pool_mutex = PTHREAD_MUTEX_INITIALIZER; /* * * * * * * * * * */ /* Выделение памяти */ /* * * * * * * * * * */ void *g_malloc (size_t size) { /* Указатель для хождения по списку */ /* больших свободных кусков */ list_of_mem *p; /* Указатель для хождения по спискам */ /* свободных кусков */ list_of_mem pt; /* Индекс в массиве short_lists */ size_t ls; size_t ts; /* Временная переменная */ if (size == 0) { return (g_NULL); /* Важно, чтобы результат был */ /* отличен от NULL, поскольку NULL */ /* – признак ненормального завершения */ } /* Округлим запрошенный размер вверх */ /* до кратного размеру size_t и */ /* прибавим слово служебной информации */ size = (size – 1 + 2 * sizeof (size_t)) & ~(sizeof (size_t) – 1); /* Вычислим индекс в массиве */ /* short_lists [], соответствующий */ /* запрошенному размеру */ ls = size / sizeof (size_t) – 2; if (ls < DIF_SIZES) { /* Попробуем выдать кусок */ /* из списка коротких */ assert ( pthread_mutex_lock( &short_lists_mutex) == 0); if ((pt = short_lists [ls]) != NULL) { /* Есть нужный кусок */ short_lists [ls] = (short_lists [ls])->pnext; assert (pthread_mutex_unlock( &short_lists_mutex) == 0); return (&pt->pnext); } assert (pthread_mutex_unlock( &short_lists_mutex) == 0); } /* Попробуем выдать кусок из */ /* списка больших */ assert (pthread_mutex_lock( &big_list_mutex) == 0); for (p = &big_list, pt = *p; pt != NULL; p = &pt->pnext, pt = *p) { if ((signed long) (ts = pt->length – size) >= 0) { /* Нашли подходящий кусок */ if (ts < sizeof (*pt)) { /* Придется выдать кусок целиком – */ /* в остатке не помещается */ /* служебная информация */ *p = pt->pnext; } else { /* Отрежем сколько надо и, */ /* при необходимости, */ /* перецепим остаток в */ /* список коротких */ if ((ls = (pt->length = ts) / sizeof (size_t) – 2) < DIF_SIZES) { *p = pt->pnext; assert (pthread_mutex_lock( &short_lists_mutex) == 0); pt->pnext = short_lists [ls]; short_lists [ls] = pt; assert (pthread_mutex_unlock( &short_lists_mutex) == 0); } pt = (list_of_mem) ((char *) pt + ts); pt->length = size; } assert ( pthread_mutex_unlock( &big_list_mutex) == 0); return (&pt->pnext); } } /* for */ assert (pthread_mutex_unlock ( &big_list_mutex) == 0); /* Кусок из большого списка */ /* выдать не удалось. */ /* Попробуем взять прямо из */ /* пула памяти */ assert (pthread_mutex_lock( &pool_mutex) == 0); if (cur_pool_size + size <= POOL_SIZE) { pt = (list_of_mem) (mem_pool + cur_pool_size); pt->length = size; cur_pool_size += size; assert (pthread_mutex_unlock ( &pool_mutex) == 0); return (&pt->pnext); } assert (pthread_mutex_unlock( &pool_mutex) == 0); /* Неудача при выделении памяти */ errno = ENOMEM; return (NULL); } /* * * * * * * * * * * * * * * * * * */ /* Возврат ранее запрошенной памяти */ /* * * * * * * * * * * * * * * * * * */ void g_free (void *p) { list_of_mem pt; size_t size, ls; if ((p == g_NULL) || (p == NULL)) { return; } /* Установим указатель на */ /* служебную информацию */ pt = (list_of_mem) ((char *) p – sizeof (size_t)); size = pt->length; ls = size / sizeof (size_t) – 2; memset (p, 0, size – sizeof (size_t)); /* Не из конца ли пула этот кусок? */ assert (pthread_mutex_lock( &pool_mutex) == 0); if (((char *) pt + size) == (mem_pool + cur_pool_size)) { pt->length = 0; cur_pool_size -= size; assert (pthread_mutex_unlock( &pool_mutex) == 0); return; } assert (pthread_mutex_unlock( &pool_mutex) == 0); /* Добавим освободившийся кусок */ /* к одному из списков */ if (ls < DIF_SIZES) { assert (pthread_mutex_lock( &short_lists_mutex) == 0); pt->pnext = short_lists [ls]; short_lists [ls] = pt; assert (pthread_mutex_unlock( &short_lists_mutex) == 0); } else { /* Добавим к большому списку */ assert (pthread_mutex_lock( &big_list_mutex) == 0); pt->pnext = big_list; big_list = pt; assert (pthread_mutex_unlock( &big_list_mutex) == 0); } } |
| Листинг 2.8. Исходный текст функций выделения и освобождения памяти в многопотоковой среде. |
| Закрыть окно |
|
#include static void *start_func (void *dummy) { void *p1, *p2, *p3; printf ("g_malloc (65000): %p\n", p1 = g_malloc (65000)); sleep (1); printf ("g_malloc (1): %p\n", p2 = g_malloc (1)); sleep (1); g_free (p1); sleep (1); g_free (p2); sleep (1); printf ("g_malloc (64990): %p\n", p1 = g_malloc (64990)); sleep (1); printf ("g_malloc (1): %p\n", p2 = g_malloc (1)); sleep (1); printf ("g_malloc (5): %p\n", p3 = g_malloc (5)); sleep (1); g_free (p1); sleep (1); g_free (p2); sleep (1); g_free (p3); sleep (1); printf ("g_malloc (100000): %p\n", p3 = g_malloc (100000)); return (NULL); } int main (void) { pthread_t pt1, pt2; pthread_create (&pt1, NULL, start_func, NULL); pthread_create (&pt2, NULL, start_func, NULL); pthread_join (pt1, NULL); pthread_join (pt2, NULL); return (0); } |
| Листинг 2.9. Пример программы, использующей функции выделения и освобождения памяти в многопотоковой среде. |
| Закрыть окно |
| g_malloc (65000): 0x8049024 g_malloc (65000): (nil) g_malloc (1): 0x8058e10 g_malloc (1): 0x8058e18 g_malloc (64990): 0x804902c g_malloc (64990): (nil) g_malloc (1): 0x8049024 g_malloc (1): 0x8058e10 g_malloc (5): 0x8058e18 g_malloc (5): 0x8058e24 g_malloc (100000): (nil) g_malloc (100000): (nil) |
| Листинг 2.10. Возможные результаты работы программы, использующей функции выделения и освобождения памяти в многопотоковой среде. |
| Закрыть окно |
|
/* Обедающие философы. Многопотоковая реализация с помощью мьютексов. Запуск: mudrecMutex [-a | -p | -I] [-t число_секунд] имя_философа ... Опции: -t число_секунд – сколько секунд моделируется Стратегии захвата вилок: -a – сначала захватывается вилка с меньшим номером; -p – сначала захватывается нечетная вилка; -I – некорректная (но эффективная) интеллигентная стратегия: во время ожидания уже захваченная вилка кладется. Пример запуска: mudrecMutex -p -t 300 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: mudrecMutex.c,v 1.14 2003/11/20 16:09:20" "sambor Exp $"; #include #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)>(b)?(b):(a)) struct mudrec { char *name; int left_fork, right_fork; int eat_time, wait_time, think_time, max_wait_time; int count; pthread_t thread; int private_pFdIn; } *kafedra; /* Глобальные счетчики и логические переменные */ int Stop = 0; /* Признак конца обеда */ /* Различные дескрипторы */ int protokol [2] = {-1, -1}; #define pFdIn (protokol [1]) #define pFdOut (protokol [0]) /* Массив мьютексов для синхронизации доступа */ /* к вилкам */ pthread_mutex_t *mtxFork; /* Разные алгоритмы захвата вилок */ static void get_forks_simple ( struct mudrec *this); static void get_forks_odd ( struct mudrec *this); static void get_forks_maybe_infinit_time( struct mudrec *this); /* Используемый метод захвата вилок */ void (*get_forks) (struct mudrec *this) = get_forks_simple; /* Возвращение вилок */ static void put_forks (struct mudrec *this); /*Потоки-философы */ void *filosof (void *arg) { struct mudrec *this = arg; char buffer [LINE_MAX]; int bytes; int private_pFdIn = this->private_pFdIn; while (!Stop) { /* Пора подкрепиться */ { int wait_time, tm = time (NULL); bytes = write ( private_pFdIn, buffer, strlen (buffer)); (*get_forks) (this); wait_time = time (NULL) – 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 (private_pFdIn, buffer, strlen (buffer)); } /* Может, обед уже закончился? */ if (Stop) { put_forks (this); break; } /* Ест */ { int eat_time = rand () % 20 + 1; sleep (eat_time); this->eat_time += eat_time; this->count++; sprintf (buffer, "%s: ел %d сек\n", this->name, eat_time); bytes = write ( private_pFdIn, buffer, strlen (buffer)); } /* Отдает вилки */ put_forks (this); if (Stop) break; /* Размышляет */ { int think_time = rand () % 10 + 1; sleep (think_time); this->think_time += think_time; } } /* while (!Stop) */ sprintf (buffer,"%s: уходит\n", this->name); bytes = write (private_pFdIn, buffer, strlen (buffer)); close (private_pFdIn); return (NULL); } /* Поток-философ */ /* Кладет вилки одну за другой */ static void put_forks (struct mudrec *this) { pthread_mutex_unlock ( &mtxFork [this->left_fork – 1]); pthread_mutex_unlock ( &mtxFork [this->right_fork – 1]); } /* Берет вилки по очереди в порядке номеров */ static void get_forks_simple( struct mudrec *this) { int first = min ( this->left_fork, this->right_fork); int last = max ( this->left_fork, this->right_fork); pthread_mutex_lock ( &mtxFork [first – 1]); pthread_mutex_lock ( &mtxFork [last – 1]); } /* Берем сначала нечетную вилку */ /* (если обе нечетные – */ /* то с большим номером) */ static void get_forks_odd (struct mudrec *this) { int left = this->left_fork, right = this->right_fork; int first; int last; if ((left & 1) > (right & 1)) { first = left; last = right; } else if ((left & 1) < (right & 1)) { first = right; last = left; } else { first = max (left, right); last = min (left, right); } pthread_mutex_lock ( &mtxFork [first – 1]); pthread_mutex_lock ( &mtxFork [last – 1]); } /* Берем вилки по очереди, в */ /* произвольном порядке. */ /* Но если вторая вилка не */ /* берется сразу, то кладем */ /* первую. */ /* То есть философ не расходует */ /* вилочное время впустую. */ static void get_forks_maybe_infinit_time (struct mudrec *this) { int left = this->left_fork, right = this->right_fork; for (;;) { pthread_mutex_lock ( &mtxFork [left – 1]); if (0 == pthread_mutex_trylock (&mtxFork [right – 1])) return; pthread_mutex_unlock ( &mtxFork [left – 1]); pthread_mutex_lock ( &mtxFork [right – 1]); if (0 == pthread_mutex_trylock (&mtxFork [left – 1])) return; pthread_mutex_unlock ( &mtxFork [right – 1]); } } /* Мелкие служебные функции */ static void stop (int dummy) { Stop = 1; } static void usage (char name []) { fprintf (stderr, "Использование: %s" " [-a | -p | -I] " "[-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; int nMudr; struct sigaction sact; while ((c = getopt (argc, argv, "apIt:")) != -1) { (c) { case 'a': get_forks = get_forks_simple; break; case 'p': get_forks = get_forks_odd; break; case 'I': get_forks = get_forks_maybe_infinit_time; 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); kafedra = calloc (sizeof (struct mudrec), nMudr); /* Зачисление на кафедру */ for (i = 0; i < nMudr; i++, optind++) { kafedra [i].name = argv [optind]; /* Выдадим телефон */ kafedra [i].private_pFdIn = fcntl (pFdIn, F_DUPFD, 0); /* Укажем новичку, */ /* какими вилками */ /* пользоваться */ kafedra [i].left_fork = i + 1; kafedra [i].right_fork = i + 2; } /* Последний */ /* пользуется вилкой первого */ kafedra [nMudr – 1].right_fork = 1; /* Зададим реакцию на сигналы */ /* и установим будильник */ /* на конец обеда */ 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); /* Создадим мьютексы для охраны вилок */ mtxFork = calloc ( sizeof (pthread_mutex_t), nMudr); for (i = 0; i < nMudr; i++) { pthread_mutex_init ( &mtxFork [i], NULL); } /* Философы входят в столовую */ for (i = 0; i < nMudr; i++) pthread_create ( &kafedra [i].thread, NULL, &filosof, (void *) &kafedra [i]); /* Выдача сообщений на стандартный */ /* вывод и выход */ /* после окончания всех задач */ close (pFdIn); while (1) { n = read (pFdOut, buffer, LINE_MAX); if (n == 0 || (n < 0 && errno != EINTR)) break; for (i = 0; i < n; i++) putchar (buffer [i]); } close (pFdOut); /* Уничтожение мьютексов */ for (i = 0; i < nMudr; i++) { pthread_mutex_destroy ( &mtxFork [i]); } /* Выдача сводной информации */ { int full_eating_time = 0; int full_waiting_time = 0; int full_thinking_time = 0; for (i = 1; i <= nMudr; i++) { struct mudrec *this = &kafedra [i – 1]; 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); } /* for */ { float total_time = ( full_eating_time + full_waiting_time + full_thinking_time) / (float) nMudr; printf("Среднее число одновременно" " едящих = %.3f\n", full_eating_time / total_time); printf("Среднее число одновременно" " ждущих = %.3f\n", full_waiting_time / total_time); } } /* Выдача сводной информации */ free (mtxFork); free (kafedra); /* Сообщим об окончании работы. */ printf ("Конец обеда\n"); return 0; } |
| Листинг 2.11. Многопотоковый вариант решения задачи об обедающих философах с использованием мьютексов. |
| Закрыть окно |
|
#include int pthread_cond_init ( pthread_cond_t * restrict cond, const pthread_condattr_t *restrict attr); int pthread_cond_destroy ( pthread_cond_t *cond); pthread_cond_t cond = PTHREAD_COND_INITIALIZER; |
| Листинг 2.12. Описание функций инициализации и разрушения переменных условия. |
| Закрыть окно |
|
#include int pthread_cond_wait ( pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); int pthread_cond_timedwait ( pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, struct timespec *restrict abstime); |
| Листинг 2.13. Описание функций блокирования на переменной условия. |
| Закрыть окно |
|
#include int pthread_cond_broadcast ( pthread_cond_t *cond); int pthread_cond_signal ( pthread_cond_t *cond); |
| Листинг 2.14. Описание функций разблокирования потоков управления, блокированных на переменной условия. |
| Закрыть окно |
|
#include int pthread_condattr_init ( pthread_condattr_t *attr); int pthread_condattr_destroy ( pthread_condattr_t *attr); |
| Листинг 2.15. Описание функций инициализации и разрушения атрибутных объектов переменных условия. |
| Закрыть окно |
|
#include int pthread_condattr_getpshared ( const pthread_condattr_t *restrict attr, int *restrict pshared); int pthread_condattr_setpshared ( pthread_condattr_t *attr, int pshared); int pthread_condattr_getclock ( const pthread_condattr_t *restrict attr, clockid_t *restrict clock_id); int pthread_condattr_setclock ( pthread_condattr_t *attr, clockid_t clock_id); |
| Листинг 2.16. Описание функций опроса и установки атрибутов переменных условия в атрибутных объектах. |
| Закрыть окно |
| rc = 0; while (! predicate && rc == 0) { rc = pthread_cond_timedwait ( &cond, &mutex, &ts); } |
| Листинг 2.17. Типичный цикл ожидания на переменной условия с контролем по времени. |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * */ /* Многопотоковый вариант обеда */ /* философов с использованием */ /* мьютексов и переменных условия */ /* * * * * * * * * * * * * * * * * */ #include /* Число обедающих философов */ #define QPH 5 /* Время ( в секундах) на обед */ #define FO 15 /* Длительность еды */ #define ernd (rand () % 3 + 1) /* Длительность разговора */ #define trnd (rand () % 5 + 1) /* Состояние вилок */ static int fork_busy [QPH] = {0, }; /* Синхронизирующая переменная условия */ static pthread_cond_t forks_cond = PTHREAD_COND_INITIALIZER; /* Синхронизирующий мьютекс */ static pthread_mutex_t forks_mutex = PTHREAD_MUTEX_INITIALIZER; /* * * * * * * * * * * * * * * * * * */ /* Стартовая функция потока-философа. */ /* Аргумент – номер философа */ /* * * * * * * * * * * * * * * * * * */ void *start_phil (void *no) { /* Время до конца обеда */ int fo; /* Время очередного отрезка */ /* еды или беседы */ int t; fo = FO; while (fo > 0) { /* Обед */ /* Философ говорит */ printf ("Философ %d беседует\n", (int) no); t = trnd; sleep (t); fo -= t; /* Пытается взять вилки */ (void) pthread_mutex_lock ( &forks_mutex); while (fork_busy [(int) no – 1] || fork_busy [(int) no % QPH] ) { (void) pthread_cond_wait ( &forks_cond, &forks_mutex); } fork_busy [(int) no – 1] = fork_busy [(int) no % QPH] = 1; (void) pthread_mutex_unlock ( &forks_mutex); /* Ест */ printf ("Философ %d ест\n", (int) no); t = ernd; sleep (t); fo -= t; /* Отдает вилки */ (void) pthread_mutex_lock ( &forks_mutex); fork_busy [(int) no – 1] = fork_busy [(int) no % QPH] = 0; (void) pthread_cond_broadcast (&forks_cond); (void) pthread_mutex_unlock (&forks_mutex); } /* while */ printf ("Философ %d закончил обед\n", (int) no); return (NULL); } /* * * * * * * * * * * * * * * */ /* Создание потоков-философов */ /* и ожидание их завершения */ /* * * * * * * * * * * * * * * */ int main (void) { /* Массив идентификаторов */ /* потоков-философов */ pthread_t pt_id [QPH]; /* Номер философа */ int no; /* Атрибутный объект для */ /* создания потоков */ pthread_attr_t attr_obj; if ((errno = pthread_attr_init( &attr_obj)) != 0) { perror ("PTHREAD_ATTR_INIT"); return (errno); } /* В очередь, господа */ /* философы, в очередь! */ if ((errno = pthread_attr_setschedpolicy( &attr_obj, SCHED_FIFO)) != 0) { perror ( "PTHREAD_ATTR_SETSCHEDPOLICY"); return (errno); } /* Все – к столу */ for (no = 1; no <= QPH; no++) { if ((errno = pthread_create ( &pt_id [no – 1], &attr_obj, start_phil, (void *) no)) != 0) { perror ( "PTHREAD_CREATE"); return (no); } } /* Ожидание завершения обеда */ for (no = 1; no <= QPH; no++) { (void) pthread_join ( pt_id [no – 1], NULL); } return 0; } |
| Листинг 2.18. Пример многопотоковой реализации обеда философов с использованием мьютексов и переменных условия. |
| Закрыть окно |
| Философ 1 беседует Философ 2 беседует Философ 3 беседует Философ 4 беседует Философ 5 беседует Философ 4 ест Философ 2 ест Философ 4 беседует Философ 5 ест Философ 2 беседует Философ 3 ест Философ 5 беседует Философ 1 ест Философ 3 беседует Философ 4 ест Философ 1 беседует Философ 2 ест Философ 2 беседует Философ 1 ест Философ 4 беседует Философ 3 ест Философ 1 беседует Философ 5 ест Философ 5 беседует Философ 3 беседует Философ 4 ест Философ 2 ест Философ 4 беседует Философ 2 беседует Философ 1 ест Философ 3 ест Философ 1 закончил обед Философ 5 ест Философ 3 закончил обед Философ 2 ест Философ 5 закончил обед Философ 4 ест Философ 2 закончил обед Философ 4 закончил обед |
| Листинг 2.19. Возможные результаты работы многопотоковой программы, моделирующей обед философов с использованием мьютексов и переменных условия. |
| Закрыть окно |
|
#include int pthread_rwlock_init ( pthread_rwlock_t * restrict rwlock, const pthread_rwlockattr_t *restrict attr); int pthread_rwlock_destroy ( pthread_rwlock_t *rwlock); |
| Листинг 2.20. Описание функций инициализации и разрушения блокировок чтение-запись. |
| Закрыть окно |
|
#include int pthread_rwlock_rdlock ( pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock ( pthread_rwlock_t *rwlock); |
| Листинг 2.21. Описание функций установки блокировки на чтение. |
| Закрыть окно |
|
#include |
| Листинг 2.22. Описание функции установки блокировки на чтение с ограниченным ожиданием. |
| Закрыть окно |
|
#include int pthread_rwlock_wrlock ( pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock ( pthread_rwlock_t *rwlock); |
| Листинг 2.23. Описание функций установки блокировки на запись. |
| Закрыть окно |
|
#include |
| Листинг 2.24. Описание функции установки блокировки на запись с ограниченным ожиданием. |
| Закрыть окно |
|
#include |
| Листинг 2.25. Описание функции снятия блокировки чтение-запись. |
| Закрыть окно |
|
#include int pthread_rwlockattr_init ( pthread_rwlockattr_t *attr); int pthread_rwlockattr_destroy ( pthread_rwlockattr_t *attr); |
| Листинг 2.26. Описание функций инициализации и разрушения атрибутных объектов блокировок. |
| Закрыть окно |
|
#include int pthread_rwlockattr_getpshared ( const pthread_rwlockattr_t *restrict attr, int *restrict pshared); int pthread_rwlockattr_setpshared ( pthread_rwlockattr_t *attr, int pshared); |
| Листинг 2.27. Описание функций опроса и установки атрибутов блокировок в атрибутных объектах. |
| Закрыть окно |
|
/* * * * * * * * * * * * * * */ /* Типы, структуры и функции */ /* для многопотоковой работы */ /* со списками */ /* * * * * * * * * * * * * * */ #ifndef g_LIST #define g_LIST /* Указатель на процедуру */ typedef void (*aproc) (void *, void *); /* Указатель на целочисленную функцию */ typedef int (*afun) (void *, void *); /* Элемент списка указателей */ /* на объекты произвольного типа */ typedef struct g_link { /* Ссылка на следующий элемент списка */ struct g_link *pnext; /* Ссылка на объект */ void *pvalue; } *g_linkP; /* Список указателей на объекты */ /* произвольного типа */ typedef struct g_list { /* Голова списка */ g_linkP head; /* Блокировка, ассоциированная */ pthread_rwlock_t lk; /* со списком */ } *g_listP; /* Инициализировать список */ extern void g_list_init (g_listP); /* Завершить работу со списком */ extern void g_list_destroy (g_listP); /* Вставить новый элемент */ /* в конец списка */ extern void g_list_ins_last ( g_listP, void *); /* Вставить новый элемент в */ /* список перед первым, */ /* удовлетворяющим заданному */ /* свойству (или в конец) */ extern void g_list_ins_fun ( g_listP, void *, afun); /* Удалить элемент из списка */ extern void g_list_del ( g_listP, void *); /* Применить процедуру ко всем */ /* элементам списка */ extern void g_list_forall_x ( g_listP, aproc, void *); /* Выбрать подходящий элемент списка */ extern void *g_list_suchas_x ( g_listP, afun, void *); /* Выдать элемент по номеру */ extern void *g_list_get_bynum ( g_listP, int); #endif |
| Листинг 2.28. Заголовочный файл g_list.h для функций работы со списками в многопотоковой среде. |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * */ /* Реализация функций для многопотоковой */ /* работы со списками. */ /* Применяются блокировки чтение-запись */ /* без приостановки выполнения */ /* * * * * * * * * * * * * * * * * * * * */ #define _XOPEN_SOURCE 600 #include /* * * * * * * * * * * * * */ /* Инициализировать список */ /* * * * * * * * * * * * * */ void g_list_init (g_listP plist) { plist->head = NULL; errno = pthread_rwlock_init ( &plist->lk, NULL); } /* * * * * * * * * * * * * * * */ /* Завершить работу со списком */ /* * * * * * * * * * * * * * * */ void g_list_destroy (g_listP plist) { errno = pthread_rwlock_destroy ( &plist->lk); } /* * * * * * * * * * * * * * * * * * * * */ /* Вставить новый элемент в конец списка */ /* * * * * * * * * * * * * * * * * * * * */ void g_list_ins_last (g_listP plist, void *pval) { register g_linkP pt; register g_linkP *p; if ((errno = pthread_rwlock_trywrlock ( &plist->lk)) != 0) { return; } for (p = &plist->head; *p != NULL; p = &(*p)->pnext) ; if ((pt = (g_linkP) malloc (sizeof (*pt))) != NULL) { pt->pnext = NULL; pt->pvalue = pval; *p = pt; } (void) pthread_rwlock_unlock ( &plist->lk); } /* * * * * * * * * * * * * * * * * */ /* Вставить новый элемент в список */ /* перед первым, */ /* удовлетворяющим заданному свойству */ /* (или в конец) */ /* * * * * * * * * * * * * * * * */ void g_list_ins_fun (g_listP plist, void *pval, afun fun) { register g_linkP pt; register g_linkP *p; if ((errno = pthread_rwlock_trywrlock ( &plist->lk)) != 0) { return; } for (p = &plist->head; (*p != NULL) && (fun (pval, (*p)->pnext->pvalue) == 0); p = &(*p)->pnext) ; if ((pt = (g_linkP) malloc ( sizeof (*pt))) != NULL) { pt->pnext = NULL; pt->pvalue = pval; *p = pt; } (void) pthread_rwlock_unlock ( &plist->lk); } /* * * * * * * * * * * * * * */ /* Удалить элемент из списка */ /* * * * * * * * * * * * * * */ void g_list_del (g_listP plist, void *pval) { register g_linkP pt; register g_linkP *p; if ((errno = pthread_rwlock_trywrlock (&plist->lk)) != 0) { return; } for (p = &plist->head; *p != NULL; p = &(*p)->pnext) { if ((*p)->pvalue == pval) { pt = *p; *p = pt->pnext; free (pt); errno = pthread_rwlock_unlock( &plist->lk); return; } } /* Пытаемся удалить */ /* несуществующий элемент */ (void) pthread_rwlock_unlock ( &plist->lk); errno = EINVAL; } /* * * * * * * * * * * * * */ /* Применить процедуру ко */ /* всем элементам списка */ /* * * * * * * * * * * * */ void g_list_forall_x (g_listP plist, aproc proc, void *extobj) { register g_linkP pt; /* Устанавливаем блокировку на запись, */ /* поскольку, возможно, процедура */ /* модифицирует */ /* объекты, ссылки на которые */ /* хранятся в списке */ if ((errno = pthread_rwlock_trywrlock( &plist->lk)) != 0) { return; } for (pt = plist->head; pt != NULL; pt = pt->pnext) { proc (extobj, pt->pvalue); } (void) pthread_rwlock_unlock( &plist->lk); } /* * * * * * * * * * * * * * * * * * */ /* Выбрать подходящий элемент списка */ /* * * * * * * * * * * * * * * * * * */ void *g_list_suchas_x (g_listP plist, afun fun, void *extobj) { register g_linkP pt; /* Устанавливаем блокировку на */ /* чтение, так как */ /* считаем, что функция не */ /* модифицирует объекты, */ /* ссылки на которые хранятся */ /* в списке */ if ((errno = pthread_rwlock_tryrdlock( &plist->lk)) != 0) { return NULL; } for (pt = plist->head; pt != NULL; pt = pt->pnext) { if (fun (extobj, pt->pvalue)) { (void) pthread_rwlock_unlock( &plist->lk); return (pt->pvalue); } } (void) pthread_rwlock_unlock( &plist->lk); return NULL; } /* * * * * * * * * * * * * * */ /* Выдать элемент по номеру */ /* * * * * * * * * * * * * * */ void *g_list_get_bynum (g_listP plist, int n) { register g_linkP pt; if ((errno = pthread_rwlock_tryrdlock( &plist->lk)) != 0) { return NULL; } for (pt = plist->head; n--, pt != NULL; pt = pt->pnext) { if (n == 0) { (void) pthread_rwlock_unlock( &plist->lk); return (pt->pvalue); } } /* Пытаемся извлечь несуществующий */ /* компонент */ (void) pthread_rwlock_unlock( &plist->lk); errno = EINVAL; return NULL; } |
| Листинг 2.29. Исходный текст функций для работы со списками в многопотоковой среде. |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * */ /* Тест функций многопотоковой работы */ /* со списками */ /* * * * * * * * * * * * * * * * * * * */ #define _XOPEN_SOURCE 600 #include /* Структура для засыпания на */ /* минимальное время */ static struct timespec nslp = {0, 1}; /* Структуры для возврата результатов */ /* потоками: */ /* Сколько операций пытались сделать, */ /* сколько из них оказались неудачными */ /* из-за занятости списка */ static struct pt_res { int nops; int nbusy; } ptres [3] = {{0, 0}, {0, 0}, {0, 0}}; /* * * * * * * * * * * * * * * * * * * * */ /* Этот поток добавляет элементы к списку */ /* * * * * * * * * * * * * * * * * * * * */ static void *start_func_1 (void *plist) { int *p1; while (1) { if ((p1 = (int *) malloc ( sizeof (int))) != NULL) { *p1 = rand (); errno = 0; g_list_ins_last (plist, p1); ptres [0].nops++; if (errno == EBUSY) { ptres [0].nbusy++; } } (void) nanosleep (&nslp, NULL); } return (NULL); } /* * * * * * * * * * * * * * * * * * * * * */ /* Процедура для подсчета суммы элементов */ /* списка */ /* * * * * * * * * * * * * * * * * * * * * */ static void proc_sum (void *sum, void *pval) { *((int *) sum) += *((int *) pval); } /* * * * * * * * * * * * * * * * * * * * * */ /* Этот поток подсчитывает сумму элементов */ /* списка */ /* * * * * * * * * * * * * * * * * * * * * */ static void *start_func_2 (void *plist) { int sum; while (1) { sum = 0; errno = 0; g_list_forall_x (plist, &proc_sum, &sum); ptres [1].nops++; if (errno == EBUSY) { ptres [1].nbusy++; } (void) nanosleep (&nslp, NULL); } return (NULL); } /* * * * * * * * * * * * * * * * * */ /* Этот поток удаляет из списка */ /* элементы со случайными номерами */ /* * * * * * * * * * * * * * * * * */ static void *start_func_3 (void *plist) { int *p1; while (1) { errno = 0; p1 = (int *) g_list_get_bynum( plist, rand ()); ptres [2].nops++; if (errno == EBUSY) { ptres[2].nbusy++; } if (p1 != NULL) { errno = 0; g_list_del (plist, p1); ptres [2].nops++; if (errno == EBUSY) { ptres [2].nbusy++; } free (p1); } (void) nanosleep (&nslp, NULL); } return (NULL); } /* * * * * * * * * * * * * * * * * * * * * * */ /* Начальный поток всех запустит, поспит, */ /* потом всех терминирует и выдаст статистику */ /* * * * * * * * * * * * * * * * * * * * * * */ int main (void) { g_listP plist; pthread_t pt1, pt2, pt3; g_list_init (plist); pthread_create (&pt1, NULL, start_func_1, plist); pthread_create (&pt2, NULL, start_func_2, plist); pthread_create (&pt3, NULL, start_func_3, plist); sleep (10); pthread_cancel (pt1); pthread_cancel (pt2); pthread_cancel (pt3); pthread_join (pt1, NULL); pthread_join (pt2, NULL); pthread_join (pt3, NULL); g_list_destroy (plist); printf ("Попыток выполнить " "операцию со списком: %d\n", ptres [0].nops + ptres [1].nops + ptres [2].nops); printf ("Число попыток, неудачных" " из-за занятости " "списка: %d\n", ptres [0].nbusy + ptres [1].nbusy + ptres [2].nbusy); return (0); } |
| Листинг 2.30. Пример программы, использующей функции для работы со списками в многопотоковой среде. |
| Закрыть окно |
| Попыток выполнить операцию со списком: 1503 Число попыток, неудачных из-за занятости списка: 0 |
| Листинг 2.31. Возможные результаты работы программы, использующей функции для работы со списками в многопотоковой среде. |
| Закрыть окно |
|
#include int pthread_spin_init ( pthread_spinlock_t *lock, int pshared); int pthread_spin_destroy ( pthread_spinlock_t *lock); |
| Листинг 2.32. Описание функций инициализации и разрушения спин-блокировок. |
| Закрыть окно |
|
#include int pthread_spin_lock ( pthread_spinlock_t *lock); int pthread_spin_trylock ( pthread_spinlock_t *lock); |
| Листинг 2.33. Описание функций установки спин-блокировки. |
| Закрыть окно |
|
#include |
| Листинг 2.34. Описание функции снятия спин-блокировки. |
| Закрыть окно |
| pthread_spin_lock (&ss->lock); /* Восстановим старую маску */ ss->blocked = oldmask; /* Проверим ждущие сигналы */ pending = ss->pending & ~ss->blocked; pthread_spin_unlock (&ss->lock); |
| Листинг 2.35. Фрагмент возможной реализации функции sigsuspend(). |
| Закрыть окно |
|
#include int pthread_barrier_init ( pthread_barrier_t * restrict barrier, const pthread_barrierattr_t *restrict attr, unsigned count); int pthread_barrier_destroy ( pthread_barrier_t *barrier); |
| Листинг 2.36. Описание функций инициализации и разрушения барьеров. |
| Закрыть окно |
|
#include |
| Листинг 2.37. Описание функции синхронизации на барьере. |
| Закрыть окно |
|
#include int pthread_barrierattr_init ( pthread_barrierattr_t *attr); int pthread_barrierattr_destroy ( pthread_barrierattr_t *attr); |
| Листинг 2.38. Описание функций инициализации и разрушения атрибутных объектов барьеров. |
| Закрыть окно |
|
#include int pthread_barrierattr_getpshared (const pthread_barrierattr_t *restrict attr, int *restrict pshared); int pthread_barrierattr_setpshared (pthread_barrierattr_t *attr, int pshared); |
| Листинг 2.39. Описание функций опроса и установки атрибутов барьеров в атрибутных объектах. |
| Закрыть окно |
|
if ((status = pthread_barrier_wait( &barrier)) == PTHREAD_BARRIER_SERIAL_THREAD) { /* Выделенные (обычно – объединительные) */ /* действия. */ /* Выполняются каким-то одним потоком */ /* управления */ } else { /* Эта часть выполняется всеми */ /* прочими потоками */ /* управления */ if (status != 0) { /* Обработка ошибочной ситуации */ } else { /* Нормальное "невыделенное" */ /* завершение ожидания */ /* на барьере */ } } /* Повторная синхронизация – */ /* ожидание завершения выделенных действий */ status = pthread_barrier_wait (&barrier); /* Продолжение параллельной работы */ . . . |
| Листинг 2.40. Типичная схема применения функции pthread_barrier_wait(). |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * * */ /* Программа использует барьеры для слияния */ /* результатов */ /* коллективных вычислений ln(2) */ /* * * * * * * * * * * * * * * * * * * * * * */ #define _XOPEN_SOURCE 600 #include static pthread_barrier_t mbrr; /* Ряд для ln(2) будут суммировать */ /* два потока. */ /* Один возьмет на себя положительные */ /* слагаемые, */ /* другой – отрицательные */ static double sums [2] = {0, 0}; /* * * * * * * * * * * * * * * * * * * * */ /* Стартовая функция потоков управления, */ /* участвующих в вычислениях ln(2). */ /* Аргумент ns на самом деле */ /* целочисленный и равен 1 или 2 */ /* * * * * * * * * * * * * * * * * * * * */ static void *start_func (void *ns) { double d = 1; double s = 0; int i; /* Вычислим свою часть ряда */ for (i = (int) ns; i <= 100000000; i += 2) { s += d / i; } /* Запомним результат в нужном месте */ sums [(int) ns – 1] = s; /* Синхронизируемся для получения */ /* общего итога */ if (pthread_barrier_wait (&mbrr) == PTHREAD_BARRIER_SERIAL_THREAD) { sums [0] -= sums [1]; } /* Указатель на итог возвращают оба потока */ return (sums); } /* * * * * * * * * * * * * * * * * */ /* Инициализация барьера, */ /* создание и ожидание завершения */ /* потоков управления */ /* * * * * * * * * * * * * * * * * */ int main (void) { pthread_t pt1, pt2; double *pd; if ((errno = pthread_barrier_init ( &mbrr, NULL, 2)) != 0) { perror ("PTHREAD_BARRIER_INIT"); return (errno); } pthread_create (&pt1, NULL, start_func, (void *) 1); pthread_create (&pt2, NULL, start_func, (void *) 2); pthread_join (pt1, (void **) &pd); pthread_join (pt2, (void **) &pd); printf ("Коллективно вычисленное" " значение ln(2): %g\n", *pd); return (pthread_barrier_destroy( &mbrr)); } |
| Листинг 2.41. Пример программы, использующей барьеры. |
| Закрыть окно |
|
#include 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 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 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 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 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 #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 |
| Листинг 3.12. Описание функции sigqueue(). |
| Закрыть окно |
|
#include 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 |
| Листинг 3.14. Описание функции sigaltstack(). |
| Закрыть окно |
|
#include 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 int sigsetjmp ( sigjmp_buf env, int savemask); void siglongjmp (sigjmp_buf env, int val); |
| Листинг 3.16. Описание функций sigsetjmp() и siglongjmp(). |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * * * */ /* Многопотоковый вариант обеда философов */ /* с использованием сигналов реального времени */ /* * * * * * * * * * * * * * * * * * * * * * * */ #include /* Число обедающих философов */ #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 |
| Листинг 3.19. Описание функции clock_nanosleep(). |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * */ /* Организация периодических процессов */ /* с помощью функции clock_nanosleep() */ /* * * * * * * * * * * * * * * * * * * */ #define _XOPEN_SOURCE 600 #include /* * * * * * * * * * * * * * * * * * * * */ /* Сложение двух структур типа 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 |
| Листинг 3.22. Описание функции timer_create(). |
| Закрыть окно |
|
#include |
| Листинг 3.23. Описание функции timer_delete(). |
| Закрыть окно |
|
#include 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 /* Число обедающих философов */ #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. Возможные результаты выполнения программы, реализующей обед философов с использованием сигналов реального времени и таймера. |
| Закрыть окно |
|
#include |
| Листинг 4.1. Описание функции mq_open(). |
| Закрыть окно |
|
#include int mq_getattr ( mqd_t mqdes, struct mq_attr *mqstat); int mq_setattr ( mqd_t mqdes, const struct mq_attr *restrict mqstat, struct mq_attr *restrict omqstat); |
| Листинг 4.2. Описание функций mq_getattr() и mq_setattr(). |
| Закрыть окно |
| #include |
| Листинг 4.3. Описание функции mq_close(). |
| Закрыть окно |
| #include |
| Листинг 4.4. Описание функции mq_unlink(). |
| Закрыть окно |
| #include |
| Листинг 4.5. Описание функции mq_send(). |
| Закрыть окно |
| #include |
| Листинг 4.6. Описание функции mq_timedsend(). |
| Закрыть окно |
|
#include |
| Листинг 4.7. Описание функции mq_receive(). |
| Закрыть окно |
|
#include |
| Листинг 4.8. Описание функции mq_timedreceive(). |
| Закрыть окно |
| #include |
| Листинг 4.9. Описание функции mq_notify(). |
| Закрыть окно |
| /* * * * * * * * * * * * * * * * * * * * * * * * */ /* Программа принимает сообщения */ /* и суммирует содержащиеся в них целые числа. */ /* Массивы случайных целых чисел */ /* генерируют несколько потоков управления. */ /* Каждый поток использует свою очередь сообщений.*/ /* Используется режим без блокировки, */ /* с уведомлениями о появлении сообщений в очереди*/ /* * * * * * * * * * * * * * * * * * * * * * * * */ #include /* Число потоков управления, порождающих случайные числа */ #define PT_N MQ_OPEN_MAX /* Число сообщений, генерируемых каждым потоком управления */ #define MSG_N 128 /* Длина имени очереди сообщений */ #define MQ_NAME_LENGTH PATH_MAX /* Количество целых чисел в одном сообщении */ #define MSG_INT_SIZE 32 /* Максимальное число сообщений в очереди */ #define MQ_MSGS_MAX 16 /* Приоритет порождаемого сообщения */ #define prio_rnd (rand () % MQ_PRIO_MAX) /* Номер сигнала, используемого для уведомлений */ #define SIG_MQ_NOTIFY SIGRTMIN /* Массив идентификаторов очередей сообщений */ static mqd_t mq_des [PT_N]; /* Массив структур для задания уведомлений */ /* о поступлении сообщений в очереди */ static struct sigevent evnt_mq_notify [PT_N]; /* Мьютекс, используемый для синхронизации */ /* доступа к переменной sum */ static pthread_mutex_t sm_mutex = PTHREAD_MUTEX_INITIALIZER; /* Общий результат суммирования */ static int sum = 0; /* * * * * * * * * * * * * * * * * * * * * * * */ /* Функция, вызываемая при получении уведомления*/ /* о том, что очередь сообщений стала непустой. */ /* Номер очереди передается как аргумент */ /* * * * * * * * * * * * * * * * * * * * * * * */ static void msg_arrvd (union sigval pt_nm) { int msg_buf [MSG_INT_SIZE]; mqd_t mqdes; unsigned int msg_prio; int msg_sum = 0; ssize_t msg_size; int i; mqdes = mq_des [pt_nm.sival_int]; /* Примем и обработаем имеющиеся сообщения, */ /* а затем снова зарегистрируемся на получение */ /* такого же уведомления */ while ((msg_size = mq_receive (mqdes, (char *) msg_buf, MSG_INT_SIZE * sizeof (int), &msg_prio)) > 0) { for (i = 0; i < (msg_size / (signed int) sizeof (int)); i++) { msg_sum += msg_buf [i]; } msg_sum *= msg_prio; } if ((errno = pthread_mutex_lock (&sm_mutex)) != 0) { perror ("PTHREAD_MUTEX_LOCK"); } sum += msg_sum; if ((errno = pthread_mutex_unlock (&sm_mutex)) != 0) { perror ("PTHREAD_MUTEX_UNLOCK"); } if (mq_notify (mqdes, &evnt_mq_notify [pt_nm.sival_int]) != 0) { perror ("MQ_NOTIFY"); } } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Стартовая функция потока, генерирующего числа и */ /* посылающего сообщения. */ /* Аргумент - номер очереди сообщений */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ void *start_sender (void *pt_nm) { int msg_buf [MSG_INT_SIZE]; int i, j; /* Сформируем и пошлем заданное число сообщений */ /* (проверяя, не переполнилась ли очередь)*/ for (j = 0; j < MSG_N; j++) { for (i = 0; i < MSG_INT_SIZE; i++) { msg_buf [i] = rand (); } if (mq_send (mq_des [(int) pt_nm], (char *) msg_buf, MSG_INT_SIZE * sizeof (int), prio_rnd) != 0) { perror ("MQ_SEND"); return (NULL); } } return (NULL); } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Создание очереди сообщений, */ /* регистрация на получение уведомлений, */ /* создание и ожидание завершения потоков управления */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ int main (void) { /* Массив идентификаторов порождаемых */ /* потоков. Эти потоки будут */ pthread_t pt_mqs [PT_N]; /* генерировать сообщения*/ /* Массив для генерации и хранения */ /* имен очередей сообщений */ char mq_name [PT_N] [MQ_NAME_LENGTH]; struct mq_attr mqattrs; /* Атрибуты создаваемых очередей */ int i; for (i = 0; i < PT_N; i++) { /* Создадим очереди сообщений */ /* и зарегистрируемся на получение уведомлений */ sprintf (mq_name [i], "g%d", i); mqattrs.mq_flags = O_NONBLOCK; mqattrs.mq_maxmsg = MQ_MSGS_MAX; mqattrs.mq_msgsize = MSG_INT_SIZE * sizeof (int); mqattrs.mq_curmsgs = 0; if ((mq_des [i] = mq_open (mq_name [i], O_RDWR | O_CREAT | O_NONBLOCK, 0777, &mqattrs)) == (mqd_t) (-1)) { perror ("MQ_OPEN"); return (-1); } /* Сформируем структуру evnt_mq_notify */ evnt_mq_notify [i].sigev_notify = SIGEV_THREAD; evnt_mq_notify [i].sigev_signo = SIG_MQ_NOTIFY; evnt_mq_notify [i].sigev_value.sival_int = i; evnt_mq_notify [i].sigev_notify_function = msg_arrvd; evnt_mq_notify [i].sigev_notify_attributes = NULL; if (mq_notify (mq_des [i], &evnt_mq_notify [i]) != 0) { perror ("MQ_NOTIFY_MAIN"); return (-1); } /* Создадим потоки управления */ if ((errno = pthread_create (&pt_mqs [i], NULL, start_sender, (void *) i)) != 0) { perror ("PTHREAD_CREATE"); return (i); } } /* for */ /* Ожидание завершения */ for (i = 0; i < PT_N; i++) { (void) pthread_join (pt_mqs [i], NULL); } /* Закроем дескрипторы и удалим очереди */ for (i = 0; i < PT_N; i++) { (void) mq_close (mq_des [i]); (void) mq_unlink (mq_name [i]); } printf ("Общая сумма: %d\n", sum); if ((errno = pthread_mutex_destroy (&sm_mutex)) != 0) { perror ("PTHREAD_MUTEX_DESTROY"); return (errno); } return 0; } |
| Листинг 4.10. Пример программы, использующей очереди сообщений. |
| Закрыть окно |
|
#include sem_t *sem_open (const char *name, int oflag, ...); int sem_init (sem_t *sem, int pshared, unsigned value); |
| Листинг 4.11. Описание функций sem_open() и sem_init(). |
| Закрыть окно |
| #include int sem_close (sem_t *sem); int sem_unlink (const char *name); int sem_destroy (sem_t *sem); |
| Листинг 4.12. Описание функций закрытия и ликвидации семафоров. |
| Закрыть окно |
| #include int sem_wait (sem_t *sem); int sem_trywait (sem_t *sem); |
| Листинг 4.13. Описание функций захвата семафоров. |
| Закрыть окно |
| #include |
| Листинг 4.14. Описание функции захвата семафора с контролем времени ожидания. |
| Закрыть окно |
| #include |
| Листинг 4.15. Описание функции sem_post(). |
| Закрыть окно |
| #include |
| Листинг 4.16. Описание функции sem_getvalue(). |
| Закрыть окно |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Программа реализует взаимодействие */ /* поставщик/потребитель (писатель/читатель). */ /* Поставщик генерирует случайные целые числа и помещает их буфер */ /* на один элемент, потребитель извлекает их оттуда и суммирует. */ /* Для синхронизации используются неименованные семафоры*/ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ #include /* Семафор, разрешающий записывать в буфер новые данные */ static sem_t w_sem; /* Семафор, разрешающий читать данные из буфера */ static sem_t r_sem; /* Общий результат суммирования */ static int sum = 0; /* * * * * * * * * * * * * * * * * * * * * * * */ /* Стартовая функция потока, генерирующего числа */ /* * * * * * * * * * * * * * * * * * * * * * * */ void *start_writer (void *dummy) { while (sem_wait (&w_sem) == 0) { my_buf = rand (); if (sem_post (&r_sem) != 0) { perror ("SEM_POST-R"); return (NULL); } } return (NULL); } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Стартовая функция потока, читающего и суммирующего числа */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ void *start_reader (void *dummy) { while (sem_wait (&r_sem) == 0) { sum += my_buf; if (sem_post (&w_sem) != 0) { perror ("SEM_POST-W"); return (NULL); } } return (NULL); } /* * * * * * * * * * * * * * * * * * * * * * * */ /* Инициализация семафоров, */ /* создание и терминирование потоков управления*/ /* * * * * * * * * * * * * * * * * * * * * * * */ int main (void) { pthread_t w_ptid;/* Идентификатор потока-писателя */ pthread_t r_ptid;/* Идентификатор потока-читателя */ /* Инициализируем семафоры. */ /* Семафор записи будет свободен,*/ /* семафор чтения - захвачен */ if (sem_init (&w_sem, 0, 1) == -1) { perror ("SEM_INIT-W"); return (-1); } if (sem_init (&r_sem, 0, 0) == -1) { perror ("SEM_INIT-R"); return (-1); } /* Создадим потоки управления - писателя и читателя */ if ((errno = pthread_create (&w_ptid, NULL, start_writer, NULL)) != 0) { perror ("PTHREAD_CREATE-W"); return (errno); } if ((errno = pthread_create (&r_ptid, NULL, start_reader, NULL)) != 0) { perror ("PTHREAD_CREATE-R"); return (errno); } /* Дадим потокам повыполняться */ sleep (10); /* Терминируем потоки */ (void) pthread_cancel (w_ptid); (void) pthread_cancel (r_ptid); /* Дождемся завершения потоков */ (void) pthread_join (w_ptid, NULL); (void) pthread_join (r_ptid, NULL); /* Ликвидируем семафоры */ if (sem_destroy (&w_sem) != 0) { perror ("SEM_DESTROY-W"); return (-1); } if (sem_destroy (&r_sem) != 0) { perror ("SEM_DESTROY-R"); return (-1); } printf ("Сумма сгенерированных чисел: %d\n", sum); return 0; } |
| Листинг 4.17. Пример программы, реализующей взаимодействие поставщик/потребитель. |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * * * */ /* Программа реализует взаимодействие */ /* поставщик/потребитель (писатель/читатель). */ /* Поставщик генерирует случайные целые числа */ /* и помещает их в кольцевой буфер, */ /* потребитель извлекает их оттуда и суммирует.*/ /* Для синхронизации используются */ /* неименованные семафоры */ /* * * * * * * * * * * * * * * * * * * * * * * */ #include /* Буфер для хранения генерируемых данных */ static int my_buf [BUFSIZ]; /* Индекс, по которому можно записать очередной элемент */ static int w_ind = 0; /* Индекс, по которому можно прочитать очередной элемент */ static int r_ind = 0; /* Семафор, разрешающий записывать в буфер новые данные */ static sem_t w_sem; /* Семафор, разрешающий читать данные из буфера */ static sem_t r_sem; /* Общий результат суммирования */ static int sum = 0; /* * * * * * * * * * * * * * * * * * * * * * * */ /* Стартовая функция потока, генерирующего числа*/ /* * * * * * * * * * * * * * * * * * * * * * * */ void *start_writer (void *dummy) { while (sem_wait (&w_sem) == 0) { my_buf [w_ind] = rand (); w_ind = (w_ind + 1) % BUFSIZ; if (sem_post (&r_sem) != 0) { perror ("SEM_POST-R"); return (NULL); } } return (NULL); } /* * * * * * * * * * * * * * * */ /* Стартовая функция потока, */ /* читающего и суммирующего числа*/ /* * * * * * * * * * * * * * * */ void *start_reader (void *dummy) { while (sem_wait (&r_sem) == 0) { sum += my_buf [r_ind]; r_ind = (r_ind + 1) % BUFSIZ; if (sem_post (&w_sem) != 0) { perror ("SEM_POST-W"); return (NULL); } } return (NULL); } /* * * * * * * * * * * * * * * * * * * * * * * */ /* Инициализация семафоров, */ /* создание и терминирование потоков управления*/ /* * * * * * * * * * * * * * * * * * * * * * * */ int main (void) { pthread_t w_ptid;/* Идентификатор потока-писателя */ pthread_t r_ptid;/* Идентификатор потока-читателя */ /* Инициализируем семафоры. */ /* Семафор записи будет свободен,*/ /* разрешая заполнить весь буфер,*/ /* семафор чтения - захвачен */ if (sem_init (&w_sem, 0, BUFSIZ) == -1) { perror ("SEM_INIT-W"); return (-1); } if (sem_init (&r_sem, 0, 0) == -1) { perror ("SEM_INIT-R"); return (-1); } /* Создадим потоки управления - писателя и читателя */ if ((errno = pthread_create (&w_ptid, NULL, start_writer, NULL)) != 0) { perror ("PTHREAD_CREATE-W"); return (errno); } if ((errno = pthread_create (&r_ptid, NULL, start_reader, NULL)) != 0) { perror ("PTHREAD_CREATE-R"); return (errno); } /* Дадим потокам повыполняться */ sleep (10); /* Терминируем потоки */ (void) pthread_cancel (w_ptid); (void) pthread_cancel (r_ptid); /* Дождемся завершения потоков */ (void) pthread_join (w_ptid, NULL); (void) pthread_join (r_ptid, NULL); /* Ликвидируем семафоры */ if (sem_destroy (&w_sem) != 0) { perror ("SEM_DESTROY-W"); return (-1); } if (sem_destroy (&r_sem) != 0) { perror ("SEM_DESTROY-R"); return (-1); } printf ("Сумма сгенерированных чисел: %d\n", sum); return 0; } |
| Листинг 4.18. Пример программы, реализующей взаимодействие поставщик/потребитель с помощью целочисленных семафоров. |
| Закрыть окно |
|
/* Обедающие философы. Многопотоковая реализация с помощью семафоров. Запуск: mudrecSem [-a | -p | -I] [-t число_секунд] имя_философа ... Опции: -t число_секунд - сколько секунд моделируется Стратегии захвата вилок: -a - сначала захватывается вилка с меньшим номером; -p - сначала захватывается нечетная вилка; -I - некорректная (но эффективная) интеллигентная стратегия: во время ожидания уже захваченная вилка кладется. Пример запуска: mudrecSem -p -t 300 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: mudrecSem.c,v 1.2 2004/03/18 10:28:38 sambor Exp $"; #include #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)>(b)?(b):(a)) struct mudrec { char *name; int left_fork, right_fork; int eat_time, wait_time, think_time, max_wait_time; int count; pthread_t thread; int private_pFdIn; } *kafedra; /* Глобальные счетчики и логические переменные */ int Stop = 0; /* Признак конца обеда */ /* Различные дескрипторы */ int protokol [2] = {-1, -1}; #define pFdIn (protokol [1]) #define pFdOut (protokol [0]) /* Массив семафоров для синхронизации доступа к вилкам */ sem_t *semFork; /* Разные алгоритмы захвата вилок */ static void get_forks_simple (struct mudrec *this); static void get_forks_odd (struct mudrec *this); static void get_forks_maybe_infinit_time (struct mudrec *this); /* Используемый метод захвата вилок */ void (*get_forks) (struct mudrec *this) = get_forks_simple; /* Возвращение вилок */ static void put_forks (struct mudrec *this); /* * Потоки-философы */ void *filosof (void *arg) { struct mudrec *this = arg; char buffer [LINE_MAX]; int bytes; int private_pFdIn = this->private_pFdIn; while (!Stop) { /* Пора подкрепиться */ { int wait_time, tm = time (NULL); sprintf (buffer, "%s: хочет есть\n", this->name); bytes = write (private_pFdIn, buffer, strlen (buffer)); (*get_forks) (this); wait_time = time (NULL) - 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 (private_pFdIn, buffer, strlen (buffer)); } /* Может, обед уже закончился? */ if (Stop) { put_forks (this); break; } /* Ест */ { int eat_time = rand () % 20 + 1; sleep (eat_time); this->eat_time += eat_time; this->count++; sprintf (buffer,"%s: ел %d сек\n", this->name, eat_time); bytes = write (private_pFdIn, buffer, strlen (buffer)); } /* Отдает вилки */ put_forks (this); if (Stop) break; /* Размышляет */ { int think_time = rand () % 10 + 1; sleep (think_time); this->think_time += think_time; } } /* while (!Stop) */ sprintf (buffer,"%s: уходит\n", this->name); bytes = write (private_pFdIn, buffer, strlen (buffer)); close (private_pFdIn); return (NULL); } /* Поток-философ */ /* Кладет вилки одну за другой */ static void put_forks (struct mudrec *this) { sem_post (&semFork [this->left_fork - 1]); sem_post (&semFork [this->right_fork - 1]); } /* Берет вилки по очереди в порядке номеров */ static void get_forks_simple (struct mudrec *this) { int first = min (this->left_fork, this->right_fork); int last = max (this->left_fork, this->right_fork); sem_wait (&semFork [first - 1]); sem_wait (&semFork [last - 1]); } /* Берем сначала нечетную вилку */ /* (если обе нечетные - то с большим номером) */ static void get_forks_odd (struct mudrec *this) { int left = this->left_fork, right = this->right_fork; int first; int last; if ((left & 1) > (right & 1)) { first = left; last = right; } else if ((left & 1) < (right & 1)) { first = right; last = left; } else { first = max (left, right); last = min (left, right); } sem_wait (&semFork [first - 1]); sem_wait (&semFork [last - 1]); } /* Берем вилки по очереди, в произвольном порядке. * Но если вторая вилка не берется сразу, то кладем первую. * То есть философ не расходует вилочное время впустую. */ static void get_forks_maybe_infinit_time (struct mudrec *this) { int left = this->left_fork, right = this->right_fork; for (;;) { sem_wait (&semFork [left - 1]); if (0 == sem_trywait (&semFork [right - 1])) return; sem_post (&semFork [left - 1]); sem_wait (&semFork [right - 1]); if (0 == sem_trywait (&semFork [left - 1])) return; sem_post (&semFork [right - 1]); } } /* Мелкие служебные функции */ static void stop (int dummy) { Stop = 1; } static void usage (char name []) { fprintf (stderr, "Использование: %s [-a | -p | -I] [-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; int nMudr; struct sigaction sact; while ((c = getopt (argc, argv, "apIt:")) != -1) { switch (c) { case 'a': get_forks = get_forks_simple; break; case 'p': get_forks = get_forks_odd; break; case 'I': get_forks = get_forks_maybe_infinit_time; 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); kafedra = calloc (sizeof (struct mudrec), nMudr); /* Зачисление на кафедру */ for (i = 0; i < nMudr; i++, optind++) { kafedra [i].name = argv [optind]; /* Выдадим телефон */ kafedra [i].private_pFdIn = fcntl (pFdIn, F_DUPFD, 0); /* Укажем новичку, какими вилками пользоваться */ kafedra [i].left_fork = i + 1; kafedra [i].right_fork = i + 2; } kafedra [nMudr - 1].right_fork = 1; /* Последний*/ /* пользуется вилкой первого */ /* Зададим реакцию на сигналы и установим будильник */ /* на конец обеда */ 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); /* Создадим семафоры для охраны вилок */ semFork = calloc (sizeof (sem_t), nMudr); for (i = 0; i < nMudr; i++) { sem_init (&semFork [i], 0, 1 /* На каждое место */ /* по одной вилке */); } /* Философы входят в столовую */ for (i = 0; i < nMudr; i++) pthread_create (&kafedra [i].thread, NULL, &filosof, (void *) &kafedra [i]); /* Выдача сообщений на стандартный вывод и выход */ /* после окончания всех задач */ close (pFdIn); while (1) { n = read (pFdOut, buffer, LINE_MAX); if (n == 0 || (n < 0 && errno != EINTR)) break; for (i = 0; i < n; i++) putchar (buffer [i]); } close (pFdOut); /* Уничтожение семафоров */ for (i = 0; i < nMudr; i++) { sem_destroy (&semFork [i]); } /* Выдача сводной информации */ { int full_eating_time = 0; int full_waiting_time = 0; int full_thinking_time = 0; for (i = 1; i <= nMudr; i++) { struct mudrec *this = &kafedra [i - 1]; 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); } /* for */ { float total_time = (full_eating_time + full_waiting_time + full_thinking_time) / (float) nMudr; printf("Среднее число одновременно едящих = %.3f\n", full_eating_time / total_time); printf("Среднее число одновременно ждущих = %.3f\n", full_waiting_time / total_time); } } /* Выдача сводной информации */ free (semFork); free (kafedra); /* Сообщим об окончании работы. */ printf ("Конец обеда\n"); return 0; } |
| Листинг 4.19. Многопотоковый вариант решения задачи об обедающих философах с использованием семафоров реального времени. |
| Закрыть окно |
|
#include int shm_open (const char *name, int oflag, mode_t mode); int shm_unlink (const char *name); |
| Листинг 4.20. Описание функций shm_open() и shm_unlink(). |
| Закрыть окно |
|
#ifndef g_SHM #define g_SHM /* Имя объекта в разделяемой памяти */ #define O_SHM_NAME "/g_o.shm" /* Используемый номер сигнала реального времени */ #define SIG_SHM SIGRTMIN /* Используемые значения сигнала реального времени */ #define SIGVAL_LINE 0 #define SIGVAL_EOF EOF #endif |
| Листинг 4.21. Заголовочный файл "g_shm.h" программы, копирующей строки со стандартного ввода на стандартный вывод. |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Программа, состоящая из двух процессов, копирует */ /* строки со стандартного ввода на стандартный вывод,*/ /* "прокачивая" их через разделяемый сегмент памяти. */ /* Для синхронизации доступа к разделяемому сегменту */ /* используются сигналы реального времени */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ #include #include "g_shm.h" /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Создание разделяемого сегмента памяти, */ /* чтение со стандартного ввода и запись строк в сегмент*/ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ int main (void) { int fd_shm; /* Дескриптор объекта в разделяемой памяти*/ FILE *fp; /* Поток для записи в объект */ char line [LINE_MAX]; /* Буфер для копируемых строк */ struct sigaction sact; /* Структура для обработки сигналов */ union sigval sg_val;/* Значение сигнала */ int sg_no; /* Номер принятого сигнала */ pid_t cpid;/* Идентификатор порожденного процесса */ /* Создадим разделяемый сегмент памяти */ if ((fd_shm = shm_open (O_SHM_NAME, O_RDWR | O_CREAT, 0777)) < 0) { perror ("SHM_CREAT"); return (1); } /* Сформируем поток данных по файловому дескриптору */ /* объекта в разделяемой памяти */ assert ((fp = fdopen (fd_shm, "w")) != NULL); /* Отменим буферизацию вывода */ setbuf (fp, NULL); /* Сформируем маску сигналов (блокируем SIG_SHM) */ (void) sigemptyset (&sact.sa_mask); (void) sigaddset (&sact.sa_mask, SIG_SHM); (void) sigprocmask (SIG_BLOCK, &sact.sa_mask, (sigset_t *) NULL); /* Установим для сигнала SIG_SHM флаг SA_SIGINFO */ sact.sa_flags = SA_SIGINFO; sact.sa_sigaction = (void (*) (int, siginfo_t *, void *)) SIG_DFL; (void) sigaction (SIG_SHM, &sact, (struct sigaction *) NULL); /* Подготовительная работа закончена */ switch (cpid = fork ()) { case -1: perror ("FORK"); return (2); case 0: /* Чтение из объекта и выдачу на стандартный */ /* вывод реализуем в порожденном процессе */ if (execl ("./g_r_shm", "g_r_shm", (char *) NULL) < 0) { perror ("EXECL"); return (3); } } /* Чтение со стандартного ввода и запись в объект */ /* возложим на родительский процесс.*/ /* В начальный момент объект в разделяемой памяти*/ /* доступен для записи */ assert (fseek (fp, 0, SEEK_SET) == 0); fputs ("Вводите строки\n", fp); /* Сообщим порожденному процессу,*/ /* что объект в разделяемой памяти заполнен*/ sg_val.sival_int = SIGVAL_LINE; assert (sigqueue (cpid, SIG_SHM, sg_val) == 0); while (fgets (line, sizeof (line), stdin) != NULL) { assert (fseek (fp, 0, SEEK_SET) == 0); /* Дождемся, когда в объект можно будет писать */ if ((sigwait (&sact.sa_mask, &sg_no) != 0) || (sg_no != SIG_SHM)) { return (4); } assert (fputs ("Вы ввели: ", fp) != EOF); assert (fputs (line, fp) != EOF); assert (sigqueue (cpid, SIG_SHM, sg_val) == 0); } /* Сообщим о конце файла */ sg_val.sival_int = SIGVAL_EOF; assert (sigqueue (cpid, SIG_SHM, sg_val) == 0); fclose (fp); (void) wait (NULL); if (shm_unlink (O_SHM_NAME) != 0) { perror ("SHM_UNLINK"); return (7); } return (0); } |
| Листинг 4.22. Исходный текст начального процесса двухпроцессной программы, копирующей строки со стандартного ввода на стандартный вывод. |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Процесс читает строки из объекта в разделяемой памяти */ /* и копирует их на стандартный вывод */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ #include #include "g_shm.h" /* * * * * * * * * * * * * * * * * * * * */ /* Открытие разделяемого сегмента памяти,*/ /* чтение из сегмента и выдача строк */ /* на стандартный вывод */ /* * * * * * * * * * * * * * * * * * * * */ int main (void) { int fd_shm; /* Дескриптор объекта */ /* в разделяемой памяти */ FILE *fp; /* Поток для чтения из объекта */ char line [LINE_MAX];/* Буфер для копируемых строк */ sigset_t smask; /* Маска ожидаемых сигналов */ siginfo_t sinfo; /* Структура для получения */ /* данных о сигнале */ pid_t ppid; /* Идентификатор родительского */ /* процесса */ /* Откроем разделяемый сегмент памяти */ if ((fd_shm = shm_open (O_SHM_NAME, O_RDONLY, 0777)) < 0) { perror ("SHM_OPEN"); return (1); } /* Сформируем поток по файловому дескриптору объекта */ /* в разделяемой памяти */ assert ((fp = fdopen (fd_shm, "r")) != NULL); /* Отменим буферизацию ввода */ setbuf (fp, NULL); /* Запомним идентификатор родительского процесса */ ppid = getppid (); /* Сформируем маску ожидаемых сигналов (SIG_SHM) */ (void) sigemptyset (&smask); (void) sigaddset (&smask, SIG_SHM); /* Подготовительная работа закончена */ while ((fseek (fp, 0, SEEK_SET) == 0) && /* Дождемся, когда из объекта можно будет читать */ (sigwaitinfo (&smask, &sinfo) == SIG_SHM) && /* И прочитаем строку, а не конец файла */ (sinfo.si_value.sival_int == SIGVAL_LINE)) { (void) fgets (line, sizeof (line), fp); /* Сообщим родительскому процессу, */ /* что данные из объекта извлечены */ assert (kill (ppid, SIG_SHM) == 0); /* Выдадим, наконец, строку на стандартный вывод */ assert (fputs (line, stdout) != EOF); } fclose (fp); return 0; } |
| Листинг 4.23. Исходный текст порождаемого процесса (файл g_r_shm.c) двухпроцессной программы, копирующей строки со стандартного ввода на стандартный вывод. |
| Закрыть окно |

|
#include |
| Листинг 5.1. Описание функции mmap(). |
| Закрыть окно |
| #include |
| Листинг 5.2. Описание функции mprotect(). |
| Закрыть окно |
| #include |
| Листинг 5.3. Описание функции munmap(). |
| Закрыть окно |
| /* * * * * * * * * * * * * * * * * * * * * * * */ /* Программа выдает на стандартный вывод файлы,*/ /* имена которых заданы в командной строке */ /* * * * * * * * * * * * * * * * * * * * * * * */ #include int main (int argc, char *argv []) { struct stat sbuf; /* Структура для получения информации */ /* о файле */ int fd; /* Дескриптор обрабатываемого файла */ void *maddr; /* Адрес отображенного в память файла */ int i; for (i = 1; i < argc; i++) { if ((fd = open (argv [i], O_RDONLY)) < 0) { perror ("OPEN"); continue; } if (fstat (fd, &sbuf) != 0) { perror ("FSTAT"); close (fd); continue; } if ((maddr = mmap (NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED){ perror ("MMAP"); close (fd); continue; } if (write (STDOUT_FILENO, maddr, sbuf.st_size) != sbuf.st_size) { perror ("WRITE"); } if (munmap (maddr, sbuf.st_size) != 0) { perror ("MUNMAP"); } close (fd); } /* for */ return 0; } |
| Листинг 5.4. Пример программы, использующей файлы, отображенные в память. |
| Закрыть окно |
|
#include int truncate (const char *path, off_t length); int ftruncate ( int fildes, off_t length); |
| Листинг 5.5. Описание функций truncate() и ftruncate. |
| Закрыть окно |
| #ifndef g_SHM #define g_SHM /* Имя объекта в разделяемой памяти */ #define O_SHM_NAME "/g_o.shm" /* Номер сигнала для синхронизации /* доступа к разделяемому сегменту */ #define SIG_SHM SIGALRM #endif |
| Листинг 5.6. Заголовочный файл "g_shm.h" программы, копирующей строки со стандартного ввода на стандартный вывод. |
| Закрыть окно |
| /* * * * * * * * * * * * * * * * * * * * * */ /* Программа, состоящая из двух процессов, */ /* копирует строки со стандартного ввода на*/ /* стандартный вывод, используя в качестве */ /* буфера разделяемый сегмент, отображенный*/ /* в адресные пространства процессов. */ /* Для синхронизации доступа к разделяемому*/ /* сегменту используется сигнал SIGALRM */ /* * * * * * * * * * * * * * * * * * * * * */ #include #include "g_shm.h" /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Создание разделяемого сегмента памяти, */ /* отображение его в адресное пространство процесса, */ /* чтение строк со стандартного ввода в сегмент */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ int main (void) { int fd_shm; /* Дескриптор объекта в разделяемой памяти */ void *addr_shm; /* Адрес отображенного в память объекта */ sigset_t smask; /* Маска блокируемых сигналов */ siginfo_t sinfo;/* Структура для получения данных о сигнале */ /* Длительность ожидания прихода сигнала */ struct timespec stmspc = {10, 0}; pid_t cpid; /* Идентификатор порожденного процесса */ /* Создадим разделяемый сегмент памяти */ if ((fd_shm = shm_open (O_SHM_NAME, O_RDWR | O_CREAT, 0777)) < 0) { perror ("SHM_CREAT"); return (1); } /* Сделаем размер созданного объекта равным LINE_MAX */ if (ftruncate (fd_shm, LINE_MAX) != 0) { perror ("FTRUNCATE"); return (2); } /* Отобразим сегмент в память */ if ((addr_shm = mmap (NULL, LINE_MAX, PROT_READ | PROT_WRITE, MAP_SHARED, fd_shm, 0)) == MAP_FAILED) { perror ("MMAP-P"); return (3); } /* Сформируем маску сигналов (блокируем SIG_SHM) */ (void) sigemptyset (&smask); (void) sigaddset (&smask, SIG_SHM); (void) sigprocmask (SIG_BLOCK, &smask, (sigset_t *) NULL); /* Подготовительная работа закончена */ switch (cpid = fork ()) { case -1: perror ("FORK"); return (4); case 0: /* Чтение из объекта и выдачу на стандартный*/ /* вывод реализуем в порожденном процессе*/ if (execl ("./g_r_shm", "g_r_shm", ( char *) NULL) < 0) { perror ("EXECL"); return (5); } } /* Чтение строк со стандартного ввода в объект */ /* возложим на родительский процесс. */ /* В начальный момент объект в разделяемой памяти */ /* доступен для записи*/ while (fgets (addr_shm, LINE_MAX, stdin) != NULL) { /* Сообщим порожденному процессу, */ /* что в объект в разделяемой памяти */ /* помещена очередная строка */ assert (kill (cpid, SIG_SHM) == 0); /* Дождемся, когда в объект можно будет прочитать*/ /* следующую строку */ if (sigtimedwait (&smask, &sinfo, &stmspc) != SIG_SHM) { break; } } /* Порожденный процесс должен завершиться*/ /* по контролю времени ожидания*/ (void) wait (NULL); if (shm_unlink (O_SHM_NAME) != 0) { perror ("SHM_UNLINK"); return (6); } return (0); } |
| Листинг 5.7. Исходный текст начального процесса двухпроцессной программы, копирующей строки со стандартного ввода на стандартный вывод. |
| Закрыть окно |
| /* * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Процесс берет строки из объекта в разделяемой памяти*/ /* и выдает их на стандартный вывод */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include #include "g_shm.h" /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Открытие разделяемого сегмента памяти, */ /* отображение его в адресное пространство процесса, */ /* чтение из сегмента и выдача строк на стандартный вывод */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ int main (void) { int fd_shm; /* Дескриптор объекта в разделяемой */ /* памяти */ void *addr_shm; /* Адрес отображенного в память */ /* объекта */ sigset_t smask; /* Маска ожидаемых сигналов */ siginfo_t sinfo; /* Структура для получения данных*/ /* о сигнале */ /* Длительность ожидания строки */ struct timespec stmspc = {10, 0}; pid_t ppid; /* Идентификатор родительского процесса */ /* Откроем разделяемый сегмент памяти */ if ((fd_shm = shm_open (O_SHM_NAME, O_RDONLY, 0777)) < 0) { perror ("SHM_OPEN"); return (1); } /* Отобразим сегмент в память */ if ((addr_shm = mmap (NULL, LINE_MAX, PROT_READ, MAP_SHARED, fd_shm, 0)) == MAP_FAILED) { perror ("MMAP-C"); return (2); } /* Запомним идентификатор родительского процесса */ ppid = getppid (); /* Сформируем маску ожидаемых сигналов (SIG_SHM) */ (void) sigemptyset (&smask); (void) sigaddset (&smask, SIG_SHM); /* Подготовительная работа закончена */ fputs ("Вводите строки\n", stdout); while (sigtimedwait (&smask, &sinfo, &stmspc) == SIG_SHM) { /* Дождались, когда в объекте появилась строка.*/ /* Выдадим ее на стандартный вывод*/ assert (fputs ("Вы ввели: ", stdout) != EOF); assert (fputs (addr_shm, stdout) != EOF); /* Сообщим родительскому процессу, */ /* что данные из объекта извлечены */ assert (kill (ppid, SIG_SHM) == 0); } return 0; } |
| Листинг 5.8. Исходный текст порождаемого процесса (файл g_r_shm.c) двухпроцессной программы, копирующей строки со стандартного ввода на стандартный вывод. |
| Закрыть окно |
|
#include |
| Листинг 5.9. Описание функции msync(). |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Программа реализует некоторые функции систем управления базами*/ /* данных в памяти, следуя принципу неуничтожения информации */ /* (изменения записываются не в сам объет, а в его новую версию) */ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include /* Размер страницы */ static int pgsz; /* Структура для получения */ /* информации об исходном файле*/ static struct stat sbuf_src; /* Адрес отображенного в память исходного файла*/ static char *saddr; /* Признак того, что выполнялась функция */ /* обработки сигнала SIGSEGV */ static char sigsegv_catching; /* * * * * * * * * * * * * * * * * * * * */ /* Функция обработки сигнала SIGSEGV. */ /* Предполагается, что этот сигнал */ /* генерируется при попытке записи в */ /* страницы, доступные только на чтение. */ /* Функция добавляет разрешение на запись*/ /* * * * * * * * * * * * * * * * * * * * */ static void sigsegv_sigaction (int sig, siginfo_t *sig_info, void *addr) { void *page_addr; /* Адрес страницы, в которую*/ /*пытались писать*/ page_addr = (char *) sig_info->si_addr - (off_t) ((char *) sig_info->si_addr - saddr) % pgsz; assert (mprotect (page_addr, pgsz, PROT_READ | PROT_WRITE) == 0); sigsegv_catching = 1; } /* * * * * * * * * * * * * * * * * * * * * * * */ /* Установка способа обработки сигнала SIGSEGV */ /* * * * * * * * * * * * * * * * * * * * * * * */ static int sigsegv_set_proc (void (*sigsegv_actn) (int, siginfo_t *, void *), struct sigaction *old_sigsegv_sact) { struct sigaction sact; (void) sigemptyset (&sact.sa_mask); sact.sa_flags = SA_SIGINFO; sact.sa_sigaction = sigsegv_actn; if (sigaction (SIGSEGV, &sact, old_sigsegv_sact) != 0) { perror ("SIGACTION"); return (-1); } return 0; } /* * * * * * * * * * * * * * * * * * * * * */ /* Функция начала работы с исходным файлом.*/ /* Нормальный результат равен нулю */ /* * * * * * * * * * * * * * * * * * * * * */ static int init_src (char *name_src) { int fd_src; /* Дескриптор исходного файла */ /* Откроем исходный файл и отобразим его в память */ if ((fd_src = open (name_src, O_RDONLY)) < 0) { perror ("OPEN-SRC"); return (-1); } if (fstat (fd_src, &sbuf_src) != 0) { perror ("FSTAT"); return (-1); } if ((saddr = (char *) mmap (NULL, sbuf_src.st_size, PROT_READ, MAP_PRIVATE, fd_src, 0)) == MAP_FAILED) { perror ("MMAP"); return (-1); } return 0; } /* * * * * * * * * * * * * * * * * * */ /* Опрос конфигурационных параметров */ /* * * * * * * * * * * * * * * * * * */ static int init_conf_params (void) { if ((pgsz = sysconf (_SC_PAGESIZE)) == -1) { perror ("SYSCONF"); return (-1); } return 0; } /* * * * * * * * * * * */ /* Общая инициализация */ /* * * * * * * * * * * */ static int init_all (char *name_src) { if ((init_src (name_src) != 0) || (sigsegv_set_proc (sigsegv_sigaction, (struct sigaction *) NULL) != 0) || (init_conf_params () != 0)) { return (-1); } return 0; } /* * * * * * * * * * * * * * * * * */ /* Запись в файл измененных страниц*/ /* * * * * * * * * * * * * * * * * */ static int write_dlt (char *name_dlt) { int fd_dlt; /* Дескриптор файла изменений*/ volatile char tmp; /* Значение пробного байта*/ off_t pos;/* Позиция в отображенной памяти*/ if ((fd_dlt = open (name_dlt, O_CREAT | O_WRONLY | O_TRUNC, 0777)) < 0) { perror ("OPEN-DLT"); return (-1); } /* Измененные страницы выявим путем пробных записей. */ for (pos = 0; pos < sbuf_src.st_size; pos += pgsz) { tmp = saddr [pos]; sigsegv_catching = 0; saddr [pos] = tmp; if (sigsegv_catching == 0) { /* В страницу удалось записать */ /* без генерации сигнала SIGSEGV.*/ /* Значит, она была доступна на запись.*/ /* Следовательно, ее модифицировали.*/ /* Запишем ее в файл изменений */ (void) lseek (fd_dlt, pos, SEEK_SET); assert (write (fd_dlt, saddr + pos, pgsz) == pgsz); } /* Уберем добавленное разрешение на запись */ assert (mprotect (saddr + pos, pgsz, PROT_READ) == 0); } /* for */ return (close (fd_dlt)); } /* * * * * * * * * * * * * */ /* Функция main() вызывает */ /* описанные выше функции */ /* и обращается на запись */ /* к отображенной памяти */ /* * * * * * * * * * * * * */ int main (int argc, char *argv []) { if (argc != 3) { fprintf (stderr, "Использование: %s исходный_файл " "файл_изменений\n", argv [0]); return (-1); } if (init_all (argv [1]) != 0) { fprintf (stderr, "Ошибка инициализации\n"); return (-1); } printf ("Размер страницы: %d\n" "Адрес отображенного в память исходного файла: %p\n" "Длина отображенного в память исходного файла: %ld\n", pgsz, saddr, sbuf_src.st_size); saddr [0] = '*'; return (write_dlt (argv [2])); } |
| Листинг 5.10. Пример программы, использующей файлы, отображенные в память. |
| Закрыть окно |
|
#include |
| Листинг 5.11. Описание функции posix_typed_mem_open(). |
| Закрыть окно |
|
#include |
| Листинг 5.12. Описание функции posix_typed_mem_get_info(). |
| Закрыть окно |
|
#include |
| Листинг 5.13. Описание функции posix_mem_offset(). |
| Закрыть окно |
|
#include |
| Листинг 5.14. Описание функции mlock(). |
| Закрыть окно |
|
#include |
| Листинг 5.15. Описание функции munlock(). |
| Закрыть окно |
|
#include int mlockall (int flags); int munlockall (void); |
| Листинг 5.16. Описание функций mlockall() и munlockall(). |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * * */ /* Программа демонстрирует удержание в памяти*/ /* всего адресного пространства процесса, */ /* в том числе стека с учетом возможного роста*/ /* * * * * * * * * * * * * * * * * * * * * * */ #include #define LOCKED_STACK_SIZE 048576 /* * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Следующая функция нужна, чтобы вызвать рост стека до*/ /* требуемого размера. На всякий случай примем меры для*/ /* защиты от слишком умного оптимизирующего компилятора*/ /* * * * * * * * * * * * * * * * * * * * * * * * * * * */ static int dummy_func (void) { char dummy_arr [LOCKED_STACK_SIZE]; int i; int res = 0; dummy_arr [0] = 0; for (i = 1; i < LOCKED_STACK_SIZE; i++) { dummy_arr [i] = dummy_arr [i - 1] + i; } for (i = 0; i < LOCKED_STACK_SIZE; i++) { res += dummy_arr [i]; } return (res); } /* * * * * * * * * * * * * * * * * * * * * * * * * */ /* Функция main() вызывает вспомогательную функцию */ /* * * * * * * * * * * * * * * * * * * * * * * * * */ int main (void) { if (mlockall (MCL_CURRENT | MCL_FUTURE) != 0) { perror ("MLOCKALL"); return (1); } fprintf (stderr, "Результат вспомогательной функции:" "%d\n", dummy_func ()); return 0; } |
| Листинг 5.17. Пример программы, удерживающей в памяти растущий стек. |
| Закрыть окно |
|
#include int sched_getscheduler (pid_t pid); int sched_getparam (pid_t pid, struct sched_param *param); int sched_setscheduler ( pid_t pid, int policy, const struct sched_param *param); int sched_setparam (pid_t pid, const struct sched_param *param); |
| Листинг 6.1. Описание функций управления планированием. |
| Закрыть окно |
|
#include int sched_get_priority_min (int policy); int sched_get_priority_max (int policy); int sched_rr_get_interval (pid_t pid, struct timespec *q_ptr); |
| Листинг 6.2. Описание функций опроса характеристик политик планирования. |
| Закрыть окно |
|
#include |
| Листинг 6.3. Описание функции sched_yield(). |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Программа опрашивает характеристики политик планирования, */ /* а также атрибуты планирования текущего процесса */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ #include int main (void) { struct sched_param shdprm; /* Значения параметров */ /* планирования */ struct timespec qp; /* Величина кванта */ /* процессорного времени */ printf ("Допустимые диапазоны приоритетов для разных " "политик планирования\n"); printf ("SCHED_FIFO : от %d до %d\n", sched_get_priority_min (SCHED_FIFO), sched_get_priority_max (SCHED_FIFO)); printf ("SCHED_RR : от %d до %d\n", sched_get_priority_min (SCHED_RR), sched_get_priority_max (SCHED_RR)); printf ("SCHED_OTHER: от %d до %d\n", sched_get_priority_min (SCHED_OTHER), sched_get_priority_max (SCHED_OTHER)); printf ("Текущая политика планирования для текущего " "процесса: "); switch (sched_getscheduler (0)) { case SCHED_FIFO: printf ("SCHED_FIFO\n"); break; case SCHED_RR: printf ("SCHED_RR\n"); break; case SCHED_OTHER: printf ("SCHED_OTHER\n"); break; case -1: perror ("SCHED_GETSCHEDULER"); break; default: printf ("Неизвестная политика планирования\n"); } if (sched_getparam (0, &shdprm) == 0) { printf ("Текущий приоритет текущего процесса: %d\n", shdprm.sched_priority); } else { perror ("SCHED_GETPARAM"); } shdprm.sched_priority = 50; if (sched_setscheduler (0, SCHED_RR, &shdprm) == -1) { perror ("SCHED_SETSCHEDULER"); } if (sched_rr_get_interval (0, &qp) == 0) { printf ("Квант процессорного времени при " "циклическом планировании: %g сек\n", qp.tv_sec + qp.tv_nsec / 1000000000.0); } else { perror ("SCHED_RR_GET_INTERVAL"); } return 0; } |
| Листинг 6.4. Пример программы, использующей функции управления планированием. |
| Закрыть окно |
|
Допустимые диапазоны приоритетов для разных политик планирования SCHED_FIFO : от 1 до 99 SCHED_RR : от 1 до 99 SCHED_OTHER : от 0 до 0 Текущая политика планирования для текущего процесса: SCHED_OTHER Текущий приоритет текущего процесса: 0 Квант процессорного времени при циклическом планировании: 0.15 сек |
| Листинг 6.5. Возможные результаты выполнения программы, использующей функции управления планированием, в ОС Linux. |
| Закрыть окно |
| Допустимые диапазоны приоритетов для разных политик планирования SCHED_FIFO : от 1 до 255 SCHED_RR : от 1 до 255 SCHED_OTHER : от 0 до 255 Текущая политика планирования для текущего процесса: SCHED_FIFO Текущий приоритет текущего процесса: 100 Квант процессорного времени при циклическом планировании: 0.08 сек |
| Листинг 6.6. Возможные результаты выполнения программы, использующей функции управления планированием, в операционной системе реального времени oc2000. |
| Закрыть окно |
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Программа моделирует ситуацию инверсии приоритетов. */ /* Идея состоит в том, что имеется три потока */ /* управления с низким, средним */ /* и высоким приоритетами. */ /* Поток с низким приоритетом захватывает семафор, */ /* когда на него никто больше не претендует, */ /* но не может освободить его, */ /* потому что его вытесняет с процессора */ /* поток со средним приоритетом. */ /* В это время поток с высоким приоритетом хочет */ /* захватить тот же семафор, */ /* но он занят и неизвестно когда освободится... */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ #include static sem_t sem_mi; /* Семафор для потока со средним */ /* приоритетом */ static sem_t sem_hi; /* Семафор для потока с высоким */ /* приоритетом */ static double s = 0; /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Стартовая функция потока с высоким приоритетом */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ void *start_hi (void *dummy) { printf ("Поток с высоким приоритетом перед захватом " "семафора\n"); assert (sem_wait (&sem_hi) == 0); printf ("Поток с высоким приоритетом после захвата " "семафора\n"); assert (sem_post (&sem_hi) == 0); return (NULL); } /* * * * * * * * * * * * * * * * * * * * * * * * * */ /* Стартовая функция потока со средним приоритетом */ /* * * * * * * * * * * * * * * * * * * * * * * * * */ void *start_mi (void *dummy) { int i; double d = 1; printf ("Поток со средним приоритетом перед захватом " "семафора\n"); assert (sem_wait (&sem_mi) == 0); /* Займем процессор вычислениями */ for (i = 1; i < 100000000; i++) { s += d/i; d = -d; } printf ("Поток со средним приоритетом перед " "освобождением семафора\n"); assert (sem_post (&sem_mi) == 0); return (&s); } /* * * * * * * * * * * * * * * * * * * * * * * * * * */ /* Стартовая функция потока с низким приоритетом */ /* * * * * * * * * * * * * * * * * * * * * * * * * * */ int main (void) { pthread_attr_t attrob; /* Атрибутный объект */ /* создаваемых потоков */ struct sched_param shdprm; /* Значения параметров */ /* планирования */ int shdplc; /* Политика планирования */ pthread_t pt_mi; /* Идентификатор потока */ /* со средним приоритетом */ pthread_t pt_hi; /* Идентификатор потока */ /* с высоким приоритетом */ /* Создадим два семафора в захваченном состоянии */ assert (sem_init (&sem_mi, 0, 0) == 0); assert (sem_init (&sem_hi, 0, 0) == 0); /* Установим политику планирования и сделаем */ /* текущий поток управления низкоприоритетным */ shdprm.sched_priority = sched_get_priority_max (SCHED_FIFO) – 31; assert (pthread_setschedparam (pthread_self (), SCHED_FIFO, &shdprm) == 0); /* Инициализируем атрибутный объект */ /* для создаваемых потоков управления */ assert (pthread_attr_init (&attrob) == 0); /* Установим атрибуты планирования */ assert (pthread_attr_setinheritsched (&attrob, PTHREAD_EXPLICIT_SCHED) == 0); assert (pthread_attr_setschedpolicy (&attrob, SCHED_FIFO) == 0); shdprm.sched_priority += 15; assert (pthread_attr_setschedparam (&attrob, &shdprm) == 0); /* Создадим поток управления со средним приоритетом */ assert (pthread_create (&pt_mi, &attrob, start_mi, NULL) == 0); /* Подправим атрибутный объект и создадим */ /* поток управления с высоким приоритетом */ shdprm.sched_priority += 15; assert (pthread_attr_setschedparam (&attrob, &shdprm) == 0); assert (pthread_create (&pt_hi, &attrob, start_hi, NULL) == 0); /* Опросим параметры планирования потоков управления */ assert (pthread_getschedparam (pthread_self (), &shdplc, &shdprm) == 0); assert (shdplc == SCHED_FIFO); printf ("Низкий приоритет: %d\n", shdprm.sched_priority); assert (pthread_getschedparam (pt_mi, &shdplc, &shdprm) == 0); assert (shdplc == SCHED_FIFO); printf ("Средний приоритет: %d\n", shdprm.sched_priority); assert (pthread_getschedparam (pt_hi, &shdplc, &shdprm) == 0); assert (shdplc == SCHED_FIFO); printf ("Высокий приоритет: %d\n", shdprm.sched_priority); /* Создадим ситуацию инверсии приоритетов */ printf ("Поток с низким приоритетом\n" "перед освобождением семафора для потока " "со средним приоритетом\n"); assert (sem_post (&sem_mi) == 0); printf ("Поток с низким приоритетом\n" "перед освобождением семафора для потока " "с высоким приоритетом\n"); assert (sem_post (&sem_hi) == 0); (void) pthread_join (pt_mi, NULL); (void) pthread_join (pt_hi, NULL); assert (sem_destroy (&sem_mi) == 0); assert (sem_destroy (&sem_hi) == 0); return 0; } |
| Листинг 6.7. Пример программы, моделирующей ситуацию инверсии приоритетов. |
| Закрыть окно |
| Поток со средним приоритетом перед захватом семафора Поток с высоким приоритетом перед захватом семафора Низкий приоритет: 224 Средний приоритет: 239 Высокий приоритет: 254 Поток с низким приоритетом перед освобождением семафора для потока со средним приоритетом Поток со средним приоритетом перед освобождением семафора Поток с низким приоритетом перед освобождением семафора для потока с высоким приоритетом Поток с высоким приоритетом после захвата семафора |
| Листинг 6.8. Возможные результаты выполнения программы, моделирующей ситуацию инверсии приоритетов, под управлением операционной системы реального времени oc2000. |
| Закрыть окно |