2015-02-14 2 views
1

В настоящее время я пытаюсь написать программу на C, которая будет считываться из двух именованных каналов и печатать любые данные в stdout по мере ее появления.C помощью select() для чтения из двух именованных каналов (FIFO)

например: Если я открываю два терминала и «./execute pipe1 pipe2» в одном из терминалов (с трубкой 1 и трубой 2 являются действительными именованными каналами), а затем введите «echo» Data здесь. "> Pipe1", затем имя трубы (здесь это pipe1), размер и данные должны печатать на stdout. Здесь это будет выглядеть как «pipe1 [25]:« Данные здесь ».

Я знаю, что мне нужно открыть трубы с помощью флагов O_RDONLY и O_NONBLOCK. Я просмотрел множество примеров (довольно много на этом форуме) людей, использующих select(), и я до сих пор не понимаю, что делают разные параметры, передаваемые двумя select(). Если кто-нибудь может дать здесь руководство, это было бы очень полезно. Ниже приведен небольшой объем кода, который я сделал до сих пор.

int pipeRouter(char[] fifo1, char[] fifo2){ 
    fileDescriptor1 = open(fifo1, O_RDONLY, O_NONBLOCK); 
    fileDescriptor2 = open(fifo2, O_RDONLY, O_NONBLOCK); 

    if(fileDescriptor1 < 0){ 
     printf("%s does not exist", fifo1); 
    } 
    if(fileDescriptor2 < 0){ 
     printf("%s does not exist", fifo2); 
    } 
} 
+0

Вы прочитали страницу руководства? –

+0

Да, он отображает подпись как int select (int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval * timeout); с nfds, являющимся наиважнейшим файловым дескриптором плюс 1. Не уверен, как определить, какой дескриптор файла поставить здесь. (Ив собранный файловый дескриптор - это то, что возвращает вызов low level open()), а readfds будут независимыми наборами файловых дескрипторов, которые просматриваются чтобы увидеть, становятся ли персонажи доступными. Итак, какую трубу поставить как аргументы nfds и как включить оба моих канала в качестве аргумента readfds? – GregH

+0

И страница имеет пример http: //linux.die.net/man/2/select –

ответ

8

select позволяет ждать ввода/вывода события вместо трать своё процессорное время на read.

Таким образом, в вашем примере, основной цикл может выглядеть следующим образом:

for (;;) 
{ 
    int res; 
    char buf[256]; 

    res = read(fileDescriptor1, buf, sizeof(buf)); 
    if (res > 0) 
    { 
     printf("Read %d bytes from channel1\n", res); 
    } 
    res = read(fileDescriptor2, buf, sizeof(buf)); 
    if (res > 0) 
    { 
     printf("Read %d bytes from channel2\n", res); 
    } 
} 

Если добавить код и запустить его, то можно заметить, что:

  • Программа на самом деле делает то, что вы хочу - он читается из обеих труб.
  • Загрузка процессора составляет 100% для одного ядра, то есть программа отбрасывает процессор даже тогда, когда нет данных для чтения.

Чтобы решить проблему, введены API-интерфейсы select и poll. Для select нам нужно знать дескрипторы (мы делаем) и максимум из них.

Итак, давайте изменим код немного:

for (;;) 
{ 
    fd_set fds; 
    int maxfd; 

    FD_ZERO(&fds); // Clear FD set for select 
    FD_SET(fileDescriptor1, &fds); 
    FD_SET(fileDescriptor2, &fds); 

    maxfd = fileDescriptor1 > fileDescriptor2 ? fileDescriptor1 : fileDescriptor2; 

    select(maxfd + 1, &fds, NULL, NULL, NULL); 
    // The minimum information for select: we are asking only about 
    // read operations, ignoring write and error ones; and not 
    // defining any time restrictions on wait. 


    // do reads as in previous example here 
} 

При запуске усовершенствованного кода, процессор не будет потрачено впустую, как много, но вы заметите, что read операция не выполняется, даже если там нет данные для конкретной трубы, но есть и для другого.

Чтобы проверить, что труба на самом деле данные, используйте FD_ISSET после select вызова:

if (FD_ISSET(fileDescriptor1, &fds)) 
{ 
    // We can read from fileDescriptor1 
} 
if (FD_ISSET(fileDescriptor2, &fds)) 
{ 
    // We can read from fileDescriptor2 
} 

Итак, после того, как соединение указанных выше, код будет выглядеть следующим образом:

for (;;) 
{ 
    fd_set fds; 
    int maxfd; 
    int res; 
    char buf[256]; 

    FD_ZERO(&fds); // Clear FD set for select 
    FD_SET(fileDescriptor1, &fds); 
    FD_SET(fileDescriptor2, &fds); 

    maxfd = fileDescriptor1 > fileDescriptor2 ? fileDescriptor1 : fileDescriptor2; 

    select(maxfd + 1, &fds, NULL, NULL, NULL); 


    if (FD_ISSET(fileDescriptor1, &fds)) 
    { 
    // We can read from fileDescriptor1 
    res = read(fileDescriptor1, buf, sizeof(buf)); 
    if (res > 0) 
    { 
     printf("Read %d bytes from channel1\n", res); 
    } 
    } 
    if (FD_ISSET(fileDescriptor2, &fds)) 
    { 
    // We can read from fileDescriptor2 
    res = read(fileDescriptor2, buf, sizeof(buf)); 
    if (res > 0) 
    { 
     printf("Read %d bytes from channel2\n", res); 
    } 
    } 
} 

Итак, добавьте обработки ошибок, и вы будете установлены.

+0

Вы не тратите процессорные циклы на чтение - он блокирует (обычно). Select позволяет узнать, какие данные есть. –

+0

@ EdHeal. Трубы открываются в неблокирующем режиме. почему они блокируют? –

+0

такой тип строки: 'printf ("Чтение% d байтов из канала1 \ n");' скорее всего, будет выглядеть следующим образом: 'printf («Чтение% d байтов из канала1 \ n», res); – user3629249