Потребность в двух семафорах связана с особенностью работы функции semop.

Если, например, процессы W уменьшают значение в семафоре, отвечающем за свободное место в буфере, до нуля, то процесс R может увеличивать это значение до бесконечности. Поэтому такой семафор не может указывать на отсутствие элементов в буфере.

 

--------------------------------------------------------------

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <linux/types.h>

#include <linux/ipc.h>

#include <linux/sem.h>

 

int main(int argc, char *argv[])

{

/* IPC */

pid_t pid;

key_t key;

int semid;

union semun arg;

struct sembuf lock_res = {0, -1, 0};

struct sembuf rel_res = {0, 1, 0};

struct sembuf push[2] = {1, -1, IPC_NOWAIT, 2, 1, IPC_NOWAIT};

struct sembuf pop[2] = {1, 1, IPC_NOWAIT, 2, -1, IPC_NOWAIT};

 

/* Остальное */

int i;

 

if(argc < 2){

printf("Usage: bufdemo [dimensione]\n");

exit(0);

}

 

/* Семафоры */

key = ftok("/etc/fstab", getpid());

 

/* Создать набор из трёх семафоров */

semid = semget(key, 3, 0666 | IPC_CREAT);

 

/* Установить в семафоре номер 0 (Контроллер ресурсов)

значение "1" */

arg.val = 1;

semctl(semid, 0, SETVAL, arg);

 

/* Установить в семафоре номер 1 (Контроллер свободного места)

значение длины буфера */

arg.val = atol(argv[1]);

semctl(semid, 1, SETVAL, arg);

 

/* Установить в семафоре номер 2 (Контроллер элементов в буфере)

значение "0" */

arg.val = 0;

semctl(semid, 2, SETVAL, arg);

 

/* Fork */

for (i = 0; i < 5; i++){

pid = fork();

if (!pid){

for (i = 0; i < 20; i++){

sleep(rand()%6);

/* Попытаться заблокировать ресурс (семафор номер 0) */

if (semop(semid, &lock_res, 1) == -1){

perror("semop:lock_res");

}

/* Уменьшить свободное место (семафор номер 1) /

Добавить элемент (семафор номер 2) */

if (semop(semid, &push, 2) != -1){

printf("---> Process:%d\n", getpid());

}

else{

printf("---> Process:%d BUFFER FULL\n", getpid());

}

/* Разблокировать ресурс */

semop(semid, &rel_res, 1);

}

exit(0);

}

}

 

for (i = 0;i < 100; i++){

sleep(rand()%3);

/* Попытаться заблокировать ресурс (семафор номер 0)*/

if (semop(semid, &lock_res, 1) == -1){

perror("semop:lock_res");

}

/* Увеличить свободное место (семафор номер 1) /

Взять элемент (семафор номер 2) */

if (semop(semid, &pop, 2) != -1){

printf("<--- Process:%d\n", getpid());

}

else printf("<--- Process:%d BUFFER EMPTY\n", getpid());

/* Разблокировать ресурс */

semop(semid, &rel_res, 1);

}

 

/* Удалить семафоры */

semctl(semid, 0, IPC_RMID);

 

return 0;

}

--------------------------------------------------------------

 

Часть кода

 

struct sembuf lock_res = {0, -1, 0};

struct sembuf rel_res = {0, 1, 0};

struct sembuf push[2] = {1, -1, IPC_NOWAIT, 2, 1, IPC_NOWAIT};

struct sembuf pop[2] = {1, 1, IPC_NOWAIT, 2, -1, IPC_NOWAIT};

 

выполняет следующие действия, которые можно производить над семафорами: первые две – содержат по одному действию каждая, вторые – по два. Первое действие, lock_res, блокирует ресурс: оно уменьшает значение первого (номер 0) семафора на единицу (если значение в семафоре не нуль), а если ресурс уже занят, то процесс ждёт. Действие rel_res аналогично lock_res, только значение в первом семафоре увеличивается на единицу, т.е. убирается блокировка ресурса.

Действия push и pop несколько отличаются от первых: это массивы из двух действий. Первое действие над семафором номер 1, второе – над семафором номер 2; одно увеличивает значение в семафоре, другое уменьшает, но теперь процесс не будет ждать освобождения ресурса: IPC_NOWAIT заставляет его продолжить работу, если ресурс заблокирован.

 

/* Установить в семафоре номер 0 (Контроллер ресурсов)

значение "1" */

arg.val = 1;

semctl(semid, 0, SETVAL, arg);

 

/* Установить в семафоре номер 1 (Контроллер свободного места)

значение длины буфера */

arg.val = atol(argv[1]);

semctl(semid, 1, SETVAL, arg);

 

/* Установить в семафоре номер 2 (Контроллер элементов в буфере)

значение "0" */

arg.val = 0;

semctl(semid, 2, SETVAL, arg);

 

Инициализация значений в семафорах: в первом – единицей, так как он контролирует доступ к ресурсу, во втором – длиной буфера (заданной в командной строке), в третьем – нулём (т.е. числом элементов в буфере).

 

/* Попытаться заблокировать ресурс (семафор номер 0) */

if (semop(semid, &lock_res, 1) == -1){

perror("semop:lock_res");

}

/* Уменьшить свободное место (семафор номер 1) /

Добавить элемент (семафор номер 2) */

if (semop(semid, &push, 2) != -1){

printf("---> Process:%d\n", getpid());

}

else{

printf("---> Process:%d BUFFER FULL\n", getpid());

}

/* Освободить ресурс */

semop(semid, &rel_res, 1);

 

Процесс W пытается заблокировать ресурс посредством действия lock_res; как только это ему удаётся, он добавляет элемент в буфер посредством действия push и выводит сообщение об этом на стандартный вывод. Если операция не может быть произведена, процесс выводит сообщение о заполнении буфера. В конце процесс освобождает ресурс.

 

/* Попытаться заблокировать ресурс (семафор номер 0) */

if (semop(semid, &lock_res, 1) == -1){

perror("semop:lock_res");

}

/* Увеличить свободное место (семафор номер 1) /

Взять элемент (семафор номер 2) */

if (semop(semid, &pop, 2) != -1){

printf("<--- Process:%d\n", getpid());

}

else printf("<--- Process:%d BUFFER EMPTY\n", getpid());

/* Отпустить ресурс */

semop(semid, &rel_res, 1);

 

Процесс R ведёт себя практически так же как и W процесс: блокирует ресурс, производит действие pop, освобождает ресурс.

Каждый процесс способен создать любое количество структур называемых очередями: каждая структура может содержать любое количество сообщений разных типов, которые имеют разную природу и содержат любую информацию; каждый процесс способен послать сообщение в очередь, зная ее идентификатор. Также процесс способен получить доступ к очереди и прочитать сообщения в хронологическом порядке (начиная с самого первого, последнего, самого недавнего и последнего, поступившего в очередь), но выборочно, т.е. сообщения только определенного типа, что обеспечивает контроль за возможностью прочитать эти сообщения.

Использование очереди легко понять представив ее в виде почтовой системы между процессами: каждый процесс обладает адресом для взаимодействия с другими процессами. Процесс читает сообщения, предназначенные ему, и дальнейшая его работа зависит от этих сообщений.

Таким образом, синхронизация двух процессов достигается использованием сообщений: ресурсы по-прежнему будут обладать семафорами, чтобы процессы знали их статус, а разделение времени работы происходит при помощи сообщений.

Синхронизация определяется еще одним аспектом – коммуникационным протоколом.

Протоколом называется набор правил, с помощью которого происходит взаимодействие объектов.

Использование очередей сообщений позволяет создавать сложные протоколы; причем все сетевые протоколы (TCP/IP, DNS, SMTP,...) построены на архитектуре обмена сообщениями. Все достаточно просто – нет никакой разницы на одном ли компьютере происходит взаимодействие процессов или между разными.

Рассмотрим простой протокол, основанный на обмене сообщениями: два процесса (А и В) выполняются параллельно и работают с разными данными: после окончания работы каждого им необходимо обменяться данными. Простой протокол их взаимодействия выглядит следующим образом:

 

ПРОЦЕСС B:

* работает со своими данными

* по окончании работы посылает сообщение процессу А

* получив ответ от процесса A – начинает передачу ему данных

 

ПРОЦЕСС A:

* работает со своими данными

* ожидает сообщение от процесса B

* отвечает на сообщение

* принимает данные и объединяет со своими

 

Выбор процесса ответственного за объединение данных, в приведенном примере, достаточно условен; в реальной жизни это зависит от природы взаимодействующих процессов ( клиент/сервер ).

Рассмотренный протокол легко применим к n процессам. Любой процесс, кроме А, обрабатывает свои данные и затем посылает сообщение процессу А. После ответа процесса А ему пересылаются данные, нет необходимости менять структуру процессов, кроме А.








Дата добавления: 2015-03-26; просмотров: 668;


Поиск по сайту:

При помощи поиска вы сможете найти нужную вам информацию.

Поделитесь с друзьями:

Если вам перенёс пользу информационный материал, или помог в учебе – поделитесь этим сайтом с друзьями и знакомыми.
helpiks.org - Хелпикс.Орг - 2014-2024 год. Материал сайта представляется для ознакомительного и учебного использования. | Поддержка
Генерация страницы за: 0.019 сек.