LINUX.ORG.RU

проблема с чтением из FIFO

 ,


0

2

Доброго времени суток. Стоит задача по реализации программы с разделением на процессы, использующих в качестве средства взаимодействия именованные каналы. Краткое описание задачи: Родитель передает три строки, потомок возвращает самую короткую из них.

#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

#define FIFO_NAME "my_fifo"

void hdl(int sig) {
    if(sig == SIGUSR1)
    printf("SIGUSR1...\n");
    else if(sig == SIGUSR2)
    printf("SIGUSR2...\n");
    else
    printf("Something else\n");
}
int main() {
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = hdl;
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGUSR1);
    sigaddset(&set, SIGUSR2);
    act.sa_mask = set;
    sigaction(SIGUSR1, &act, 0);
    sigaction(SIGUSR2, &act, 0);
    signal(SIGUSR1, hdl);
    int sig;
    pid_t pid;

    char str1[100], str2[100], str3[100];
    char shortest[100];

    int fd_fifo; /* дескриптор FIFO */

    /* Если файл с таким именем существует, удалим его */
    unlink(FIFO_NAME);

    /* Обнуляем маску прав */
    (void)umask(0);

    /* Создаем FIFO */
    if ((mkfifo(FIFO_NAME, 0666)) == -1) {
        perror("Невозможно создать fifo\n");
        exit(0);
    }

    pid=fork();
    if (pid>0) {
        printf("parent...\n");
        printf("Pid = %d\n",getpid() );

        printf("Введите первую строку: ");
        fgets(str1, sizeof(str1), stdin);
        printf("Введите вторую строку: ");
        fgets(str2, sizeof(str2), stdin);
        printf("Введите третью строку: ");
        fgets(str3, sizeof(str3), stdin);
        /* Открываем fifo для чтения и записи */
        if ((fd_fifo = open(FIFO_NAME, O_RDWR)) == -1) {
            perror("Невозможно открыть fifo\n");
            exit(0);
        }

        /* Записываем в fifo */
        //write(fd_fifo,buffer,strlen(buffer)) ;
        write(fd_fifo, str1, strlen(str1) + 1);
        write(fd_fifo, str2, strlen(str2) + 1);
        write(fd_fifo, str3, strlen(str3) + 1);
        close(fd_fifo);

        //посылаем сигнал дочернему процессу
        kill(pid, SIGUSR1);
        sigemptyset(&set);
        sigaddset(&set, SIGUSR2);
        printf("parent wait signal...\n");

        //ожидаем сигнал SIGUSR1
        sigwait(&set, &sig);
        printf("parent exit...\n");

        /* Открываем fifo для чтения и записи */
        if ((fd_fifo = open(FIFO_NAME, O_RDWR|O_NONBLOCK)) == -1) {
            perror("Невозможно открыть fifo\n");
            exit(0);
        }

        // Читаем самую короткую строку от дочернего процесса
        read(fd_fifo, shortest, sizeof(shortest));
        printf("Родитель получил от потомка самую короткую строку: %s\n", shortest);
        close(fd_fifo);

        }
        else if (pid==0){ //дочерний процесс
            sigemptyset(&set);
            sigaddset(&set, SIGUSR1);
            //ожидаем сигнал SIGUSR1
            sigwait(&set, &sig);
            printf("child...\n");
            printf("Pid = %d\n",getpid() );
            printf("work...\n");

            /* Открываем fifo для чтения и записи */
            if ((fd_fifo = open(FIFO_NAME, O_RDWR)) == -1) {
                perror("Невозможно открыть fifo\n");
                exit(0);
            }

            // Чтение строк от родителя
            if(read(fd_fifo, str1, sizeof(str1)) == -1)
                perror("Невозможно прочесть str1 из FIFO\n");
            if(read(fd_fifo, str2, sizeof(str2)) == -1)
                perror("Невозможно прочесть str2 из FIFO\n");
            if(read(fd_fifo, str3, sizeof(str3)) == -1)
                perror("Невозможно прочесть str3 из FIFO\n");
            close(fd_fifo);

            printf("Дочерний процесс получил три строки от родителя.\n");
            printf("%s\n, %s\n, %s\n", str1, str2, str3);

            // Находим самую короткую строку
            strcpy(shortest, str1);
            if (strlen(str2) < strlen(shortest)) {
                strcpy(shortest, str2);
            }
            if (strlen(str3) < strlen(shortest)) {
                strcpy(shortest, str3);
            }
            printf("Дочерний процесс вычислил самую короткую строку: %s\n", shortest);

            /*открываем fifo для чтения и записи */
            if ((fd_fifo = open(FIFO_NAME, O_RDWR)) == -1) {
                perror("Невозможно открыть fifo\n");
                exit(0);
            }

            // Записываем самую короткую строку в FIFO
            write(fd_fifo, shortest, strlen(shortest) + 1);
            printf("Дочерний процесс отправил родителю самую короткую строку: %s\n", shortest);
            close(fd_fifo);

            sleep(5);
            //посылаем сигнал родительскому процессу
            kill(getppid(), SIGUSR2);
            printf("child exit...\n");
            exit(0);
        }
    return 0;
}

