- •Сравнение характеристик разных ос.
- •Задание
- •Вопросы коллоквиума.
- •«Наследники» Создание дочерних процессов
- •Задание
- •Вопросы коллоквиума
- •«Трубы» Использование программных каналов
- •Задание
- •Вопросы коллоквиума
- •«На деревню дедушке» Использование очереди сообщений
- •Задание
- •Вопросы коллоквиума
- •«Вспомнить всё» Использование семафоров и разделяемой памяти
- •Задание
- •Вопросы коллоквиума
Задание
Построить генеалогическое дерево из 4-х поколений процессов. Количество процессов во 2-ом поколении – А (см. табл. 1), в 3-ем – Е, в 4-ом – D. Процессы должны существовать 1 секунду и выводить:
собственный идентификатор;
идентификатор предка;
статистику жизни.
Вопросы коллоквиума
Системный вызов fork возвращает.
Чем различаются порожденный и предковый процессы.
«Папы», «дети», «сироты» и «зомби».
Что делает системный вызов _exit.
Предназначение системного вызова wait.
Предназначение системного вызова exec.
В каком случае процесс переводиться в режим ядра.
«Трубы» Использование программных каналов
Процессы могут обмениваться между собой информацией через файлы. Существуют файлы с необычным поведением - так называемые FIFO-файлы (first in, first out), ведущие себя подобно очереди. У них указатели чтения и записи разделены. Работа с таким файлом напоминает проталкивание шаров через трубу - с одного конца мы вталкиваем данные, с другого конца - вынимаем их. Операция позиционирования указателя чтения - lseek(), неприменима к FIFO-файлам, данные удаляются из канала сразу же после их считывания. FIFO-файл создается системным вызовом:
#include <sys/types.h>
#include <sys/stat.h>
mknod( имяФайла, S_IFIFO | 0666, 0 );
где 0666 - коды доступа к файлу.
При помощи FIFO-файла могут общаться даже не родственные процессы.
Разновидностью FIFO-файла является безымянный FIFO-файл, предназначенный для обмена информацией между процессом-отцом и процессом-сыном. Такой файл - канал связи как раз и называется термином "труба" или pipe.
Системный вызов pipe создает коммуникационный канал между двумя взаимосвязанными процессами. В частности, эта функция создает файл канала, который служит в качестве временного буфера и используется для того, чтобы вызывающий процесс мог записывать и считывать данные другого процесса. Файлу канала имя не присваивается, поэтому он называется неименованным каналом. Канал освобождается сразу после того, как все процессы закроют файловые дескрипторы, ссылающиеся на этот канал.
#include <unistd.h>
int pipe (int fifo[2]);
Аргумент fifo является массивом, состоящим из двух целых чисел, присваиваемых ему интерфейсом pipe. В большинстве систем UNIX канал является однонаправленным, т.е. для чтения данных из канала процесс использует дескриптор файла fifo[0], а для записи данных в канал — другой дескриптор файла, fifo[1].
Родительский и порожденный процессы: родительский процесс вызывает pipe для создания канала, а затем с помощью fork порождает процесс потомок. Так как порожденный процесс имеет копию дескрипторов файлов родительского, два процесса могут общаться между собой по каналу через свои дескрипторы fifo[0] и fifo[1].
Порожденные процессы-братья: родительский вызывает pipe для создания канала, затем порождает два или больше процессов-братьев. Порожденные процессы могут сообщаться по каналу посредством своих дескрипторов fifo[0] и fifo[1].
Так как буфер, связанный с каналом, имеет конечный размер (PIPE_BUF), то при попытке процесса записать данные в уже заполненный канал ядро заблокирует его до тех пор, пока другой процесс не произведет считывание из канала достаточного количества данных, чтобы заблокированный процесс смог возобновить операцию записи. И наоборот, если канал пуст, а процесс пытается прочитать из него данные, он будет блокироваться до тех пор, пока другой процесс не запишет данные в канал. Эти блокирующие механизмы можно использовать для синхронизации выполнения двух (и более) процессов.
Коммуникационный канал обычно делают однонаправленным и устанавливают его только между двумя процессами, чтобы один процесс являлся отправителем, а другой — получателем. Если два процесса, например А и В, нуждаются в двунаправленном коммуникационном канале, они создадут два канала: один для записи данных процесса А в процесс В, другой для осуществления обратной операции. Количество процессов, которые могут параллельно присоединяться к любому концу канала, не ограничено.
Если дескриптора файла, связанного с записывающей стороной канала, не существует, то последняя считается "закрытой" и любой процесс, пытающийся считать данные из канала, получает оставшиеся в нем данные. Однако если все данные из канала уже выбраны, процесс, пытающийся продолжать чтение данных из канала, получает признак конца файла (системный вызов read возвращает нулевое значение). С другой стороны, если не существует дескриптора файла, связанного с читающей стороной канала, а процесс пытается записать данные в канал, он получает от ядра сигнал SIGPIPE (разорванный канал). Это происходит потому, что данные, записанные в канал, не могут быть выбраны процессом и, следовательно, операция записи считается недопустимой. Процесс, производящий запись, будет "наказан" этим сигналом (действие этого сигнала по умолчанию — преждевременное завершение процесса).
В приведенном ниже примере исполнимый файл ./creator создаёт две трубы, двоих сыновей и ждет окончания их работы. ./client1 в цикле читает из файла строки и посылает их в трубу. ./clent2 читает из трубы и записывает в файл строки. Оба процесса дописывают строки. Вторая труба не используется.
/*creator.c*/
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include <sys/errno.h>
extern int errno;
#define chan1 "fifo.1"
#define chan2 "fifo.2"
#define PERM 0666
main ()
{
int fdw1,fdw2,fdw3,i,st;
if((mknod(chan1, S_IFIFO | PERM, 0) < 0) && (errno != EEXIST))
printf ("Can't create fifo 1: %s, chan1");
if((mknod(chan2, S_IFIFO | PERM, 0) < 0) && (errno != EEXIST))
printf ("Can't create fifo 2: %s, chan2");
if(fork()==0)
execvp("./client1",NULL);
if(fork()==0)
execvp("./client2",NULL);
while(i=wait(&st)>0);
unlink(chan1);
unlink(chan2);
}
/*client1.c*/
#include <stdio.h>
#define chan1 "fifo.1"
#define MAXBUFF 1024
main ()
{
int fdr1, i;
FILE *fpA;
char buff[MAXBUFF];
fpA=fopen("pi_ser.txt","r") ;
if ((fdr1 = open(chan1,1)) < 0)
printf("client1 не нашел трубы\n");
for(i=0;i<=5;i++)
{
fgets(buff,MAXBUFF,fpA);
strcat(buff,"+client1 читает из файла pi_ser.txt\n");
write(fdr1,buff,MAXBUFF);
}
close(fdr1);
fclose(fpA);
exit(0);
}
/*client2.c*/
#include <stdio.h>
extern int errno;
#define chan1 "fifo.1"
#define MAXBUFF 1024
main ()
{
int i, fdr1;
FILE *fpA;
char buff[MAXBUFF];
fpA=fopen("text.txt","w+");
if ((fdr1 = open(chan1, 0)) < 0)
printf("client2 не нашел трубы\n");
for(i=0;i<=5;i++)
{
read(fdr1,buff,MAXBUFF);
strcat(buff,"+client2 читает из трубы\n");
fprintf(fpA,buff);
}
fclose(fpA);
close(fdr1);
exit(0);
}
