2014-03-04 4 views
1

exec() 후에 소켓에 ​​걸려있는 자식 프로세스에 문제가 있습니다. 이 프로세스는 1) UDP 패킷을 읽고 2) 다른 프로세스를 중지/시작합니다. 이 프로세스는 보내는 다른 UDP 패킷을 통해 다른 프로세스를 모니터링합니다.파일 설명자가 exec에서 닫히지 않았습니다.

이것은 Windows, Linux 및 AIX에서 실행됩니다. 필자는 AIX에서만 문제를 경험 한 적이 없으며 Linux에서만 문제가 발생했습니다. (Windows 코드는 상당히 다르므로 여기에 대해서는 자세히 설명하지 않겠습니다.) fcntl()을 통해 생성 한 직후 반환 된 설명자에 FD_CLOEXEC 플래그를 설정합니다. 이것은

가 유지 관리를 위해 모니터링 프로세스를 다시 시작해야 할 수있는 옵션 (RHEL4/5의 커널 옵션이 없습니다.)하지 그래서 창조에 O_CLOEXEC를 사용하여, 레드햇 EL 4-6에서 실행해야합니다 내가 그것을 다시 시작하려고 할 때, 때때로 자식 프로세스 중 하나가 여전히 소켓에 바인딩되어있어 모니터링 프로세스가 그렇게하지 못하는 경우가 있습니다. 일반적으로 이것은 (사용자가 재시작 실패를보고 적절한 조치를 취하기 때문에) 문제가되지 않지만 (SPOF를 피하기 위해) 다른 메커니즘을 통해 모니터 자체가 모니터되고 모니터링 프로세스의 자동 재시작이 하위 프로세스 중 하나가 소켓에 고정되어 있으면 실패합니다. 이것은 하류에서 일어나는 더 나쁜 것들로 이어질 수 있습니다. ]

fork()와 exec() 호출 사이에 코드를 추가하여 자식 프로세스에서 명시 적으로 소켓을 닫고 (관련 종료) 관련된 fork() 및 read) pthread_mutex를 통해 포크가 발생했을 때 소켓에서 읽지 않도록한다.

소켓은

s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) 

과 다른 옵션이 생성됩니다. 생성 직후 fcntl을 호출하여 FD_CLOEXEC을 설정합니다. 프로세스는 여전히이 시점에서 단일 스레드이므로 플래그가 설정되기 전에는 이론적 인 경쟁 조건이 없습니다.

바인딩은 다음에 수행되지만 여전히 단일 스레드입니다. getaddrinfo에 의해 반환 된 "localhost"와 일치하는 첫 번째 IPV4 주소에 바인딩합니다 (아마도 불필요하지만 기본 유틸리티 함수를 사용하여 바인딩 호출을 단순화합니다.)

포크 후 하위 프로세스의 닫기 논리 있는) 때문에 FD_CLOEXEC의 필요합니다입니다 : 그래서

char retryClose = 1; 
int eno = 0; 
int retries = 20; 

if (shutdown(s, SHUT_RDWR)) { 
    /* Failed to shutdown. Wait and try again */ 
    my_sleep(3000); /* sleep using select(0,NULL,NULL,NULL, timeval) */ 
    shutdown(socketno, SHUT_RDWR); 
    /* not much else can be done... */ 
} 
while (retryClose && (close(s) == -1)) 
{ 
    /* save error number */ 
    eno = errno; 
    /* check specific error */ 
    switch (eno) { 
     case (EIO) : 
     /* terminate loop if retries have expired; otherwise sleep for a while and try again */ 
      if (--retries <= 0) { 
       retryClose = 0; 
      } 
      else { 
       my_sleep(50); 
      } 
      case (EINTR) : 
       break; 
      case (EBADF) : 
     default: 
      retryClose = FALSE; 
      break; 
    } /* switch (eno) */ 
} 

, 나는 FD_CLOEXEC 플래그를 설정하고, 명시 적으로 전 간부() 호출에 FD를 폐쇄하고있다.

나는 무엇이 있습니까? 자식 프로세스 이 실제로이 소켓에 걸리지 않도록하기 위해 할 수있는 일이 있습니까?

+0

소켓에 남아있는 하위 프로세스가 맞습니까? 프리 포크 코드, 특히'bind()'를 보아도 좋지만,이 동작을 보여주는 완전한 컴파일 가능한 예제가 더 바람직합니다.또한 재시작과 재시작 전에'lsof -i udp' 또는'ss -apeu'를 사용하여 소켓 상태를 검사 할 수 있습니다. – thuovila

+0

@thouvila netstat -anp는/proc//fd/inode가있는 포트에 대해/proc/net/udp 아이 노드를 상호 참조하는 것처럼 자식 프로세스임을 확인합니다 (netstat가 수행 할 수도 있음). 자식 프로세스를 죽이면 문제가 해결됩니다. –

+0

's'와'socketno'는 같은 소켓입니까? 'fcntl()'호출이 성공했는지 확인합니까? _explicitly_ FD_CLOEXEC 플래그를 추가합니까? 코드는 어떻게 생겼는가? 여기에 갈 길이 많지 않습니다. 나는 그런 문제를 겪지 않았으므로, 당신의 설명에서 온전히 해결책을 추측 할 수는 없다. 기본 유틸리티 함수 등을 언급하기 때문에 응용 프로그램 코드가 복잡 할 수 있습니다. 아마도 일부 유틸리티 함수는 이식성이없는 것을 수행합니다. 이 동작을 나타내는 코드 조각을 만들어서 보여 주어야합니다. – thuovila

답변

0

이 문제를 일으키는 fork/exec가 아닙니다.

문제없이 모든 자식 프로세스를 시작한 후 서버 프로세스를 여러 번 다시 시작할 수 있지만 서버가 종료 될 때 자식 프로세스 중 하나가 실제로 서버 소켓을 점유합니다.

클라이언트에서 connect()/send()를 사용하지 않고 sendto()로 전환하면 문제가 해결 된 것으로 보입니다.

관련 문제