программа зависает на моменте открытия FIFO в дочернем процессе, но ошибку при этом не выдает

parent...
Pid = 13172
Введите первую строку: a
Введите вторую строку: aa
Введите третью строку: aaa
parent wait signal...
child...
Pid = 13173
work...

Так с fifo не работают, в него надо писать и читать одновременно. А в тот момент, когда вы делаете первый раз close(fifo) в родители, у fifo ведь не остаётся ни одно читателя/писателя.

ошибку при этом не выдает

Странно отношение к ошибкам, read() проверяете, а write() нет.

mky ★★★★★
()
write(fd_fifo, str1, strlen(str1) + 1);

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

pathfinder ★★★★
()
Ответ на: комментарий от pathfinder

write() может записать меньше, чем ты указал. К этому надо быть готовым.

Интересно было бы узнать, в какой ситуации (т.е. записал не 0, но меньше, чем запрошено) и как это промоделировать.

vbr ★★★★
()
Ответ на: комментарий от vbr

IMHO при неблокируемом вводе/выводе если остаток буфера меньше чем ты хочешь записать.

У fifo есть буфер. Записать несколько блоков размером некратному размеру буфера (4/8к?).

vel ★★★★★
()
Ответ на: комментарий от vbr

Это конечно не совсем то, то ты хотел, но я приведу кусок из манов.

       Note that a successful write() may transfer fewer than count
       bytes.  Such partial writes can occur for various reasons; for
       example, because there was insufficient space on the disk device
       to write all of the requested bytes, or because a blocked write()
       to a socket, pipe, or similar was interrupted by a signal handler
       after it had transferred some, but before it had transferred all
       of the requested bytes.  In the event of a partial write, the
       caller can make another write() call to transfer the remaining
       bytes.  The subsequent call will either transfer further bytes or
       may result in an error (e.g., if the disk is now full).

pathfinder ★★★★
()
Ответ на: комментарий от firkax

Я не зря поставил знак вопроса в предпологаемом размере буфера. Судя по всему он сильно зависит от архитектуры и ос.

Но буфер есть и при неблокируемой записи можно получить ситуацию с частичной записью данных.

Кстати, немного другой вариант: разрешены siginterrupt, процесс с блокируемой записью записал часть данных в буфер, а часть не влезла и он остался ждать, а тут бац, и сигнал. Получил он результат -1 и EINTR. Как продолжить? С сокетами таже фигня :)

vel ★★★★★
()

А, нет, всё правильно я писал. У тебя проблема в том, что read() ждёт все 100 байт. Я немного причесал твой код в процессе исследования, но дальше ты сам.

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>


#define FIFO_NAME "my_fifo"


void signalHandler( int sig ) {
         if( sig == SIGUSR1 ) printf( "[Signal] SIGUSR1...\n" );
    else if( sig == SIGUSR2 ) printf( "[Signal] SIGUSR2...\n" );
    else                      printf( "[Signal] Something else\n" );
}


