2015-03-06 2 views
2

Я создал (а) простой класс Process, который имитирует std::thread. Предполагается, что он работает только с Linux.Waitpid и вопросы синхронизации

struct Process 
{ 
    Process(/*...*/) { /* fork + join, initialize m_pid */ } 
    ~Process() { assert(!joinable()); } 

    bool joinable() const { return m_pid != 0; } 

    void join() 
    { 
     assert (joinable()); 
     int status; 
     waitpid(m_pid, &status, 0); 
     m_pid = 0; 
    } 
private: 
    std::atomic<pid_t> m_pid; 
}; 

У меня есть дополнительное требование: мне нужно иметь возможность остановить процесс из другого потока.

Например:

Process p(/* ... */); 

Thread 1: 
    p.join(); 

Thread 2: 
    p.interrupt(); 

Как я могу осуществить поточно-Process:interrupt? Моя первая мысль что-то вроде этого:

void Process::interrupt() 
{ 
    if (m_pid) 
     kill(m_pid, SIGTERM); 
} 

К сожалению, я не уверен, что это работает, потому что если interrupt называется между waitpid и m_pid = 0 в join() тогда я убивающий несуществующий процесс, или хуже, совершенно не связаны процесс, который имеет тот же самый pid, что и старый.

Перестановка заявления m_pid = 0 и waitpid в join() бы сделать перерыв совершенно бесполезно, так как вы можете ожидать, что первая нить будет проводить большую часть своего времени в ожидании завершения дочернего процесса.

Так что мне нужен waitpid, который ждет завершения процесса дочернего процесса, но сохраняет дочерний процесс в состоянии зомби, так что ни один процесс с одним и тем же pid не может появиться в то же время.

Есть ли такая вещь или любое другое решение?

+0

Вы можете просто использовать блокировку – Dani

+0

@ Дани err ... как? Если я заблокирую весь раздел 'waitpid' +' m_pid = 0', тогда прерывание будет ждать завершения процесса. Это не то, что я хочу. В идеале мне нужно что-то вроде 'pthread_cond_wait', где вы можете передать блокировку функции, и она будет атомарно заблокирована при возврате' waitpid'. – sbabbi

+0

Вы можете прервать waitpid, убить процесс и возобновить waitpid – Dani

ответ

2

Вы правы, блокировка waitpid(m_pid, ...); m_pid = 0 в одном потоке управления и kill(m_pid, ...) в другом будет гонка. Добавление мьютекса не поможет по указанной причине - официант должен подождать и обновить некоторую глобальную информацию, но убийцу должно быть позволено (косвенно) прерывать ожидание.

(я собираюсь игнорировать часть о имитируя std::thread, не, например, поддерживают прерывание.)

Вместо этого, вы можете рассчитывать на унаследованной pipe, и убедитесь, что дочерний процесс наследует конец записи, оставляя родительский элемент с конечным концом. Этот конец чтения укажет EOF, когда ребенок закончит работу, но внешний ресурс (запись процесса зомби) останется до явного получения. Этот метод не является надежным, так как ребенок может развиваться или выполняться или иным образом закрываться или проходить по его контенту fd неожиданно.

В стороне, есть более сложные решения, которые вы могли бы спроектировать. Например, вы можете создать отдельный секретный поток до waitpid и kill дочернего процесса, а ваш метод interrupt мог бы прервать этот поток с сигналом в реальном времени. Вы также можете взять на себя обработку SIGCHLD для всего родительского процесса. Тем не менее, это тяжеловес и склонность к ошибкам.

+0

Я пойду с трубой. Мне нужно в любом случае прочитать «stdout» дочернего процесса – sbabbi

Смежные вопросы