다음 코드는 spawn()
에 특정 프로세스를 포킹 할 때 read(fds[0]...)
에서 spawn()
으로 블록화하는 경우가 있습니다.pipe2 대신 파이프를 사용할 때 포크 후 파이프에서 블록 읽기
#include <fcntl.h>
#include <unistd.h>
#include <atomic>
#include <mutex>
#include <thread>
#include <vector>
void spawn()
{
static std::mutex m;
static std::atomic<int> displayNumber{30000};
std::string display{":" + std::to_string(displayNumber++)};
const char* const args[] = {"NullXServer", display.c_str(), nullptr};
int fds[2];
m.lock();
pipe(fds);
int oldFlags = fcntl(fds[0], F_GETFD);
fcntl(fds[0], F_SETFD, oldFlags | FD_CLOEXEC);
oldFlags = fcntl(fds[1], F_GETFD);
fcntl(fds[1], F_SETFD, oldFlags | FD_CLOEXEC);
m.unlock();
if (vfork() == 0) {
execvp("NullXServer", const_cast<char**>(args));
_exit(0);
}
close(fds[1]);
int i;
read(fds[0], &i, sizeof(int));
close(fds[0]);
}
int main()
{
std::vector<std::thread> threads;
for (int i = 0; i < 100; ++i) {
threads.emplace_back(spawn);
}
for (auto& t : threads) {
t.join();
}
return 0;
}
참고 : 여기에 파이프를 만드는 것은 쓸데없는 일입니다. 교착 상태를 보여주기위한 것입니다. read(fds[0], ...)
은 spawn()
에 차단해서는 안됩니다. read
이 호출되면 파이프의 모든 쓰기 엔드가 닫히므로 read
이 즉시 반환되어야합니다. 부모 프로세스에서 파이프의 write-end가 명시 적으로 닫히고 자식 프로세스의 write-end가 파일 설명 자에 FD_CLOEXEC
플래그가 설정되어있어 암시 적으로 닫히면 execvp
이 성공하자마자 파일 설명자가 닫힙니다 이 경우 항상 그렇습니다).
여기서 문제는 read()
이 한 번 블로킹된다는 것입니다. 코드의 두 개는 적어도 결과 FD_CLOEXEC
의 파이프 파일 디스크립터를위한 극히 미세하게 설정되고한다하더라도
pipe2(fds, O_CLOEXEC);
수정 블로킹 판독이 :
m.lock();
pipe(fds);
int oldFlags = fcntl(fds[0], F_GETFD);
fcntl(fds[0], F_SETFD, oldFlags | FD_CLOEXEC);
oldFlags = fcntl(fds[1], F_GETFD);
fcntl(fds[1], F_SETFD, oldFlags | FD_CLOEXEC);
m.unlock();
:
모두 교체.
불행히도 우리가 배포하는 모든 플랫폼에서 pipe2
을 사용할 수 없습니다.
누구나 이 위의 코드에서 pipe
접근 방식을 사용하여 차단되는 이유에 대해 설명해 줄 수 있습니까?
좀 더 관찰하십시오 vfork()
블록을 충당하기 위해 뮤텍스 잠금을 확장
- 읽기 차단을 해결합니다.
- 하나의 시스템 호출이 실패하지 않습니다.
vfork()
대신fork()
을 사용하면 동일한 동작을 나타냅니다.- 스폰 된 프로세스가 중요합니다. 이 경우 'null'X 서버 프로세스가 특정 디스플레이에 생성됩니다. 예를 들어 여기서 'ls'를 포킹하는 것이 차단되지 않거나 블록이 발생할 확률이 상당히 낮습니다. 확실하지 않습니다.
- 리눅스 2.6.18에서 4.12.8까지 재현 가능합니다. 그래서 이것은 내가 생각하기에 일종의 리눅스 커널 문제가 아닙니다.
- GCC 4.8.2 및 GCC 7.2.0을 모두 사용하여 재현 가능합니다.
물론 감사합니다. 지금 부끄러워 한 느낌 :) 물론 뮤텍스는 파일 설명자 플래그를 설정하는 동안 단 하나의 스레드 만 실제로 실행될 것이라고 보장하지 않습니다. 이는 어떤 이유로 든 가지고 있었던 인상입니다. –
@TonvandenHeuvel 당황 할 필요가 없습니다. 동시 코드는 제대로 생각하기에는 너무 자주 피타입니다.) – Ctx