static int readString( int fd, char* buf, size_t bufsize ){
	
	int i;
	int res;
	
	for( i = 0; i < bufsize; i++ ){
		
		res = read( fd, buf + i, 1 );
		if( res < 0 ){
			perror( "string read" );
			return -1;
		}
		
		if( !res ){
			printf( "Unexpected end of line.\n" );
			return -1;
		}
		
		//ignore LF
		if( buf[i] == '\n' ){
			i--;
			continue;
		}
		
		//end of line
		if( !buf[i] ) return 0;
		
	}
	
	
	return 0;
}



static int mainProcess( pid_t children ){
	
	int res;
	int fd_fifo;
	char str1[100];
	char str2[100];
	char str3[100];
	char shortest[100];
	
	int sig;
	sigset_t set;
	struct sigaction act;
	
	
	//инициализируем маску сигналов для родителя
	sigemptyset( &set );
	sigaddset( &set, SIGUSR1 );
	
	//добавляем обработчик сигнала SIGUSR1
	memset( &act, 0, sizeof(act) );
	act.sa_handler = signalHandler;
	sigaction( SIGUSR1, &act, NULL );
	
	
	printf( "{P} Parent is here \\ё/\n" );
	printf( "{P} Pid = %d\n",getpid() );
	
	sleep( 1 );
	printf( "{P} Введите первую строку: " );
	fgets( str1, sizeof(str1), stdin );
	printf( "{P} Введите вторую строку: " );
	fgets( str2, sizeof(str2), stdin );
	printf( "{P} Введите третью строку: " );
	fgets( str3, sizeof(str3), stdin );
	
	//Открываем fifo для чтения и записи
	fd_fifo = open( FIFO_NAME, O_RDWR );
	if( fd_fifo < 0 ) {
		perror( "{P} fifo open" );
		return 1;
	}
	
	//записываем в fifo
	//write(fd_fifo,buffer,strlen(buffer)) ;
	write( fd_fifo, str1, strlen(str1) + 1 );
	write( fd_fifo, str2, strlen(str2) + 1 );
	write( fd_fifo, str3, strlen(str3) + 1 );
	
	//посылаем сигнал дочернему процессу
	res = kill( children, SIGUSR2 );
	if( res < 0 ){
		perror( "{P} sig send" );
		return -1;
	}
	
	//ожидаем сигнал SIGUSR1
	printf( "{P} Wait signal...\n" );
	sigwait( &set, &sig );
	printf( "{P} Signal got\n" );
	
	
	
	// Читаем самую короткую строку от дочернего процесса
	readString( fd_fifo, shortest, sizeof(shortest) );
	printf( "{P} Родитель получил от потомка самую короткую строку: %s\n", shortest );
	close( fd_fifo );
	
	return 0;
}


static int childrenProcess(){
	
	int res;
	int fd_fifo;
	char str1[100];
	char str2[100];
	char str3[100];
	char shortest[100];
	
	
	int sig;
	sigset_t set;
	struct sigaction act;
	
	sigemptyset( &set );
	sigaddset( &set, SIGUSR2 );
	
	//добавляем обработчик сигнала SIGUSR2
	memset( &act, 0, sizeof(act) );
	act.sa_handler = signalHandler;
	sigaction( SIGUSR2, &act, NULL );
	
	
	printf( "{C} Children is here \\ё/\n" );
	printf( "{C} Pid = %d\n",getpid() );
	printf( "{C} Wait signal...\n" );
	
	//ожидаем сигнал SIGUSR2
	sigwait( &set, &sig );
	
	printf( "{C} Signal got, go to work.\n" );
	
	//Открываем fifo для чтения и записи
	fd_fifo = open( FIFO_NAME, O_RDWR );
	if( fd_fifo < 0 ) {
		perror( "{C} Fifo open" );
		return -1;
	}
	
	
	// Чтение строк от родителя
	res = readString(fd_fifo, str1, sizeof(str1));
	if( res < 0 ){
		perror( "{C} str1 read" );
		return -1;
	}
	
	res = readString( fd_fifo, str2, sizeof(str2) );
	if( res < 0 ){
		perror( "{C} str2 read" );
		return -1;
	}
	
	res = readString( fd_fifo, str3, sizeof(str3) );
	if( res < 0 ){
		perror( "{C} str3 read" );
		return -1;
	}
	
	printf( "Дочерний процесс получил три строки от родителя:\n" );
	printf( "%s,\n%s,\n%s\n", str1, str2, str3 );
	
	
	// Находим самую короткую строку
	strcpy( shortest, str1 );
	if( strlen(str2) < strlen(shortest) ) strcpy(shortest, str2);
	if( strlen(str3) < strlen(shortest) ) strcpy(shortest, str3);
	
	printf("Дочерний процесс вычислил самую короткую строку: %s\n", shortest);
	
	
	
	// Записываем самую короткую строку в FIFO
	write( fd_fifo, shortest, strlen(shortest) + 1 );
	printf( "Дочерний процесс отправил родителю самую короткую строку: %s\n", shortest );
	close( fd_fifo );
	
	//посылаем сигнал родительскому процессу
	kill( getppid(), SIGUSR1 );
	
	printf( "{C} Bye!\n" );
	
	return 0;
}



int main( int argc, char* argv[], char* envp[] ){
	
	int res;
	pid_t pid;
	
	
	/* Если файл с таким именем существует, удалим его */
	unlink( FIFO_NAME );
	
	/* Обнуляем маску прав */
	umask( 0 );
	
	/* Создаем FIFO */
	res = mkfifo( FIFO_NAME, 0666 );
	if( res ){
		perror( "perror" );
		return 1;
	}
	
	
	pid = fork();
	
	//on fork error
	if( pid < 0 ){
		perror( "fork" );
		return 1;
	}
	
	//children
	if( !pid ){
		res = childrenProcess();
		printf( "[MAIN] Child process done. Result code is %i.\n", res );
		return res;
	}
	
	//else parent
	res = mainProcess( pid );
	printf( "[MAIN] Main process done. Result code is %i.\n", res );
	
	
	return res;
}
$ ./main 
{P} Parent is here \ё/
{P} Pid = 6745
{C} Children is here \ё/
{C} Pid = 6746
{C} Wait signal...
{P} Введите первую строку: 111
{P} Введите вторую строку: 22222
{P} Введите третью строку: 3333
{P} Wait signal...
{C} Signal got, go to work.
Дочерний процесс получил три строки от родителя:
111,
22222,
3333
Дочерний процесс вычислил самую короткую строку: 111
Дочерний процесс отправил родителю самую короткую строку: 111
{C} Bye!
[MAIN] Child process done. Result code is 0.
{P} Signal got
{P} Родитель получил от потомка самую короткую строку: 111
[MAIN] Main process done. Result code is 0.


u5er ★★
()
Ответ на: комментарий от vel

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

От настроек. Ну а от ОС дефолтные настройки, да.

Блокируемая неблокируемая тут ни при чём - оно влияет только на возможность вернуть EWOULDBLOCK. И сигналы тоже ни при чём - они делают EINTR и 0 байт. Если хоть 1 байт записался - write возвращает сколько записалось, запись всего буфера он ждёт только для реальных файлов на диске.

firkax ★★★★★
()
Ответ на: комментарий от pathfinder

Мне кажется, это всё теория. write на диск будет писать в буферный кеш, есть подозрение, что ситуация с нехваткой места обнаружится только при закрытии файла, а не во время write. Особенно со всякими CoW ФС, где нехватка места это вообще нетривиальное понятие. С прерыванием - тоже есть подозрение, что это может сработать, но только при огромном массиве записываемых данных, а если записывать немного, то оно будет или 0 или всё.

PS с тем, что надо write в цикле вызывать, я не спорю, если что, просто любопытно, если кто с этим на практике сталкивался, когда это происходит.

vbr ★★★★
()
Последнее исправление: vbr (всего исправлений: 1)