Администрирование локальных сетей
Рефераты >> Программирование и компьютеры >> Администрирование локальных сетей

#include <sys/types.h>

#include <sys/stat.h>

mknod( имяФайла, S_IFIFO | 0666, 0 );

где 0666 - коды доступа к файлу. При помощи FIFO-файла могут общаться даже неродственные процессы.

Разновидностью FIFO-файла является безымянный FIFO-файл, предназначенный для обмена информацией между процессом-отцом и процессом-сыном. Такой файл - канал связи как раз и называется термином "труба" или pipe. Он создается вызовом pipe:

int conn[2]; pipe(conn);

Если бы файл-труба имел имя PIPEFILE, то вызов pipe можно было бы описать как

mknod("PIPEFILE", S_IFIFO | 0600, 0);

conn[0] = open("PIPEFILE", O_RDONLY);

conn[1] = open("PIPEFILE", O_WRONLY);

unlink("PIPEFILE");

При вызове fork каждому из двух процессов достанется в наследство пара дескрипторов:

pipe(conn);

fork();

conn[0]----<---- ----<-----conn[1]

FIFO

conn[1]---->---- ---->-----conn[0]

процесс A процесс B

Пусть процесс A будет посылать информацию в процесс B. Тогда процесс A сделает:

close(conn[0]);

// т.к. не собирается ничего читать

write(conn[1], . );

а процесс B

close(conn[1]);

// т.к. не собирается ничего писать

read (conn[0], . );

Получаем в итоге:

conn[1]---->----FIFO---->-----conn[0]

процесс A процесс B

Обычно поступают еще более элегантно, перенаправляя стандартный вывод A в канал conn[1]

dup2 (conn[1], 1); close(conn[1]);

write(1, . ); /* или printf */

а стандартный ввод B - из канала conn[0]

dup2(conn[0], 0); close(conn[0]);

read(0, . ); /* или gets */

Это соответствует конструкции

$ A | B

записанной на языке СиШелл.

Файл, выделяемый под pipe, имеет ограниченный размер (и поэтому обычно целиком оседает в буферах в памяти машины). Как только он заполнен целиком - процесс, пишущий в трубу вызовом write, приостанавливается до появления свободного места в трубе. Это может привести к возникновению тупиковой ситуации, если писать программу неаккуратно. Пусть процесс A является сыном процесса B, и пусть процесс B издает вызов wait, не закрыв канал conn[0]. Процесс же A очень много пишет в трубу conn[1]. Мы получаем ситуацию, когда оба процесса спят:

A потому что труба переполнена, а процесс B ничего из нее не читает, так как ждет окончания A;

B потому что процесс-сын A не окончился, а он не может окончиться пока не допишет свое сообщение.

Решением служит запрет процессу B делать вызов wait до тех пор, пока он не прочитает ВСЮ информацию из трубы (не получит EOF). Только сделав после этого close(conn[0]); процесс B имеет право сделать wait.

Если процесс B закроет свою сторону трубы close(conn[0]) прежде, чем процесс A закончит запись в нее, то при вызове write в процессе A, система пришлет процессу A сигнал SIGPIPE - "запись в канал, из которого никто не читает".

Нелокальный переход.

Теперь поговорим про нелокальный переход. Стандартная функция setjmp позволяет установить в программе "контрольную точку"*, а функция longjmp осуществляет прыжок в эту точку, выполняя за один раз выход сразу из нескольких вызванных функций (если надо)*. Эти функции не являются системными вызовами, но поскольку они реализуются машинно-зависимым образом, а используются чаще всего как реакция на некоторый сигнал, речь о них идет в этом разделе. Вот как, например, выглядит рестарт программы по прерыванию с клавиатуры:

#include <signal.h>

#include <setjmp.h>

jmp_buf jmp; /* контрольная точка */

/* прыгнуть в контрольную точку */

void onintr(nsig){ longjmp(jmp, nsig); }

main(){

int n;

n = setjmp(jmp); /* установить контрольную точку */

if( n ) printf( "Рестарт после сигнала %d\n", n);

signal (SIGINT, onintr); /* реакция на сигнал */

printf("Начали\n");

.

}

setjmp возвращает 0 при запоминании контрольной точки. При прыжке в контрольную точку при помощи longjmp, мы оказываемся снова в функции setjmp, и эта функция возвращает нам значение второго аргумента longjmp, в этом примере - nsig.

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

Разделяемая память

shmget создает новый сегмент разделяемой памяти или находит существующий сегмент с тем же ключом shmat подключает сегмент с указанным дескриптором к виртуальной памяти обращающегося процесса shmdt отключает от виртуальной памяти ранее подключенный к ней сегмент с указанным виртуальным адресом начала shmctl служит для управления параметрами, связанными с существующим сегментом После подключения сегмента разделяемой памяти к виртуальной памяти процесса, он может обращаться к соответствующим элементам памяти с использованием обычных машинных команд чтения и записи

shmid = shmget(key, size, flag);

· size определяет желаемый размер сегмента в байтах

· если в таблице разделяемой памяти находится элемент, содержащий заданный ключ, и права доступа не противоречат текущим характеристикам процесса, то значением системного вызова является дескриптор существующего сегмента

· реальный размер сегмента можно узнать с помощью системного вызова shmctl

· иначе создается новый сегмент с размером не меньше установленного в системе минимального размера сегмента разделяемой памяти и не больше установленного максимального размера

· создание сегмента не означает немедленного выделения под него основной памяти

· откладывается до выполнения первого системного вызова подключения сегмента к виртуальной памяти некоторого процесса

· при выполнении последнего системного вызова отключения сегмента от виртуальной памяти соответствующая основная память освобождается

virtaddr = shmat(id, addr, flags);

· id - это ранее полученный дескриптор сегмента

· addr - желаемый процессом виртуальный адрес, который должен соответствовать началу сегмента в виртуальной памяти

· virtaddr - реальный виртуальный адрес начала сегмента не обязательно совпадает со значением прямого параметра addr

· если addr == 0, ядро выбирает наиболее удобный виртуальный адрес начала сегмента


Страница: