Страница:
if(T_TST(tbl, sel, I_DIR)){ /* это каталог */
/* попытаться перейти в этот каталог */
cd(SELECTION, wd, CWD);
} else if(T_TST(tbl, sel, I_EXE)){ /* выполняемый файл */
(void) Edit(tbl->win, SELECTION, RUNCMD);
} else {
editAccessModes(wd);
/* На самом деле надо производить подбор команды по
* типу файла (набор соответствий должен программироваться
* вами в специальном файле, считываемом при запуске коммандера).
* runCommand( classify(SELECTION));
* где классификация в простейшем случае - по имени и суффиксу,
* а в более развитом - еще и по кодам доступа (включая тип файла)
* и по первой строке файла (или "магическому числу").
*/
}
} /* end for */
t_leave( tbl );
out:
if( !retcode ) current_menu = SEL_PULL; /* выход по ESC */
return retcode;
}
/*-----------------------------------------------------------------*
* Горизонтальное командное меню (вызывается по ESC). *
*-----------------------------------------------------------------*/
PullInfo pm_items [] = { /* подсказка */
{{ " \\Left ", 0 }, NULL, "Left pane" }, /* 0 */
{{ " \\Commands ", 0 }, &mwrk, "Do some commands"}, /* 1 */
{{ " \\Tools ", PM_NOSEL }, NULL, "" }, /* 2 */
{{ " \\Sorttype ", 0 }, &msort, "Change sort type"}, /* 3 */
{{ " \\Right ", 0 }, NULL, "Right pane" }, /* 4 */
{{ NULL, 0 }, NULL, NULL }
};
void p_help(PullMenu *p, int n, int among){ Message( PM_NOTE(p, n)); }
/* Выбор в меню-строке */
void SelectPullMenu(){
int c, sel; Menu *m;
for(;current_menu == SEL_PULL;){
c = PullUsualSelect(&pull);
sel = pull.current;
if( PM_REFUSED(&pull)){ current_menu = previous_menu; return;}
switch(sel){
case 0: current_menu = SEL_PANE1; return;
case 1: SelectWorkingMenu(c); return;
case 2: return; /* не бывает */
case 3: SelectSortType(c); return;
case 4: current_menu = SEL_PANE2; return;
}
}
}
/*-----------------------------------------------------------------*
* Инициализация и завершение. *
*-----------------------------------------------------------------*/
void die(int sig){
echo(); nocbreak(); mvcur(-1,-1,LINES-1,0);
refresh(); endwin (); putchar('\n');
if(sig) printf("Signal %d\n", sig);
if(sig == SIGSEGV) abort(); else exit(sig);
}
void main (void) {
setlocale(LC_ALL, ""); /* получить информацию о языке диагностик */
initscr (); /* включить curses */
signal(SIGINT, die); /* по сигналу вызывать die(); */
signal(SIGBUS, die); /* по нарушению защиты памяти */
signal(SIGSEGV,die);
refresh(); /* обновить экран: это очистит его */
noecho(); cbreak(); /* выключить эхо, включить прозрачный ввод */
/* Проинициализировать истории */
HistInit(&hcwd, 20); hcwd. mnu.title = "История пути";
HistInit(&hedit, 20); hedit.mnu.title = "История команд";
HistInit(&hpat, 8); hpat. mnu.title = "Шаблоны имен";
/* Разметить меню сортировки */
msort.items = sort_info;
msort.title = "Вид сортировки каталога";
msort.top = 1; msort.left = 2;
msort.showMe = sort_show;
msort.bg_attrib = A_NORMAL; msort.sel_attrib = A_STANDOUT;
/* MnuInit (&msort); инициализируется в pull-menu */
/* Разметить рабочее меню */
mwrk.items = mwrk_info;
mwrk.title = "Главное меню";
mwrk.top = 1; mwrk.left = COLS/3;
mwrk.handler = NULL; mwrk.hitkeys = NULL;
mwrk.bg_attrib = A_STANDOUT; mwrk.sel_attrib = A_REVERSE;
mwrk.scrollBar = m_help;
#ifdef __GNUC__
mwrk_init();
#endif
/* MnuInit (&mwrk); инициализируется в pull-menu */
/* Разметить левую и правую панели */
tpane1.t.width = CENTER - 1;
tpane2.t.width = COLS - tpane1.t.width - 2 - (2 + BARWIDTH);
tpane1.t.height = tpane2.t.height = (LINES - 8);
tpane1.t.win = tpane2.t.win = panewin = stdscr;
tpane1.t.left = 1;
tpane2.t.left = CENTER+1;
tpane1.t.top = tpane2.t.top = 3;
tpane1.t.bg_attrib = tpane2.t.bg_attrib = A_NORMAL;
tpane1.t.sel_attrib = tpane2.t.sel_attrib = A_STANDOUT;
tpane1.t.scrollBar = tpane2.t.scrollBar = t_scrollbar;
tpane1.t.hitkeys = tpane2.t.hitkeys = t_hit;
tpane1.t.handler = tpane2.t.handler = t_handler;
tpane1.t.showMe = tpane2.t.showMe = t_show;
tpane1.t.hideMe = tpane2.t.hideMe = NULL;
/* Разметить имена для файловых объектов */
tpane1.d.name = strdup("Текущий каталог");
tpane2.d.name = strdup("Корневой каталог");
/* Изобразить рамки (но пока не проявлять их)
* Это надо сделать до первого cd(), т.к. иначе при неудаче будет выдано
* сообщение, которое проявит НЕЗАВЕРШЕННУЮ картинку */
t_border_common(); t_restore_corners();
/* Доразметить левую панель */
mychdir("."); /* узнать полное имя текущего каталога в CWD[] */
/* прочитать содержимое каталога CWD в tpane1.d */
cd( CWD , &tpane1, CWD);
tpane1.t.fmt = "directory";
InitTblFromDir(&tpane1, NO, NULL);
/* Доразметить правую панель */
tpane2.t.fmt = NULL;
/* прочитать содержимое каталога "/" в tpane2.d */
cd( "/", &tpane2, CWD); /* теперь стоим в корне */
/* Вернуться в рабочий каталог */
cd( tpane1.d.name, &tpane1, CWD);
/* Нарисовать обе панели */
TblDraw(A_tbl); TblDraw(B_tbl);
/* Разметить pulldown меню */
pull.bg_attrib = A_REVERSE; pull.sel_attrib = A_NORMAL;
pull.items = pm_items; pull.scrollBar = p_help;
PullInit(&pull);
/* Основной цикл */
for(done=NO, current_menu=SEL_PANE1, A_pane= &tpane1, B_pane= &tpane2;
done == NO; ){
Message("");
if(SEL_PANE) previous_menu = current_menu;
switch(current_menu){
case SEL_WRK : SelectWorkingMenu(NOSELECTED); break;
case SEL_PULL: SelectPullMenu(); break;
case SEL_PANE1: if( SelectPane(&tpane1) < 0)
M_SET(&mwrk, 0, I_NOSEL); break;
case SEL_PANE2: if( SelectPane(&tpane2) < 0)
M_SET(&mwrk, 0, I_NOSEL); break;
}
}
die(0); /* Завершить работу */
}
/* Пример 24 */
/* Пример коммуникации процессов при помощи программных каналов
* (трубы, pipes).
* Данная программа превращается в две программы,
* соединенные трубами в таком порядке:
*
* stdout stdin
* /------------ PIP1 -----------> cmd2
* cmd1 <----------PIP2---------------/
* stdin stdout
*/
/* файл LOOP_strt.c */
#include <stdio.h>
#define eq(s1,s2) ( strcmp(s1,s2) == 0 ) /* истина, если строки равны */
#define SEP "---" /* разделитель команд при наборе */
main( c, v ) char **v;
{
char **p, **q;
int pid;
int PIP1[2]; /* труба cmd1-->cmd2 */
int PIP2[2]; /* труба cmd2-->cmd1 */
if( c==1 ){
printf( "Call: strt cmd1... %s cmd2...\n", SEP );
exit(1);
}
/* разбор аргументов */
v++;
/* в p - аргументы первой команды */
p = v;
while( *v && !eq( *v, SEP ))
v++;
*v = NULL;
v++;
/* в q - аргументы второй команды */
q = v;
pipe( PIP1 ); /* создаем две трубы */
pipe( PIP2 ); /* PIP[0] - открыт на чтение, PIP[1] - на запись */
if( pid = fork()){ /* развилка: порождаем процесс */
/* ПОРОЖДЕННЫЙ ПРОЦЕСС */
fprintf( stderr, "сын=%s pid=%d\n", p[0], getpid());
/* перенаправляем stdout нового процесса в PIP1 */
dup2( PIP1[1], 1 );
close( PIP1[1] );
/* канал чтения мы не будем использовать */
close( PIP1[0] );
/* перенаправляем stdin из PIP2 */
dup2( PIP2[0], 0 );
close( PIP2[0] );
/* канал записи мы не будем использовать */
close( PIP2[1] );
/* начинаем выполнять программу, содержащуюся в
* файле p[0] с аргументами p (т.е. cmd1)
*/
execvp( p[0], p );
/* возврата из сисвызова exec не бывает */
}else{
/* ПРОЦЕСС-РОДИТЕЛЬ */
fprintf( stderr, "отец=%s pid=%d\n", q[0], getpid());
/* перенаправляем stdout в PIP2 */
dup2( PIP2[1], 1 );
close( PIP2[1] ); close( PIP2[0] );
/* перенаправляем stdin из PIP1 */
dup2( PIP1[0], 0 );
close( PIP1[0] ); close( PIP1[1] );
/* запускаем cmd2 */
execvp( q[0], q );
}
}
/* Ниже приводятся тексты двух программ, которые можно запустить
* как тест. Сервер компилируется в программу cmd2,
* клиент - в программу cmd1. Если запускающая программа
* скомпилирована в strt, то наберите команду
* strt cmd1 --- cmd2
* либо strt cmd2 --- cmd1
*/
/* файл LOOP_p.c ---------------------------------------------
* Процесс-клиент (cmd1)
*/
#include <stdio.h>
int trace = 1; /* вести трассировку своих действий */
main(c , v) char **v;
{
FILE *fp; int pid;
char buf[128];
fprintf( stderr, "P: process pid=%d\n", getpid());
fp = fopen( "LOOP_p.c", "r" );
/* открываем файл с текстом этой команды */
/* читаем его построчно */
while( fgets( buf, sizeof buf, fp ) != NULL ){
if( trace ) fprintf( stderr, "P посылает: %s", buf );
/* посылаем его в стандартный вывод: трубу PIP1 */
printf( "%s", buf );
fflush( stdout );
/* ожидать ответа из трубы PIP2 */
fgets( buf, sizeof buf, stdin );
if( trace ) fprintf( stderr, "P получил: %s", buf );
}
fclose( stdout );
/* отключиться от трубы PIP1. Если этого не сделать, сервер
* не прочитает из нее EOF */
while((pid = wait(NULL)) > 0 )
fprintf( stderr, "P: %d умер\n", pid );
}
/* файл LOOP_q.c ------------------------------------------------
* процесс-сервер (cmd2)
*/
#include <stdio.h>
int trace = 1;
main(c , v) char **v;
{
char buf[128]; int pid;
fprintf( stderr, "Q: process pid=%d\n", getpid());
/* читать поступающие из трубы PIP1 строки */
while( fgets( buf, sizeof(buf), stdin ) != NULL ){
/* напечатать полученное сообщение */
if( trace ) fprintf( stderr, "Q прочел: %s", buf );
if( trace ) fprintf( stderr, "Q отвечает: OK=%s", buf );
/* ответить в трубу PIP2 */
printf( "OK=%s", buf ); fflush( stdout );
}
fclose( stdout ); /* отключиться от трубы PIP2 */
while((pid = wait(NULL)) > 0 )
fprintf( stderr, "Q: %d умер\n", pid );
}
/* Пример 25 */
/* Пример использования именованных "труб" (pipes) FIFO-файлов
* для коммуникации независимых процессов
* (FIFO - first in, first out : первым пришел - первым ушел).
* По мотивам книги М.Дансмура и Г.Дейвиса.
*/
/* файл P_packet.h --------------------------------------------*/
#include <sys/types.h>
#include <sys/stat.h> /* S_IFIFO */
/* структура пакета-запроса */
struct packet {
int pk_pid; /* идентификатор процесса-отправителя */
int pk_blk; /* номер блока, который надо прочитать */
int pk_code; /* код запроса */
};
/* request codes (коды запросов) */
#define RQ_READ 0 /* запрос на чтение */
#define CONNECT 1 /* запрос на соединение */
#define SENDPID 2 /* ответ на запрос соединения */
#define DISCONNECT 3 /* разрыв связи */
#define BYE 4 /* завершить сервер */
/* имена FIFO-каналов связи */
#define DNAME "datapipe"
#define CNAME "ctrlpipe"
/* размер блока информации */
#define PBUFSIZE 512
/* P_client.c --------------------------------------------------------- */
/*
* Процесс-клиент, посылающий запросы к серверу.
*/
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include "P_packet.h"
int datapipe, ctrlpipe;
int got_sig;
int mypid; /* идентификатор процесса-клиента */
int spid; /* идентификатор процесса-сервера */
/* waiting for signal */
#define WAITSIG while( !got_sig )
void handler(nsig){
signal( SIGUSR1, handler );
got_sig ++;
}
void init(){
extern void die();
/* Ожидать создания каналов связи */
while( (datapipe = open( DNAME, O_RDONLY | O_NDELAY )) < 0 );
while( (ctrlpipe = open( CNAME, O_WRONLY | O_NDELAY )) < 0 );
mypid = getpid(); /* my process identifier */
printf( "Client pid=%d started\n", mypid );
signal( SIGINT, die);
signal( SIGQUIT, die);
signal( SIGTERM, die);
handler(0);
}
int canRun = 1;
void die(nsig){
canRun = 0;
}
/* подключиться к серверу, запросив его pid */
connect(){
struct packet pk;
pk.pk_pid = mypid;
pk.pk_code = CONNECT;
pk.pk_blk = (-1);
got_sig = 0;
write( ctrlpipe, &pk, sizeof pk ); /* послать запрос */
/* ожидать сигнала-"толчка" */
WAITSIG;
/* прочитать ответ из канала данных */
read( datapipe, &pk, sizeof pk );
/* послать сигнал-подтверждение */
kill( pk.pk_pid, SIGUSR1 );
return pk.pk_pid;
}
void disconnect(){
struct packet pk;
pk.pk_pid = mypid;
pk.pk_code = DISCONNECT;
pk.pk_blk = (-1);
got_sig = 0;
write( ctrlpipe, &pk, sizeof pk ); /* send request */
/* wait for reply */
WAITSIG;
/* receive reply */
read( datapipe, &pk, sizeof pk );
/* confirm */
kill( pk.pk_pid, SIGUSR1 );
printf( "Disconnected.\n" );
}
request( ptr, blk, spid )
char *ptr;
int blk;
int spid;
{
struct packet pk;
pk.pk_pid = mypid;
pk.pk_blk = blk;
pk.pk_code = RQ_READ;
got_sig = 0;
write( ctrlpipe, &pk, sizeof pk );
WAITSIG;
read( datapipe, ptr, PBUFSIZE );
kill( spid, SIGUSR1 );
}
bye(){
struct packet pk;
pk.pk_pid = mypid;
pk.pk_code = BYE;
pk.pk_blk = (-1);
got_sig = 0;
write( ctrlpipe, &pk, sizeof pk ); /* send request */
exit(0);
}
/* client [номер_блока] */
main(argc, argv) char *argv[];
{
int blk;
char buffer[ PBUFSIZE ];
setbuf( stdout, NULL ); /* make unbuffered */
blk = (argv[1] ? atoi( argv[1] ) : 0);
init();
spid = connect();
printf( "Client pid=%d connected to server pid=%d\n",
mypid, spid );
/* запрос блока номер -33 соответствует запросу "завершить
* работу сервера"
*/
if( blk == -33 )
bye();
/* в цикле посылать запросы на чтение блока blk */
while( canRun ){
request( buffer, blk, spid );
printf( "\nBEG-------------------------------------\n" );
fwrite( buffer, PBUFSIZE, 1, stdout );
printf( "\nEND-------------------------------------\n" );
}
disconnect(); /* отключиться от сервера */
exit(0);
}
/* P_server.c ---------------------------------------------------------*/
/*
* Процесс-сервер, принимающий запросы и выполняющий их.
*/
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include "P_packet.h"
int datapipe, ctrlpipe, datafile, got_sig;
char *dataname = "/etc/passwd";
/* waiting for signal */
#define WAITSIG while( !got_sig )
void handler(nsig){
signal( SIGUSR1, handler ); /* reset trap */
got_sig++;
}
/* завершение работы сервера: уничтожить каналы связи */
void die(nsig){
unlink( CNAME ); unlink( DNAME ); exit(0);
/* Если эти файлы были открыты клиентами,
* то клиенты не умрут, хотя имена файлов и будут удалены!
*/
}
main(){
struct packet pk;
struct packet sendpk;
/* сделать стандартный вывод небуферизованным каналом */
setbuf( stdout, NULL ); /* make unbuffered */
/* создать каналы связи */
mknod( DNAME, S_IFIFO | 0666, 0 ); /* create FIFO */
mknod( CNAME, S_IFIFO | 0666, 0 ); /* create FIFO */
/* по этим сигналам будет вызываться функция die() */
signal( SIGINT, die );
signal( SIGQUIT, die );
signal( SIGTERM, die );
/* Открыть управляющий канал связи. O_NDELAY означает,
* что файл открывается для "чтения без ожидания",
* т.е. если канал пуст (нет заявок), то системный вызов
* read() не будет "спать", дожидаясь появления информации,
* а просто вернет 0 (прочитано 0 байт).
* Этот флаг применим также к чтению с терминала.
*/
ctrlpipe = open( CNAME, O_RDONLY | O_NDELAY );
if( ctrlpipe < 0 ){
printf( "Can't open %s\n", CNAME );
die(0);
}
datafile = open( dataname, O_RDONLY );
if( datafile < 0 ){
printf( "Can't open %s\n", dataname );
die(0);
}
/* заранее формируем пакет для ответов */
sendpk.pk_code = SENDPID;
sendpk.pk_pid = getpid(); /* server's pid */
sendpk.pk_blk = (-1);
printf( "Server pid=%d\n", getpid());
handler(0);
for(;;){
int n;
static long i = 0L;
/* active spin loop */
printf( "%20ld\r", i++ );
/* опрашивать канал насчет поступления запросов */
while((n = read( ctrlpipe, &pk, sizeof(pk))) > 0 ){
putchar( '\n' );
if( n != sizeof pk ){
printf( "Wrong packet size\n" );
continue;
}
/* обработать прочитанный запрос */
process( &pk, &sendpk );
}
}
die(0);
}
process( pkp, spkp )
struct packet *pkp, *spkp;
{
char pbuf[ PBUFSIZE ];
/* Запись в FIFO-файл будет произведена только если
* он уже открыт для чтения
*/
datapipe = open( DNAME, O_WRONLY | O_NDELAY );
printf( "REQUEST TYPE_%d from pid=%d blk=%d\n",
pkp->pk_code, pkp->pk_pid, pkp->pk_blk );
switch( pkp -> pk_code ){
case CONNECT: /* ответить своим идентификатором процесса */
write( datapipe, spkp, sizeof( struct packet ));
break;
case RQ_READ: /* ответить блоком информации из файла */
/* read block # pk_blk */
lseek( datafile, pkp -> pk_blk * (long)PBUFSIZE, 0 );
read( datafile, pbuf, PBUFSIZE );
write( datapipe, pbuf, PBUFSIZE );
break;
case DISCONNECT: /* подтвердить отключение */
printf( "Client pid=%d finished\n", pkp -> pk_pid );
write ( datapipe, spkp, sizeof( struct packet ));
break;
case BYE: /* завершиться */
printf( "Server terminated.\n" );
kill( pkp-> pk_pid, SIGKILL );
die(0);
default:
printf( "Unknown packet type %d\n", pkp -> pk_code );
break;
}
close( datapipe );
/* "подтолкнуть" отправителя сигналом */
got_sig = 0;
kill( pkp -> pk_pid , SIGUSR1 );
printf( "Waiting for reply... " );
/* ждать сигнала-подтверждения от клиента */
WAITSIG;
printf( "server continued\n" );
}
/* Пример 26 */
/* Общение процессов при помощи общей памяти и семафоров.
* Вызов: shms &
* shmc a & shmc b & shmc c &
*/
/* --------------------------- файл shm.h ----------------------- */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <signal.h>
#include <errno.h>
extern errno; /* Системный код ошибки */
struct connect { /* Структура почтового ящика */
int pid; int msgnum; int max;
char message[128]; /* текст сообщения */
};
#define NSEMS 3 /* число семафоров */
/* Имена семафоров */
#define EMPTY 0 /* 1 - ящик пуст; 0 - содержит письмо */
#define NOTEMPTY 1 /* негатив для EMPTY */
#define ACCESS 2 /* 1 - ящик доступен (закрыт);
* 0 - ящик уже открыт кем-то еще */
/* Значения семафоров */
#define YES 1
#define NO 0
/* Операции */
#define OPEN 1
#define CLOSE (-1)
#define TEST_NO 0
#ifdef COMMENT
Алгоритм одновременного изменения семафоров: semop
Дано:
аргумент: число семафоров : nsems
аргумент: величины изменения : sem_op[i]
в ядре: текущие значения семафоров группы sem_id: sem[i]
Алгоритм:
again: Сохранить значения всех семафоров (для отмены изменений);
for(i=0; i<nsems; i++)
/* OPEN */ if( sem_op[i] > 0 ){
sem[i] += sem_op[i];
разбудитьЖдущихСобытие( "sem[i]++" );
/* CLOSE */ }else if( sem_op[i] < 0 ){
if((newsm = sem[i] + sem_op[i]) >= 0 ){
sem[i] = newsm;
if( sem[i] == 0 )
разбудитьЖдущихСобытие( "sem[i]==0" );
}else{
восстановитьВсеСемафоры;
ждатьСобытие( "sem[i]++" );
goto again;
}
/* TEST0 */ }else{ /* sem_op[i] == 0 */
if( sem[i] != 0 ){
восстановитьВсеСемафоры;
ждатьСобытие( "sem[i]==0" );
goto again;
}
}
Алгоритм синхронизации в нашей схеме КЛИЕНТ-СЕРВЕР:
|----------------------------------------------------------------|
|семафоры: EMPTY ACCESS |
|----------------------------------------------------------------|
|начальное значение: YES YES |
|----------------------------------------------------------------|
СЕРВЕР
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: NO YES |
|сделать: NO(test0) NO(close) |
|----------------------------------------------------------------|
| прочесть почту; |
|----------------------------------------------------------------|
|из: NO NO |
|сделать: YES(open) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
КЛИЕНТ
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: YES YES |
|сделать: YES(test!=0) NO(close) |
|----------------------------------------------------------------|
| записать почту; |
|----------------------------------------------------------------|
|из: YES NO |
|сделать: NO(close) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
К сожалению, операции test!=0 не существует - приходится вводить
дополнительный семафор NOTEMPTY, негативный для EMPTY:
|----------------------------------------------------------------|
|семафоры: EMPTY NOTEMPTY ACCESS |
|----------------------------------------------------------------|
|начальное значение: YES NO YES |
|----------------------------------------------------------------|
СЕРВЕР
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: NO - YES |
|сделать: NO(test0) - NO(close) |
|----------------------------------------------------------------|
| прочесть почту; |
|----------------------------------------------------------------|
|из: NO YES NO |
|сделать: YES(open) NO(close) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
КЛИЕНТ
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: - NO YES |
|сделать: - NO(test0) NO(close) |
|----------------------------------------------------------------|
| записать почту; |
|----------------------------------------------------------------|
|из: YES NO NO |
|сделать: NO(close) YES(open) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
#endif /*COMMENT*/
/* Общая часть сервера и клиента ------------------------------- */
key_t key = 1917; /* Уникальный ключ для доступа */
int shm_id; /* Дескриптор для доступа к общей памяти */
int sem_id; /* Дескриптор для доступа к семафорам */
char name[40]; /* имя программы */
char far *addr;
struct connect far *caddr;
struct sembuf ops[NSEMS];
/* EMPTY NOTEMPTY ACCESS */
short values[NSEMS] = { YES, NO, YES };
void semtell(msg, name) char *msg, *name; { int i;
semctl(sem_id, NSEMS, GETALL, values);
printf( "%s %-10s: значения семафоров:", name, msg);
for(i=0; i < NSEMS; i++) printf( " %d", values[i]);
putchar('\n');
}
void inisem(){
register i;
for(i=0; i < NSEMS; i++ ) ops[i].sem_flg = 0;
}
/* --------------------------- файл shms.c ----------------------- */
/* Shared memory server */
#include "shm.h"
int npack; /* номер сообщения */
void cleanup(sig){
/* Уничтожить сегмент общей памяти (это нужно делать явно) */
shmctl( shm_id, IPC_RMID, NULL );
/* Уничтожить семафоры */
semctl( sem_id, NSEMS, IPC_RMID, NULL );
if( npack ) printf( "\t** Всего было %d сообщений **\n", npack+1);
exit(0);
}
void main(){
register i; int pid = getpid();
FILE *fout;
sprintf( name, "Server-%03d", pid );
for( i = 1; i <= SIGTERM; i++ )
signal( i, cleanup );
/* Создать разделяемый сегмент */
if((shm_id = shmget( key, sizeof(struct connect),
0644 | IPC_CREAT )) < 0 ){
perror( "shmget" ) ; exit(1);
}
/* Подключить общий сегмент к произвольному адресу */
if((addr = (char far *) shmat( shm_id, NULL, 0 )) == NULL ){
perror( "shmat" ); cleanup();
}
caddr = (struct connect far *) addr;
/* Создать группу из NSEMS семафоров */
if((sem_id = semget( key, NSEMS, 0644 |IPC_CREAT |IPC_EXCL)) < 0){
if(errno == EEXIST){ printf( "Сервер уже запущен\n");exit(2); }
else{ perror( "semget" ); cleanup(); }
}
/* Загрузить начальные значения семафоров */
semctl( sem_id, NSEMS, SETALL, values );
setbuf(stdout, NULL);
inisem(); printf( "Server is up now. Читай файл MESSAGES.\n");
fout = fopen( "MESSAGES", "w");
for(;;npack++){
printf( "%s: ждет почты\n", name );
semtell("Вход", name);
ops[0].sem_num = EMPTY; ops[0].sem_op = TEST_NO;
ops[1].sem_num = ACCESS; ops[1].sem_op = CLOSE;
semop( sem_id, ops, 2 /* сразу два семафора */);
printf( "%s: GOT-%02d/%02d от %d \"%s\"\n", name,
caddr->msgnum, caddr->max, caddr->pid, caddr->message);
fprintf( fout, "#%03d %02d/%02d от %d \"%s\"\n", npack,
caddr->msgnum, caddr->max, caddr->pid, caddr->message);
if( ! strcmp(caddr->message, "-exit" )){
printf( "%s: завершает работу.\n", name );
cleanup();
}
semtell("Выход", name);
ops[0].sem_num = EMPTY ; ops[0].sem_op = OPEN;
ops[1].sem_num = NOTEMPTY; ops[1].sem_op = CLOSE;
ops[2].sem_num = ACCESS ; ops[2].sem_op = OPEN;
semop( sem_id, ops, 3 /* сразу три семафора */);
}
/*NOTREACHED*/
}
/* --------------------------- файл shmc.c ----------------------- */
/* Shared memory client */
#include "shm.h"
void ignsigs(sig){
register i;
for( i = 1; i <= SIGTERM; i++ )
signal( i, ignsigs );
printf( "Клиент игнорирует сигналы,\n\
чтобы не оставлять закрытых семафоров в случае своей смерти.\n" );
}
void main(argc, argv) char **argv; {
int pid = getpid();
int i, ntimes = 60;
if( argc < 2 ){
fprintf( stderr, "Вызов: %s сообщение [числоПовторов]\n", argv[0] );
fprintf( stderr, "сообщение \"-exit\" завершает сервер\n");
fprintf( stderr, "сообщение \"-info\" выдает значения семафоров\n");
exit(1);
}
if( argc > 2 ) ntimes = atoi(argv[2]);
sprintf( name, "Client-%03d", pid);
ignsigs(); srand( pid );
/* Получить доступ к разделяемому сегменту */
if((shm_id = shmget( key, sizeof(struct connect), 0644)) < 0 ){
perror( "shmget" ); exit(2);
}
/* Подключить общий сегмент к произвольному адресу */
if((addr = (char far *) shmat( shm_id, NULL, 0 )) == NULL ){
perror( "shmat" ); exit(3);
}
caddr = (struct connect far *) addr;
/* Получить доступ к семафорам */
if((sem_id = semget( key, NSEMS, 0644)) < 0 ){
perror( "semget" ); exit(4);
}
setbuf(stdout, NULL);
inisem();
if( !strcmp(argv[1], "-info")){
semtell("Информация", name); exit(0);
}
for( i=0; i < ntimes; i++ ){
printf( "%s: ждет пустого ящика\n", name);
semtell("Вход", name);
ops[0].sem_num = NOTEMPTY; ops[0].sem_op = TEST_NO;
ops[1].sem_num = ACCESS ; ops[1].sem_op = CLOSE;
if( semop( sem_id, ops, 2 /* сразу два семафора */) < 0)
goto err;
caddr->pid = pid; caddr->msgnum = i; caddr->max = ntimes;
strncpy( caddr->message, argv[1],
sizeof(caddr->message) - 1);
printf( "%s: PUT-%02d \"%s\"\n", name, i, argv[1]);
semtell("Выход", name);
ops[0].sem_num = EMPTY ; ops[0].sem_op = CLOSE;
ops[1].sem_num = NOTEMPTY; ops[1].sem_op = OPEN;
ops[2].sem_num = ACCESS ; ops[2].sem_op = OPEN;
if( semop( sem_id, ops, 3 /* сразу три семафора */) < 0)
goto err;
if( rand()%2 ) sleep(2); /* пауза */
}
shmdt( addr ); /* Отключиться от общего сегмента */
exit(0);
err:
perror("semop");
exit(5);
}
/* Пример 27 */
/* Коммуникация процессов при помощи псевдо-терминала.
* Данная программа позволяет сохранять полный протокол работы
* экранной программы в файл.
* Не экранные программы данная версия НЕ трассирует,
* поскольку сама работает в "прозрачном" режиме.
*
* Вариацией данной программы может служить использование
* системного вызова select() вместо запуска нескольких процессов.
*
* Программа также иллюстрирует "дерево" из 5 процессов.
* Данная версия написана для UNIX System V.
* TRACE__
* \ \ master slave
* |экран<======\(Reader)=======!~!<====(целевая )
* / <==\ | ! !====>(программа)
* \ | !P! |
* | | !T! |
* . . . . | | !Y! (Slave)-->Управляет
* клавиатура=|===|=>(Writer)=>!_! | \ семафором
* | | | | \
* | #####starter################## \
* |...................................|
* ftty
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <termio.h>
#include <sys/stat.h>
#include <fcntl.h>
extern int exit ();
extern char *ttyname ();
extern FILE * fopen ();
extern errno;
#define SEMAPHORE "/tmp/+++" /* семафорный файл */
#define TRACE "./TRACE" /* файл с протоколом */
/* псевдотерминал связи */
/* master - это часть, которая ведет себя как ФАЙЛ и умеет
* реагировать на некоторые специальные ioctl()-и */
#define PTY "/dev/ptyp0" /* master */
/* slave - это часть, которая ведет себя как драйвер терминалов */
#define TTYP "/dev/ttyp0" /* slave */
int ptyfd;
FILE * ftrace = NULL;
/* при прерывании завершить работу процесса "писателя" */
onintr () {
closeVisual ();
fprintf (stderr, "\rwriter finished\r\n");
exit (0);
}
/* завершение работы процесса-"читателя" */
bye () {
if (ftrace)
fclose (ftrace);
fprintf (stderr, "\rreader finished\r\n");
exit (0);
}
int visual = 0;
struct termio old,
new;
/* настроить режимы работы терминала на "прозрачный" режим */
initVisual () {
ioctl (0, TCGETA, &old);
new = old;
new.c_iflag &= ~ICRNL;
new.c_lflag &= ~(ECHO | ICANON);
new.c_oflag &= ~(TAB3 | ONLCR);
new.c_cc[VMIN] = 1;
new.c_cc[VTIME] = 0;
/* new.c_cc[VINTR] = ctrl('C'); */
new.c_cc[VQUIT] = 0;
new.c_cc[VERASE] = 0;
new.c_cc[VKILL] = 0;
}
/* включить прозрачный режим */
openVisual () {
if (visual) return;
visual = 1;
ioctl (0, TCSETAW, &new);
}
/* выключить прозрачный режим */
closeVisual () {
if (!visual) return;
visual = 0;
ioctl (0, TCSETAW, &old);
}
struct stat st;
main (argc, argv) char **argv; {
int r, /* pid процесса-"читателя" */
w; /* pid процесса-"писателя" */
if (argc == 1) {
fprintf (stderr, "pty CMD ...\n");
exit (1);
}
initVisual ();
if((ptyfd = open ( PTY , O_RDWR)) < 0){
fprintf(stderr, "Cannot open pty\n"); exit(2);
}
/* запустить процесс чтения с псевдодисплея */
r = startReader ();
/* запустить процесс чтения с клавиатуры */
w = startWriter ();
sleep (2);
/* запустить протоколируемый процесс */
startSlave (argv + 1, r, w);
/* дождаться окончания всех потомков */
while (wait (NULL) > 0);
exit (0);
}
/* запуск протоколируемого процесса */
startSlave (argv, r, w) char **argv; {
FILE * ftty;
int pid;
int tfd;
char *tty = ttyname (1); /* полное имя нашего терминала */
if (!(pid = fork ())) {
/* PTY SLAVE process */
ftty = fopen (tty, "w"); /* Для выдачи сообщений */
setpgrp (); /* образовать новую группу процессов ;
* лишиться управляющего терминала */
/* закрыть стандартные ввод, вывод, вывод ошибок */
close (0);
close (1);
close (2);
/* первый открытый терминал станет управляющим для процесса,
* не имеющего управляющего терминала.
* Открываем псевдотерминал (slave) в качестве стандартных
* ввода, вывода и вывода ошибок
*/
open ( TTYP, O_RDWR);
open ( TTYP, O_RDWR);
tfd = open ( TTYP, O_RDWR);
if (tfd < 0) {
fprintf (ftty, "\rSlave: can't read/write pty\r\n");
kill(r, SIGKILL); kill(w, SIGKILL); exit (1);
}
/* запускаем целевую программу */
if (!(pid = fork ())) {
fprintf (ftty, "\rCreating %s\r\n", SEMAPHORE);
fflush (ftty);
/* создаем семафорный файл */
close (creat (SEMAPHORE, 0644));
fprintf (ftty, "\rStart %s\r\n", argv[0]);
fclose(ftty);
/* заменить ответвившийся процесс программой,
* указанной в аргументах
*/
execvp (argv[0], argv);
exit (errno);
}
/* дожидаться окончания целевой программы */
while (wait (NULL) != pid);
/* уничтожить семафор, что является признаком завершения
* для процессов чтения и записи
*/
unlink (SEMAPHORE);
fprintf (ftty, "\rDied.\r\n");
fflush (ftty);
/* убить процессы чтения и записи */
/* terminate reader & writer */
kill (r, SIGINT); kill (w, SIGINT);
exit (0);
}
return pid;
}
/* Пара master-процессов чтения и записи */
/* запуск процесса чтения с псевдотерминала (из master-части) */
startReader () {
char c[512];
int pid;
int n;
if (!(pid = fork ())) {
/* читать данные с ptyp на экран и в файл трассировки */
signal (SIGINT, bye);
/* ожидать появления семафора */
while (stat (SEMAPHORE, &st) < 0);
fprintf (stderr, "\rReader: Hello\r\n");
ftrace = fopen (TRACE, "w");
/* работать, пока существует семафорный файл */
while (stat (SEMAPHORE, &st) >= 0) {
/* прочесть очередные данные */
n = read (ptyfd, c, 512);
if( n > 0 ) {
/* записать их на настоящий терминал */
fwrite( c, sizeof(char), n, stdout );
/* и в файл протокола */
fwrite( c, sizeof(char), n, ftrace );
fflush (stdout);
}
}
bye ();
}
return pid;
}
/* запуск процесса чтения данных с клавиатуры и записи
* их на "псевдоклавиатуру". Эти данные протоколировать не надо,
* так как их эхо-отобразит сам псевдотерминал
*/
startWriter () {
char c;
int pid;
if (!(pid = fork ())) {
/* читать клавиатуру моего терминала и выдавать это в ptyp */
openVisual (); /* наш терминал - в прозрачный режим */
signal (SIGINT, onintr);
while (stat (SEMAPHORE, &st) < 0);
fprintf (stderr, "\rWriter: Hello\r\n");
/* работать, пока существует семафорный файл */
while (stat (SEMAPHORE, &st) >= 0) {
read (0, &c, 1); /* читать букву с клавиатуры */
write (ptyfd, &c, 1); /* записать ее на master-pty */
}
onintr (); /* завершиться */
}
return pid;
}
/* Пример 28 */
/* Оценка фрагментированности тома файловой системы
* (неупорядоченности блоков в файлах).
* Иллюстрация работы с файловой системой UNIX напрямую,
* в обход ядра системы. Для этого вы должны иметь права
* суперпользователя !!! Данная программа относится к классу
* "системных" (администраторских) программ.
* Эта программа предполагает каноническую файловую систему V7
* ("старую"), а не ту, которая используется начиная с BSD/4.2 и
* в которой все устроено несколько сложнее и эффективнее.
* Поэтому вы должны будете модифицировать эту программу для
* использования в современных UNIX-системах.
* По мотивам книги М.Дансмура и Г.Дейвиса.
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ino.h> /* struct dinode: disk inode */
#include <sys/stat.h> /* struct stat */
#include <sys/dir.h> /* struct direct */
char blkflag; /* печатать ли номера блоков файла */
/* Отведение памяти в куче с выдачей ошибки, если нет памяти */
char *MyAlloc( n ){
extern char *malloc();
char *ptr;
ptr = malloc( n );
if( ptr == NULL ){
fprintf( stderr, "Cannot allocate %d bytes\n", n );
exit(77);
}
return ptr;
}
char DEV[] = "/dev" ; /* каталог, где лежат все файлы устройств */
/* Определить имя устройства по его st_dev номеру.
* Поиск - по каталогу /dev
*/
char *whichdev( dev ) dev_t dev;
{
struct stat s;
struct direct d;
long i;
int fd; /* дескриптор чтения каталога */
long dsize; /* число слотов каталога */
char *devname;
if( stat( DEV, &s ) < 0 ){
fprintf( stderr, "Cannot stat %s\n", DEV );
exit(1);
}
if((fd = open( DEV, O_RDONLY )) < 0 ){
fprintf( stderr, "Cannot read %s\n", DEV );
/* попытаться перейти в этот каталог */
cd(SELECTION, wd, CWD);
} else if(T_TST(tbl, sel, I_EXE)){ /* выполняемый файл */
(void) Edit(tbl->win, SELECTION, RUNCMD);
} else {
editAccessModes(wd);
/* На самом деле надо производить подбор команды по
* типу файла (набор соответствий должен программироваться
* вами в специальном файле, считываемом при запуске коммандера).
* runCommand( classify(SELECTION));
* где классификация в простейшем случае - по имени и суффиксу,
* а в более развитом - еще и по кодам доступа (включая тип файла)
* и по первой строке файла (или "магическому числу").
*/
}
} /* end for */
t_leave( tbl );
out:
if( !retcode ) current_menu = SEL_PULL; /* выход по ESC */
return retcode;
}
/*-----------------------------------------------------------------*
* Горизонтальное командное меню (вызывается по ESC). *
*-----------------------------------------------------------------*/
PullInfo pm_items [] = { /* подсказка */
{{ " \\Left ", 0 }, NULL, "Left pane" }, /* 0 */
{{ " \\Commands ", 0 }, &mwrk, "Do some commands"}, /* 1 */
{{ " \\Tools ", PM_NOSEL }, NULL, "" }, /* 2 */
{{ " \\Sorttype ", 0 }, &msort, "Change sort type"}, /* 3 */
{{ " \\Right ", 0 }, NULL, "Right pane" }, /* 4 */
{{ NULL, 0 }, NULL, NULL }
};
void p_help(PullMenu *p, int n, int among){ Message( PM_NOTE(p, n)); }
/* Выбор в меню-строке */
void SelectPullMenu(){
int c, sel; Menu *m;
for(;current_menu == SEL_PULL;){
c = PullUsualSelect(&pull);
sel = pull.current;
if( PM_REFUSED(&pull)){ current_menu = previous_menu; return;}
switch(sel){
case 0: current_menu = SEL_PANE1; return;
case 1: SelectWorkingMenu(c); return;
case 2: return; /* не бывает */
case 3: SelectSortType(c); return;
case 4: current_menu = SEL_PANE2; return;
}
}
}
/*-----------------------------------------------------------------*
* Инициализация и завершение. *
*-----------------------------------------------------------------*/
void die(int sig){
echo(); nocbreak(); mvcur(-1,-1,LINES-1,0);
refresh(); endwin (); putchar('\n');
if(sig) printf("Signal %d\n", sig);
if(sig == SIGSEGV) abort(); else exit(sig);
}
void main (void) {
setlocale(LC_ALL, ""); /* получить информацию о языке диагностик */
initscr (); /* включить curses */
signal(SIGINT, die); /* по сигналу вызывать die(); */
signal(SIGBUS, die); /* по нарушению защиты памяти */
signal(SIGSEGV,die);
refresh(); /* обновить экран: это очистит его */
noecho(); cbreak(); /* выключить эхо, включить прозрачный ввод */
/* Проинициализировать истории */
HistInit(&hcwd, 20); hcwd. mnu.title = "История пути";
HistInit(&hedit, 20); hedit.mnu.title = "История команд";
HistInit(&hpat, 8); hpat. mnu.title = "Шаблоны имен";
/* Разметить меню сортировки */
msort.items = sort_info;
msort.title = "Вид сортировки каталога";
msort.top = 1; msort.left = 2;
msort.showMe = sort_show;
msort.bg_attrib = A_NORMAL; msort.sel_attrib = A_STANDOUT;
/* MnuInit (&msort); инициализируется в pull-menu */
/* Разметить рабочее меню */
mwrk.items = mwrk_info;
mwrk.title = "Главное меню";
mwrk.top = 1; mwrk.left = COLS/3;
mwrk.handler = NULL; mwrk.hitkeys = NULL;
mwrk.bg_attrib = A_STANDOUT; mwrk.sel_attrib = A_REVERSE;
mwrk.scrollBar = m_help;
#ifdef __GNUC__
mwrk_init();
#endif
/* MnuInit (&mwrk); инициализируется в pull-menu */
/* Разметить левую и правую панели */
tpane1.t.width = CENTER - 1;
tpane2.t.width = COLS - tpane1.t.width - 2 - (2 + BARWIDTH);
tpane1.t.height = tpane2.t.height = (LINES - 8);
tpane1.t.win = tpane2.t.win = panewin = stdscr;
tpane1.t.left = 1;
tpane2.t.left = CENTER+1;
tpane1.t.top = tpane2.t.top = 3;
tpane1.t.bg_attrib = tpane2.t.bg_attrib = A_NORMAL;
tpane1.t.sel_attrib = tpane2.t.sel_attrib = A_STANDOUT;
tpane1.t.scrollBar = tpane2.t.scrollBar = t_scrollbar;
tpane1.t.hitkeys = tpane2.t.hitkeys = t_hit;
tpane1.t.handler = tpane2.t.handler = t_handler;
tpane1.t.showMe = tpane2.t.showMe = t_show;
tpane1.t.hideMe = tpane2.t.hideMe = NULL;
/* Разметить имена для файловых объектов */
tpane1.d.name = strdup("Текущий каталог");
tpane2.d.name = strdup("Корневой каталог");
/* Изобразить рамки (но пока не проявлять их)
* Это надо сделать до первого cd(), т.к. иначе при неудаче будет выдано
* сообщение, которое проявит НЕЗАВЕРШЕННУЮ картинку */
t_border_common(); t_restore_corners();
/* Доразметить левую панель */
mychdir("."); /* узнать полное имя текущего каталога в CWD[] */
/* прочитать содержимое каталога CWD в tpane1.d */
cd( CWD , &tpane1, CWD);
tpane1.t.fmt = "directory";
InitTblFromDir(&tpane1, NO, NULL);
/* Доразметить правую панель */
tpane2.t.fmt = NULL;
/* прочитать содержимое каталога "/" в tpane2.d */
cd( "/", &tpane2, CWD); /* теперь стоим в корне */
/* Вернуться в рабочий каталог */
cd( tpane1.d.name, &tpane1, CWD);
/* Нарисовать обе панели */
TblDraw(A_tbl); TblDraw(B_tbl);
/* Разметить pulldown меню */
pull.bg_attrib = A_REVERSE; pull.sel_attrib = A_NORMAL;
pull.items = pm_items; pull.scrollBar = p_help;
PullInit(&pull);
/* Основной цикл */
for(done=NO, current_menu=SEL_PANE1, A_pane= &tpane1, B_pane= &tpane2;
done == NO; ){
Message("");
if(SEL_PANE) previous_menu = current_menu;
switch(current_menu){
case SEL_WRK : SelectWorkingMenu(NOSELECTED); break;
case SEL_PULL: SelectPullMenu(); break;
case SEL_PANE1: if( SelectPane(&tpane1) < 0)
M_SET(&mwrk, 0, I_NOSEL); break;
case SEL_PANE2: if( SelectPane(&tpane2) < 0)
M_SET(&mwrk, 0, I_NOSEL); break;
}
}
die(0); /* Завершить работу */
}
/* Пример 24 */
/* Пример коммуникации процессов при помощи программных каналов
* (трубы, pipes).
* Данная программа превращается в две программы,
* соединенные трубами в таком порядке:
*
* stdout stdin
* /------------ PIP1 -----------> cmd2
* cmd1 <----------PIP2---------------/
* stdin stdout
*/
/* файл LOOP_strt.c */
#include <stdio.h>
#define eq(s1,s2) ( strcmp(s1,s2) == 0 ) /* истина, если строки равны */
#define SEP "---" /* разделитель команд при наборе */
main( c, v ) char **v;
{
char **p, **q;
int pid;
int PIP1[2]; /* труба cmd1-->cmd2 */
int PIP2[2]; /* труба cmd2-->cmd1 */
if( c==1 ){
printf( "Call: strt cmd1... %s cmd2...\n", SEP );
exit(1);
}
/* разбор аргументов */
v++;
/* в p - аргументы первой команды */
p = v;
while( *v && !eq( *v, SEP ))
v++;
*v = NULL;
v++;
/* в q - аргументы второй команды */
q = v;
pipe( PIP1 ); /* создаем две трубы */
pipe( PIP2 ); /* PIP[0] - открыт на чтение, PIP[1] - на запись */
if( pid = fork()){ /* развилка: порождаем процесс */
/* ПОРОЖДЕННЫЙ ПРОЦЕСС */
fprintf( stderr, "сын=%s pid=%d\n", p[0], getpid());
/* перенаправляем stdout нового процесса в PIP1 */
dup2( PIP1[1], 1 );
close( PIP1[1] );
/* канал чтения мы не будем использовать */
close( PIP1[0] );
/* перенаправляем stdin из PIP2 */
dup2( PIP2[0], 0 );
close( PIP2[0] );
/* канал записи мы не будем использовать */
close( PIP2[1] );
/* начинаем выполнять программу, содержащуюся в
* файле p[0] с аргументами p (т.е. cmd1)
*/
execvp( p[0], p );
/* возврата из сисвызова exec не бывает */
}else{
/* ПРОЦЕСС-РОДИТЕЛЬ */
fprintf( stderr, "отец=%s pid=%d\n", q[0], getpid());
/* перенаправляем stdout в PIP2 */
dup2( PIP2[1], 1 );
close( PIP2[1] ); close( PIP2[0] );
/* перенаправляем stdin из PIP1 */
dup2( PIP1[0], 0 );
close( PIP1[0] ); close( PIP1[1] );
/* запускаем cmd2 */
execvp( q[0], q );
}
}
/* Ниже приводятся тексты двух программ, которые можно запустить
* как тест. Сервер компилируется в программу cmd2,
* клиент - в программу cmd1. Если запускающая программа
* скомпилирована в strt, то наберите команду
* strt cmd1 --- cmd2
* либо strt cmd2 --- cmd1
*/
/* файл LOOP_p.c ---------------------------------------------
* Процесс-клиент (cmd1)
*/
#include <stdio.h>
int trace = 1; /* вести трассировку своих действий */
main(c , v) char **v;
{
FILE *fp; int pid;
char buf[128];
fprintf( stderr, "P: process pid=%d\n", getpid());
fp = fopen( "LOOP_p.c", "r" );
/* открываем файл с текстом этой команды */
/* читаем его построчно */
while( fgets( buf, sizeof buf, fp ) != NULL ){
if( trace ) fprintf( stderr, "P посылает: %s", buf );
/* посылаем его в стандартный вывод: трубу PIP1 */
printf( "%s", buf );
fflush( stdout );
/* ожидать ответа из трубы PIP2 */
fgets( buf, sizeof buf, stdin );
if( trace ) fprintf( stderr, "P получил: %s", buf );
}
fclose( stdout );
/* отключиться от трубы PIP1. Если этого не сделать, сервер
* не прочитает из нее EOF */
while((pid = wait(NULL)) > 0 )
fprintf( stderr, "P: %d умер\n", pid );
}
/* файл LOOP_q.c ------------------------------------------------
* процесс-сервер (cmd2)
*/
#include <stdio.h>
int trace = 1;
main(c , v) char **v;
{
char buf[128]; int pid;
fprintf( stderr, "Q: process pid=%d\n", getpid());
/* читать поступающие из трубы PIP1 строки */
while( fgets( buf, sizeof(buf), stdin ) != NULL ){
/* напечатать полученное сообщение */
if( trace ) fprintf( stderr, "Q прочел: %s", buf );
if( trace ) fprintf( stderr, "Q отвечает: OK=%s", buf );
/* ответить в трубу PIP2 */
printf( "OK=%s", buf ); fflush( stdout );
}
fclose( stdout ); /* отключиться от трубы PIP2 */
while((pid = wait(NULL)) > 0 )
fprintf( stderr, "Q: %d умер\n", pid );
}
/* Пример 25 */
/* Пример использования именованных "труб" (pipes) FIFO-файлов
* для коммуникации независимых процессов
* (FIFO - first in, first out : первым пришел - первым ушел).
* По мотивам книги М.Дансмура и Г.Дейвиса.
*/
/* файл P_packet.h --------------------------------------------*/
#include <sys/types.h>
#include <sys/stat.h> /* S_IFIFO */
/* структура пакета-запроса */
struct packet {
int pk_pid; /* идентификатор процесса-отправителя */
int pk_blk; /* номер блока, который надо прочитать */
int pk_code; /* код запроса */
};
/* request codes (коды запросов) */
#define RQ_READ 0 /* запрос на чтение */
#define CONNECT 1 /* запрос на соединение */
#define SENDPID 2 /* ответ на запрос соединения */
#define DISCONNECT 3 /* разрыв связи */
#define BYE 4 /* завершить сервер */
/* имена FIFO-каналов связи */
#define DNAME "datapipe"
#define CNAME "ctrlpipe"
/* размер блока информации */
#define PBUFSIZE 512
/* P_client.c --------------------------------------------------------- */
/*
* Процесс-клиент, посылающий запросы к серверу.
*/
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include "P_packet.h"
int datapipe, ctrlpipe;
int got_sig;
int mypid; /* идентификатор процесса-клиента */
int spid; /* идентификатор процесса-сервера */
/* waiting for signal */
#define WAITSIG while( !got_sig )
void handler(nsig){
signal( SIGUSR1, handler );
got_sig ++;
}
void init(){
extern void die();
/* Ожидать создания каналов связи */
while( (datapipe = open( DNAME, O_RDONLY | O_NDELAY )) < 0 );
while( (ctrlpipe = open( CNAME, O_WRONLY | O_NDELAY )) < 0 );
mypid = getpid(); /* my process identifier */
printf( "Client pid=%d started\n", mypid );
signal( SIGINT, die);
signal( SIGQUIT, die);
signal( SIGTERM, die);
handler(0);
}
int canRun = 1;
void die(nsig){
canRun = 0;
}
/* подключиться к серверу, запросив его pid */
connect(){
struct packet pk;
pk.pk_pid = mypid;
pk.pk_code = CONNECT;
pk.pk_blk = (-1);
got_sig = 0;
write( ctrlpipe, &pk, sizeof pk ); /* послать запрос */
/* ожидать сигнала-"толчка" */
WAITSIG;
/* прочитать ответ из канала данных */
read( datapipe, &pk, sizeof pk );
/* послать сигнал-подтверждение */
kill( pk.pk_pid, SIGUSR1 );
return pk.pk_pid;
}
void disconnect(){
struct packet pk;
pk.pk_pid = mypid;
pk.pk_code = DISCONNECT;
pk.pk_blk = (-1);
got_sig = 0;
write( ctrlpipe, &pk, sizeof pk ); /* send request */
/* wait for reply */
WAITSIG;
/* receive reply */
read( datapipe, &pk, sizeof pk );
/* confirm */
kill( pk.pk_pid, SIGUSR1 );
printf( "Disconnected.\n" );
}
request( ptr, blk, spid )
char *ptr;
int blk;
int spid;
{
struct packet pk;
pk.pk_pid = mypid;
pk.pk_blk = blk;
pk.pk_code = RQ_READ;
got_sig = 0;
write( ctrlpipe, &pk, sizeof pk );
WAITSIG;
read( datapipe, ptr, PBUFSIZE );
kill( spid, SIGUSR1 );
}
bye(){
struct packet pk;
pk.pk_pid = mypid;
pk.pk_code = BYE;
pk.pk_blk = (-1);
got_sig = 0;
write( ctrlpipe, &pk, sizeof pk ); /* send request */
exit(0);
}
/* client [номер_блока] */
main(argc, argv) char *argv[];
{
int blk;
char buffer[ PBUFSIZE ];
setbuf( stdout, NULL ); /* make unbuffered */
blk = (argv[1] ? atoi( argv[1] ) : 0);
init();
spid = connect();
printf( "Client pid=%d connected to server pid=%d\n",
mypid, spid );
/* запрос блока номер -33 соответствует запросу "завершить
* работу сервера"
*/
if( blk == -33 )
bye();
/* в цикле посылать запросы на чтение блока blk */
while( canRun ){
request( buffer, blk, spid );
printf( "\nBEG-------------------------------------\n" );
fwrite( buffer, PBUFSIZE, 1, stdout );
printf( "\nEND-------------------------------------\n" );
}
disconnect(); /* отключиться от сервера */
exit(0);
}
/* P_server.c ---------------------------------------------------------*/
/*
* Процесс-сервер, принимающий запросы и выполняющий их.
*/
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include "P_packet.h"
int datapipe, ctrlpipe, datafile, got_sig;
char *dataname = "/etc/passwd";
/* waiting for signal */
#define WAITSIG while( !got_sig )
void handler(nsig){
signal( SIGUSR1, handler ); /* reset trap */
got_sig++;
}
/* завершение работы сервера: уничтожить каналы связи */
void die(nsig){
unlink( CNAME ); unlink( DNAME ); exit(0);
/* Если эти файлы были открыты клиентами,
* то клиенты не умрут, хотя имена файлов и будут удалены!
*/
}
main(){
struct packet pk;
struct packet sendpk;
/* сделать стандартный вывод небуферизованным каналом */
setbuf( stdout, NULL ); /* make unbuffered */
/* создать каналы связи */
mknod( DNAME, S_IFIFO | 0666, 0 ); /* create FIFO */
mknod( CNAME, S_IFIFO | 0666, 0 ); /* create FIFO */
/* по этим сигналам будет вызываться функция die() */
signal( SIGINT, die );
signal( SIGQUIT, die );
signal( SIGTERM, die );
/* Открыть управляющий канал связи. O_NDELAY означает,
* что файл открывается для "чтения без ожидания",
* т.е. если канал пуст (нет заявок), то системный вызов
* read() не будет "спать", дожидаясь появления информации,
* а просто вернет 0 (прочитано 0 байт).
* Этот флаг применим также к чтению с терминала.
*/
ctrlpipe = open( CNAME, O_RDONLY | O_NDELAY );
if( ctrlpipe < 0 ){
printf( "Can't open %s\n", CNAME );
die(0);
}
datafile = open( dataname, O_RDONLY );
if( datafile < 0 ){
printf( "Can't open %s\n", dataname );
die(0);
}
/* заранее формируем пакет для ответов */
sendpk.pk_code = SENDPID;
sendpk.pk_pid = getpid(); /* server's pid */
sendpk.pk_blk = (-1);
printf( "Server pid=%d\n", getpid());
handler(0);
for(;;){
int n;
static long i = 0L;
/* active spin loop */
printf( "%20ld\r", i++ );
/* опрашивать канал насчет поступления запросов */
while((n = read( ctrlpipe, &pk, sizeof(pk))) > 0 ){
putchar( '\n' );
if( n != sizeof pk ){
printf( "Wrong packet size\n" );
continue;
}
/* обработать прочитанный запрос */
process( &pk, &sendpk );
}
}
die(0);
}
process( pkp, spkp )
struct packet *pkp, *spkp;
{
char pbuf[ PBUFSIZE ];
/* Запись в FIFO-файл будет произведена только если
* он уже открыт для чтения
*/
datapipe = open( DNAME, O_WRONLY | O_NDELAY );
printf( "REQUEST TYPE_%d from pid=%d blk=%d\n",
pkp->pk_code, pkp->pk_pid, pkp->pk_blk );
switch( pkp -> pk_code ){
case CONNECT: /* ответить своим идентификатором процесса */
write( datapipe, spkp, sizeof( struct packet ));
break;
case RQ_READ: /* ответить блоком информации из файла */
/* read block # pk_blk */
lseek( datafile, pkp -> pk_blk * (long)PBUFSIZE, 0 );
read( datafile, pbuf, PBUFSIZE );
write( datapipe, pbuf, PBUFSIZE );
break;
case DISCONNECT: /* подтвердить отключение */
printf( "Client pid=%d finished\n", pkp -> pk_pid );
write ( datapipe, spkp, sizeof( struct packet ));
break;
case BYE: /* завершиться */
printf( "Server terminated.\n" );
kill( pkp-> pk_pid, SIGKILL );
die(0);
default:
printf( "Unknown packet type %d\n", pkp -> pk_code );
break;
}
close( datapipe );
/* "подтолкнуть" отправителя сигналом */
got_sig = 0;
kill( pkp -> pk_pid , SIGUSR1 );
printf( "Waiting for reply... " );
/* ждать сигнала-подтверждения от клиента */
WAITSIG;
printf( "server continued\n" );
}
/* Пример 26 */
/* Общение процессов при помощи общей памяти и семафоров.
* Вызов: shms &
* shmc a & shmc b & shmc c &
*/
/* --------------------------- файл shm.h ----------------------- */
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <signal.h>
#include <errno.h>
extern errno; /* Системный код ошибки */
struct connect { /* Структура почтового ящика */
int pid; int msgnum; int max;
char message[128]; /* текст сообщения */
};
#define NSEMS 3 /* число семафоров */
/* Имена семафоров */
#define EMPTY 0 /* 1 - ящик пуст; 0 - содержит письмо */
#define NOTEMPTY 1 /* негатив для EMPTY */
#define ACCESS 2 /* 1 - ящик доступен (закрыт);
* 0 - ящик уже открыт кем-то еще */
/* Значения семафоров */
#define YES 1
#define NO 0
/* Операции */
#define OPEN 1
#define CLOSE (-1)
#define TEST_NO 0
#ifdef COMMENT
Алгоритм одновременного изменения семафоров: semop
Дано:
аргумент: число семафоров : nsems
аргумент: величины изменения : sem_op[i]
в ядре: текущие значения семафоров группы sem_id: sem[i]
Алгоритм:
again: Сохранить значения всех семафоров (для отмены изменений);
for(i=0; i<nsems; i++)
/* OPEN */ if( sem_op[i] > 0 ){
sem[i] += sem_op[i];
разбудитьЖдущихСобытие( "sem[i]++" );
/* CLOSE */ }else if( sem_op[i] < 0 ){
if((newsm = sem[i] + sem_op[i]) >= 0 ){
sem[i] = newsm;
if( sem[i] == 0 )
разбудитьЖдущихСобытие( "sem[i]==0" );
}else{
восстановитьВсеСемафоры;
ждатьСобытие( "sem[i]++" );
goto again;
}
/* TEST0 */ }else{ /* sem_op[i] == 0 */
if( sem[i] != 0 ){
восстановитьВсеСемафоры;
ждатьСобытие( "sem[i]==0" );
goto again;
}
}
Алгоритм синхронизации в нашей схеме КЛИЕНТ-СЕРВЕР:
|----------------------------------------------------------------|
|семафоры: EMPTY ACCESS |
|----------------------------------------------------------------|
|начальное значение: YES YES |
|----------------------------------------------------------------|
СЕРВЕР
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: NO YES |
|сделать: NO(test0) NO(close) |
|----------------------------------------------------------------|
| прочесть почту; |
|----------------------------------------------------------------|
|из: NO NO |
|сделать: YES(open) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
КЛИЕНТ
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: YES YES |
|сделать: YES(test!=0) NO(close) |
|----------------------------------------------------------------|
| записать почту; |
|----------------------------------------------------------------|
|из: YES NO |
|сделать: NO(close) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
К сожалению, операции test!=0 не существует - приходится вводить
дополнительный семафор NOTEMPTY, негативный для EMPTY:
|----------------------------------------------------------------|
|семафоры: EMPTY NOTEMPTY ACCESS |
|----------------------------------------------------------------|
|начальное значение: YES NO YES |
|----------------------------------------------------------------|
СЕРВЕР
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: NO - YES |
|сделать: NO(test0) - NO(close) |
|----------------------------------------------------------------|
| прочесть почту; |
|----------------------------------------------------------------|
|из: NO YES NO |
|сделать: YES(open) NO(close) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
КЛИЕНТ
|================================================================|
|loop: |
|----------------------------------------------------------------|
|ждать: - NO YES |
|сделать: - NO(test0) NO(close) |
|----------------------------------------------------------------|
| записать почту; |
|----------------------------------------------------------------|
|из: YES NO NO |
|сделать: NO(close) YES(open) YES(open) |
|----------------------------------------------------------------|
| goto loop; |
|================================================================|
#endif /*COMMENT*/
/* Общая часть сервера и клиента ------------------------------- */
key_t key = 1917; /* Уникальный ключ для доступа */
int shm_id; /* Дескриптор для доступа к общей памяти */
int sem_id; /* Дескриптор для доступа к семафорам */
char name[40]; /* имя программы */
char far *addr;
struct connect far *caddr;
struct sembuf ops[NSEMS];
/* EMPTY NOTEMPTY ACCESS */
short values[NSEMS] = { YES, NO, YES };
void semtell(msg, name) char *msg, *name; { int i;
semctl(sem_id, NSEMS, GETALL, values);
printf( "%s %-10s: значения семафоров:", name, msg);
for(i=0; i < NSEMS; i++) printf( " %d", values[i]);
putchar('\n');
}
void inisem(){
register i;
for(i=0; i < NSEMS; i++ ) ops[i].sem_flg = 0;
}
/* --------------------------- файл shms.c ----------------------- */
/* Shared memory server */
#include "shm.h"
int npack; /* номер сообщения */
void cleanup(sig){
/* Уничтожить сегмент общей памяти (это нужно делать явно) */
shmctl( shm_id, IPC_RMID, NULL );
/* Уничтожить семафоры */
semctl( sem_id, NSEMS, IPC_RMID, NULL );
if( npack ) printf( "\t** Всего было %d сообщений **\n", npack+1);
exit(0);
}
void main(){
register i; int pid = getpid();
FILE *fout;
sprintf( name, "Server-%03d", pid );
for( i = 1; i <= SIGTERM; i++ )
signal( i, cleanup );
/* Создать разделяемый сегмент */
if((shm_id = shmget( key, sizeof(struct connect),
0644 | IPC_CREAT )) < 0 ){
perror( "shmget" ) ; exit(1);
}
/* Подключить общий сегмент к произвольному адресу */
if((addr = (char far *) shmat( shm_id, NULL, 0 )) == NULL ){
perror( "shmat" ); cleanup();
}
caddr = (struct connect far *) addr;
/* Создать группу из NSEMS семафоров */
if((sem_id = semget( key, NSEMS, 0644 |IPC_CREAT |IPC_EXCL)) < 0){
if(errno == EEXIST){ printf( "Сервер уже запущен\n");exit(2); }
else{ perror( "semget" ); cleanup(); }
}
/* Загрузить начальные значения семафоров */
semctl( sem_id, NSEMS, SETALL, values );
setbuf(stdout, NULL);
inisem(); printf( "Server is up now. Читай файл MESSAGES.\n");
fout = fopen( "MESSAGES", "w");
for(;;npack++){
printf( "%s: ждет почты\n", name );
semtell("Вход", name);
ops[0].sem_num = EMPTY; ops[0].sem_op = TEST_NO;
ops[1].sem_num = ACCESS; ops[1].sem_op = CLOSE;
semop( sem_id, ops, 2 /* сразу два семафора */);
printf( "%s: GOT-%02d/%02d от %d \"%s\"\n", name,
caddr->msgnum, caddr->max, caddr->pid, caddr->message);
fprintf( fout, "#%03d %02d/%02d от %d \"%s\"\n", npack,
caddr->msgnum, caddr->max, caddr->pid, caddr->message);
if( ! strcmp(caddr->message, "-exit" )){
printf( "%s: завершает работу.\n", name );
cleanup();
}
semtell("Выход", name);
ops[0].sem_num = EMPTY ; ops[0].sem_op = OPEN;
ops[1].sem_num = NOTEMPTY; ops[1].sem_op = CLOSE;
ops[2].sem_num = ACCESS ; ops[2].sem_op = OPEN;
semop( sem_id, ops, 3 /* сразу три семафора */);
}
/*NOTREACHED*/
}
/* --------------------------- файл shmc.c ----------------------- */
/* Shared memory client */
#include "shm.h"
void ignsigs(sig){
register i;
for( i = 1; i <= SIGTERM; i++ )
signal( i, ignsigs );
printf( "Клиент игнорирует сигналы,\n\
чтобы не оставлять закрытых семафоров в случае своей смерти.\n" );
}
void main(argc, argv) char **argv; {
int pid = getpid();
int i, ntimes = 60;
if( argc < 2 ){
fprintf( stderr, "Вызов: %s сообщение [числоПовторов]\n", argv[0] );
fprintf( stderr, "сообщение \"-exit\" завершает сервер\n");
fprintf( stderr, "сообщение \"-info\" выдает значения семафоров\n");
exit(1);
}
if( argc > 2 ) ntimes = atoi(argv[2]);
sprintf( name, "Client-%03d", pid);
ignsigs(); srand( pid );
/* Получить доступ к разделяемому сегменту */
if((shm_id = shmget( key, sizeof(struct connect), 0644)) < 0 ){
perror( "shmget" ); exit(2);
}
/* Подключить общий сегмент к произвольному адресу */
if((addr = (char far *) shmat( shm_id, NULL, 0 )) == NULL ){
perror( "shmat" ); exit(3);
}
caddr = (struct connect far *) addr;
/* Получить доступ к семафорам */
if((sem_id = semget( key, NSEMS, 0644)) < 0 ){
perror( "semget" ); exit(4);
}
setbuf(stdout, NULL);
inisem();
if( !strcmp(argv[1], "-info")){
semtell("Информация", name); exit(0);
}
for( i=0; i < ntimes; i++ ){
printf( "%s: ждет пустого ящика\n", name);
semtell("Вход", name);
ops[0].sem_num = NOTEMPTY; ops[0].sem_op = TEST_NO;
ops[1].sem_num = ACCESS ; ops[1].sem_op = CLOSE;
if( semop( sem_id, ops, 2 /* сразу два семафора */) < 0)
goto err;
caddr->pid = pid; caddr->msgnum = i; caddr->max = ntimes;
strncpy( caddr->message, argv[1],
sizeof(caddr->message) - 1);
printf( "%s: PUT-%02d \"%s\"\n", name, i, argv[1]);
semtell("Выход", name);
ops[0].sem_num = EMPTY ; ops[0].sem_op = CLOSE;
ops[1].sem_num = NOTEMPTY; ops[1].sem_op = OPEN;
ops[2].sem_num = ACCESS ; ops[2].sem_op = OPEN;
if( semop( sem_id, ops, 3 /* сразу три семафора */) < 0)
goto err;
if( rand()%2 ) sleep(2); /* пауза */
}
shmdt( addr ); /* Отключиться от общего сегмента */
exit(0);
err:
perror("semop");
exit(5);
}
/* Пример 27 */
/* Коммуникация процессов при помощи псевдо-терминала.
* Данная программа позволяет сохранять полный протокол работы
* экранной программы в файл.
* Не экранные программы данная версия НЕ трассирует,
* поскольку сама работает в "прозрачном" режиме.
*
* Вариацией данной программы может служить использование
* системного вызова select() вместо запуска нескольких процессов.
*
* Программа также иллюстрирует "дерево" из 5 процессов.
* Данная версия написана для UNIX System V.
* TRACE__
* \ \ master slave
* |экран<======\(Reader)=======!~!<====(целевая )
* / <==\ | ! !====>(программа)
* \ | !P! |
* | | !T! |
* . . . . | | !Y! (Slave)-->Управляет
* клавиатура=|===|=>(Writer)=>!_! | \ семафором
* | | | | \
* | #####starter################## \
* |...................................|
* ftty
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <termio.h>
#include <sys/stat.h>
#include <fcntl.h>
extern int exit ();
extern char *ttyname ();
extern FILE * fopen ();
extern errno;
#define SEMAPHORE "/tmp/+++" /* семафорный файл */
#define TRACE "./TRACE" /* файл с протоколом */
/* псевдотерминал связи */
/* master - это часть, которая ведет себя как ФАЙЛ и умеет
* реагировать на некоторые специальные ioctl()-и */
#define PTY "/dev/ptyp0" /* master */
/* slave - это часть, которая ведет себя как драйвер терминалов */
#define TTYP "/dev/ttyp0" /* slave */
int ptyfd;
FILE * ftrace = NULL;
/* при прерывании завершить работу процесса "писателя" */
onintr () {
closeVisual ();
fprintf (stderr, "\rwriter finished\r\n");
exit (0);
}
/* завершение работы процесса-"читателя" */
bye () {
if (ftrace)
fclose (ftrace);
fprintf (stderr, "\rreader finished\r\n");
exit (0);
}
int visual = 0;
struct termio old,
new;
/* настроить режимы работы терминала на "прозрачный" режим */
initVisual () {
ioctl (0, TCGETA, &old);
new = old;
new.c_iflag &= ~ICRNL;
new.c_lflag &= ~(ECHO | ICANON);
new.c_oflag &= ~(TAB3 | ONLCR);
new.c_cc[VMIN] = 1;
new.c_cc[VTIME] = 0;
/* new.c_cc[VINTR] = ctrl('C'); */
new.c_cc[VQUIT] = 0;
new.c_cc[VERASE] = 0;
new.c_cc[VKILL] = 0;
}
/* включить прозрачный режим */
openVisual () {
if (visual) return;
visual = 1;
ioctl (0, TCSETAW, &new);
}
/* выключить прозрачный режим */
closeVisual () {
if (!visual) return;
visual = 0;
ioctl (0, TCSETAW, &old);
}
struct stat st;
main (argc, argv) char **argv; {
int r, /* pid процесса-"читателя" */
w; /* pid процесса-"писателя" */
if (argc == 1) {
fprintf (stderr, "pty CMD ...\n");
exit (1);
}
initVisual ();
if((ptyfd = open ( PTY , O_RDWR)) < 0){
fprintf(stderr, "Cannot open pty\n"); exit(2);
}
/* запустить процесс чтения с псевдодисплея */
r = startReader ();
/* запустить процесс чтения с клавиатуры */
w = startWriter ();
sleep (2);
/* запустить протоколируемый процесс */
startSlave (argv + 1, r, w);
/* дождаться окончания всех потомков */
while (wait (NULL) > 0);
exit (0);
}
/* запуск протоколируемого процесса */
startSlave (argv, r, w) char **argv; {
FILE * ftty;
int pid;
int tfd;
char *tty = ttyname (1); /* полное имя нашего терминала */
if (!(pid = fork ())) {
/* PTY SLAVE process */
ftty = fopen (tty, "w"); /* Для выдачи сообщений */
setpgrp (); /* образовать новую группу процессов ;
* лишиться управляющего терминала */
/* закрыть стандартные ввод, вывод, вывод ошибок */
close (0);
close (1);
close (2);
/* первый открытый терминал станет управляющим для процесса,
* не имеющего управляющего терминала.
* Открываем псевдотерминал (slave) в качестве стандартных
* ввода, вывода и вывода ошибок
*/
open ( TTYP, O_RDWR);
open ( TTYP, O_RDWR);
tfd = open ( TTYP, O_RDWR);
if (tfd < 0) {
fprintf (ftty, "\rSlave: can't read/write pty\r\n");
kill(r, SIGKILL); kill(w, SIGKILL); exit (1);
}
/* запускаем целевую программу */
if (!(pid = fork ())) {
fprintf (ftty, "\rCreating %s\r\n", SEMAPHORE);
fflush (ftty);
/* создаем семафорный файл */
close (creat (SEMAPHORE, 0644));
fprintf (ftty, "\rStart %s\r\n", argv[0]);
fclose(ftty);
/* заменить ответвившийся процесс программой,
* указанной в аргументах
*/
execvp (argv[0], argv);
exit (errno);
}
/* дожидаться окончания целевой программы */
while (wait (NULL) != pid);
/* уничтожить семафор, что является признаком завершения
* для процессов чтения и записи
*/
unlink (SEMAPHORE);
fprintf (ftty, "\rDied.\r\n");
fflush (ftty);
/* убить процессы чтения и записи */
/* terminate reader & writer */
kill (r, SIGINT); kill (w, SIGINT);
exit (0);
}
return pid;
}
/* Пара master-процессов чтения и записи */
/* запуск процесса чтения с псевдотерминала (из master-части) */
startReader () {
char c[512];
int pid;
int n;
if (!(pid = fork ())) {
/* читать данные с ptyp на экран и в файл трассировки */
signal (SIGINT, bye);
/* ожидать появления семафора */
while (stat (SEMAPHORE, &st) < 0);
fprintf (stderr, "\rReader: Hello\r\n");
ftrace = fopen (TRACE, "w");
/* работать, пока существует семафорный файл */
while (stat (SEMAPHORE, &st) >= 0) {
/* прочесть очередные данные */
n = read (ptyfd, c, 512);
if( n > 0 ) {
/* записать их на настоящий терминал */
fwrite( c, sizeof(char), n, stdout );
/* и в файл протокола */
fwrite( c, sizeof(char), n, ftrace );
fflush (stdout);
}
}
bye ();
}
return pid;
}
/* запуск процесса чтения данных с клавиатуры и записи
* их на "псевдоклавиатуру". Эти данные протоколировать не надо,
* так как их эхо-отобразит сам псевдотерминал
*/
startWriter () {
char c;
int pid;
if (!(pid = fork ())) {
/* читать клавиатуру моего терминала и выдавать это в ptyp */
openVisual (); /* наш терминал - в прозрачный режим */
signal (SIGINT, onintr);
while (stat (SEMAPHORE, &st) < 0);
fprintf (stderr, "\rWriter: Hello\r\n");
/* работать, пока существует семафорный файл */
while (stat (SEMAPHORE, &st) >= 0) {
read (0, &c, 1); /* читать букву с клавиатуры */
write (ptyfd, &c, 1); /* записать ее на master-pty */
}
onintr (); /* завершиться */
}
return pid;
}
/* Пример 28 */
/* Оценка фрагментированности тома файловой системы
* (неупорядоченности блоков в файлах).
* Иллюстрация работы с файловой системой UNIX напрямую,
* в обход ядра системы. Для этого вы должны иметь права
* суперпользователя !!! Данная программа относится к классу
* "системных" (администраторских) программ.
* Эта программа предполагает каноническую файловую систему V7
* ("старую"), а не ту, которая используется начиная с BSD/4.2 и
* в которой все устроено несколько сложнее и эффективнее.
* Поэтому вы должны будете модифицировать эту программу для
* использования в современных UNIX-системах.
* По мотивам книги М.Дансмура и Г.Дейвиса.
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/ino.h> /* struct dinode: disk inode */
#include <sys/stat.h> /* struct stat */
#include <sys/dir.h> /* struct direct */
char blkflag; /* печатать ли номера блоков файла */
/* Отведение памяти в куче с выдачей ошибки, если нет памяти */
char *MyAlloc( n ){
extern char *malloc();
char *ptr;
ptr = malloc( n );
if( ptr == NULL ){
fprintf( stderr, "Cannot allocate %d bytes\n", n );
exit(77);
}
return ptr;
}
char DEV[] = "/dev" ; /* каталог, где лежат все файлы устройств */
/* Определить имя устройства по его st_dev номеру.
* Поиск - по каталогу /dev
*/
char *whichdev( dev ) dev_t dev;
{
struct stat s;
struct direct d;
long i;
int fd; /* дескриптор чтения каталога */
long dsize; /* число слотов каталога */
char *devname;
if( stat( DEV, &s ) < 0 ){
fprintf( stderr, "Cannot stat %s\n", DEV );
exit(1);
}
if((fd = open( DEV, O_RDONLY )) < 0 ){
fprintf( stderr, "Cannot read %s\n", DEV );