Книга: Основы программирования в 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 извлекает номер файлового дескриптора из строки аргументов и затем читает из него данные.
- Процессы-зомби
- 3.4.3. Процессы-зомби
- Рабочие процессы
- При завершении работы Windows сообщает, что некоторые процессы не отвечают, и компьютер не выключается. Как завершать та...
- 3.4. Процессы
- Родительский контроль
- 10.8.3. Родительский надзор: три различные стратегии
- 10.8.3.3. Строгий родительский контроль
- Процессы
- Программы и процессы
- Системные процессы
- Прикладные процессы