Изучая возможность улучшения производительности Recoll с помощью vfork() вместо fork(), я столкнулся с проблемой fork(), которую я не могу объяснить.Резьба, выполняемая между fork и exec блокирует другую нить, прочитанную
Recoll неоднократно выполняет внешние команды для перевода файлов, так что это то, что делает программа-образец: он запускает потоки, которые повторно выполняют «ls» и считывают вывод.
Следующая проблема не является «реальной» в том смысле, что фактическая программа не будет делать то, что вызывает проблему. Я просто наткнулся на это, посмотрев, какие потоки были остановлены или нет между fork()/vfork() и exec().
Когда у меня есть один из потоков, занятых циклом между fork() и exec(), другой поток никогда не завершает чтение данных: последний read(), который должен указывать eof, заблокирован навсегда или до тех пор, пока другой конец цикла потока (в этот момент все возобновляется нормально, что можно увидеть, заменив бесконечный цикл на тот, который завершается). В то время как read() заблокирован, команда «ls» вышла (ps показывает < несуществующий >, зомби).
Существует случайный аспект проблемы, но образец программы «преуспевает» большую часть времени. Я тестировал с ядрами Linux 3.2.0 (Debian), 3.13.0 (Ubuntu) и 3.19 (Ubuntu). Работает на виртуальной машине, но вам нужно как минимум 2 procs, я не мог заставить ее работать с одним процессором.
Здесь следует образец программы, я не вижу, что я делаю неправильно.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <memory.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <iostream>
using namespace std;
struct thread_arg {
int tnum;
int loopcount;
const char *cmd;
};
void* task(void *rarg)
{
struct thread_arg *arg = (struct thread_arg *)rarg;
const char *cmd = arg->cmd;
for (int i = 0; i < arg->loopcount; i++) {
pid_t pid;
int pipefd[2];
if (pipe(pipefd)) {
perror("pipe");
exit(1);
}
pid = fork();
if (pid) {
cerr << "Thread " << arg->tnum << " parent " << endl;
if (pid < 0) {
perror("fork");
exit(1);
}
} else {
// Child code. Either exec ls or loop (thread 1)
if (arg->tnum == 1) {
cerr << "Thread " << arg->tnum << " looping" <<endl;
for (;;);
//for (int cc = 0; cc < 1000 * 1000 * 1000; cc++);
} else {
cerr << "Thread " << arg->tnum << " child" <<endl;
}
close(pipefd[0]);
if (pipefd[1] != 1) {
dup2(pipefd[1], 1);
close(pipefd[1]);
}
cerr << "Thread " << arg->tnum << " child calling exec" <<
endl;
execlp(cmd, cmd, NULL);
perror("execlp");
_exit(255);
}
// Parent closes write side of pipe
close(pipefd[1]);
int ntot = 0, nread;
char buf[1000];
while ((nread = read(pipefd[0], buf, 1000)) > 0) {
ntot += nread;
cerr << "Thread " << arg->tnum << " nread " << nread << endl;
}
cerr << "Total " << ntot << endl;
close(pipefd[0]);
int status;
cerr << "Thread " << arg->tnum << " waiting for process " << pid
<< endl;
if (waitpid(pid, &status, 0) != -1) {
if (status) {
cerr << "Child exited with status " << status << endl;
}
} else {
perror("waitpid");
}
}
return 0;
}
int main(int, char **)
{
int loopcount = 5;
const char *cmd = "ls";
cerr << "cmd [" << cmd << "]" << " loopcount " << loopcount << endl;
const int nthreads = 2;
pthread_t threads[nthreads];
for (int i = 0; i < nthreads; i++) {
struct thread_arg *arg = new struct thread_arg;
arg->tnum = i;
arg->loopcount = loopcount;
arg->cmd = cmd;
int err;
if ((err = pthread_create(&threads[i], 0, task, arg))) {
cerr << "pthread_create failed, err " << err << endl;
exit(1);
}
}
void *status;
for (int i = 0; i < nthreads; i++) {
pthread_join(threads[i], &status);
if (status) {
cerr << "pthread_join: " << status << endl;
exit(1);
}
}
}
Мне понравилось решать вашу головоломку :) – Celada