Администрирование локальных сетейРефераты >> Программирование и компьютеры >> Администрирование локальных сетей
#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, ядро выбирает наиболее удобный виртуальный адрес начала сегмента