Книга: Основы программирования в Linux

Родительский и дочерний процессы

Следующий логический шаг в нашем изучении вызова pipe — разрешить дочернему процессу быть другой программой, отличной от своего родителя, а не просто другим процессом, выполняющим ту же самую программу. Сделать это можно с помощью вызова exec. Единственная сложность заключается в том, что новому процессу, созданному exec, нужно знать, какой файловый дескриптор применять для доступа. В предыдущем примере этой проблемы не возникло, потому что дочерний процесс обращался к своей копии данных file_pipes. После вызова exec возникает другая ситуация, поскольку старый процесс заменен новым дочерним процессом. Эту проблему можно обойти, если передать файловый дескриптор (который, в конце концов, просто число) как параметр программе, вновь созданной с помощью вызова exec.

Для того чтобы посмотреть, как это работает, вам понадобятся две программы (упражнение 13.7). Первая — поставщик данных. Она создает канал и затем вызывает дочерний процесс, потребитель данных.

Упражнение 13.7. Каналы и exec

1. Для получения первой программы исправьте pipe2.c, превратив ее в pipe3.c. Измененные строки затенены.

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main() {
 int data_processed;
 int file_pipes[2];
 const char somedata[] = "123";
 char buffer[BUFSIZ + 1];
 pid_t fork_result;
 memset(buffer, '', sizeof(buffer));
 if (pipe(file_pipes) == 0) {
  fork_result = fork();
  if (fork_result == (pid_t)-1) {
   fprintf(stderr, "Fork failure");
   exit(EXIT_FAILURE);
  }
  if (fork_result == 0) {
   sprintf(buffer, "%d", file_pipes[0]);
   (void)execl("pipe4", "pipe4", buffer, (char*)0);
   exit(EXIT_FAILURE);
  } else {
   data_processed = write(file_pipes[1], some_data, strlen(some_data));
   printf ("%d - wrote %d bytesn", getpid(), data_processed);
  }
 }
 exit(EXIT_SUCCESS);
}

2. Программа-потребитель pipe4.c, читающая данные, гораздо проще:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
 int data_processed;
 char buffer[BUFSIZ + 1];
 int file_descriptor;
 memset(buffer, '', sizeof(buffer));
 sscanf(argv[1], "%d", &file_descriptor);
 data_processed = read(file_descriptor, buffer, BUFSIZ);
 printf("%d — read %d bytes: %sn", getpid(), data_processed,
  buffer);
 exit(EXIT_SUCCESS);
}

Выполнив pipe3 и помня о том, что она вызывает программу pipe4, вы получите вывод, аналогичный приведенному далее:

$ ./pipe3
22460 - wrote 3 bytes
22461 - read 3 bytes: 123

Как это работает

Программа pipe3 начинается как предыдущий пример, используя вызов pipe для создания канала и затем вызов fork для создания нового процесса. Далее она применяет функцию sprintf для сохранения в буфере номера файлового дескриптора чтения из канала, который формирует аргумент программы pipe4.

Вызов execl применен для вызова программы pipe4. В нем использованы следующие аргументы:

? вызванная программа;

argv[0], принимающий имя программы;

argv[1], содержащий номер файлового дескриптора, из которого программа должна читать;

(char *)0, завершающий список параметров.

Программа pipe4 извлекает номер файлового дескриптора из строки аргументов и затем читает из него данные.

Оглавление книги


Генерация: 1.870. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз