2011-08-22 2 views
3

현재 Linux 환경에서 C를 사용하여 소켓 프로그래밍을 배우고 있습니다. 프로젝트로서 나는 기본적인 채팅 서버와 클라이언트를 작성하려고 시도하고있다.동일한 파이프에서 여러 자식 프로세스 읽기/쓰기

의도는 서버가 연결하는 각 클라이언트에 대한 프로세스를 포크하게하는 것입니다.

내가 겪고있는 문제는 한 어린이의 데이터를 읽고 연결된 모든 클라이언트에게 쓰는 것입니다.

데이터를 소켓에 도착 시키거나 파이프의 끝을 읽는 것을 기다리는 하위에서 select를 호출하여 루핑하여이 작업을 수행하려고했습니다. 소켓에 도착하면 파이프의 쓰기 끝 부분에 쓰기를 수행하여 select가 파이프의 읽기 끝을 읽을 준비가 된 것으로 반환하도록합니다.

이 파이프는 모든 자식간에 공유되므로 각 자식은 파이프의 데이터를 읽어야합니다. 이는 데이터 파이프가 동시에 나타나지 않고, 동시에 각 하위 프로세스에서 읽을 수 없으며 읽기 호출에서 데이터 블록을 "잃어 버리는"자식으로도 작동하지 않기 때문에 작동하지 않습니다.

for(; ;) 
{ 
    rset = mset; 
    if(select(maxfd+1, &rset, NULL, NULL, NULL) > 0) 
    { 
    if(FD_ISSET(clientfd, &rset)) 
    { 
     read(clientfd, buf, sizeof(buf)); 
     write(pipes[1], buf, strlen(buf)); 
    } 
    if(FD_ISSET(pipes[0], &rset)) 
    { 
     read(pipes[0], buf, sizeof(buf)); 
     write(clientfd, buf, sizeof(buf)); 
    } 
    } 
} 

나는 현재 작동하지 않습니다 단순히 사용하고있는 방법을 추정하고 있습니다 :

다음은이 작업을 수행 자식 프로세스의 코드입니다. 클라이언트로부터받은 메시지가 IPC를 통해 연결된 다른 모든 클라이언트에 쓰여질 수 있습니까?

감사

답변

2

이 (그리고에서 턴 다른 아이가 빈 파이프에서 읽으려고 "붙어"얻을 만들기) 것보다 파이프에서 더 많은 데이터를 읽는 아이의 문제를 해결하려면, 당신은 아마해야 부모 프로세스와 자식 프로세스간에 통신하는 단일 전역 파이프가 아닌 상위 프로세스와 개별 하위 프로세스 사이에 POSIX 메시지 대기열 또는 단일 파이프를 사용하십시오. 현재 서버가 자식과 통신하기 위해 파이프에 쓸 때 OS에 의한 프로세스의 스케줄링이 비 결정적이기 때문에 특정 시점에 파이프에서 읽는 자식을 정확히 제어 할 수는 없습니다 . 즉, 어떤 유형의 동기화 메커니즘이나 읽기/쓰기 장벽이 없으면 서버가 파이프에 쓰는 경우 코드에서 하나의 자식이 읽기를 건너 뛰지 않고 두 번째 자식이 두 번 읽는 것을 멈추게하는 것은 아무것도 없습니다 , 서버에서 방송 된 데이터를 얻었어야하는 다른 아이가 굶어 죽어 버렸습니다.

이 간단한 해결 방법은 부모와 개별 자식간에 공유되는 단일 개인 파이프를 다시 가질 수 있다는 것입니다. 따라서 서버에서 자식 프로세스는 클라이언트로부터 읽어 들여 그 데이터를 부모 프로세스로 다시 보낼 수 있으며 부모는 모든 자식에 대해 누적 된 파이프 설명자의 전체 목록을 사용하여 각 개별 자식 프로세스에 다시 기록 할 수 있습니다 각 클라이언트에 다시 전송되는 브로드 캐스트 메시지 어떤 자식도 다른 자식 프로세스에 의한 이중 읽기의 가능성이 없으므로 "굶어 죽은"데이터를 얻지 못합니다. 각 파이프에는 하나의 리더/라이터가 있으며 통신은 결정적입니다.

서버의 상위 프로세스에서 각 하위에 대해 여러 파이프를 처리하지 않으려는 경우 POSIX 메시지 큐 (mqueue.h에 있음)를 사용하는 글로벌 메시지 큐를 사용할 수 있습니다. 그 접근법으로, 아이가 가지고 있지 않은 메시지를 잡으면 (즉, ID 값이 포함 된 struct을 전달해야 할 필요가있을 때) 메시지를 대기열에 다시 놓고 다른 메시지를 읽으려고합니다 메시지는 직접 파이프 방식만큼 속도면에서 효율적이지는 않지만, 글로벌 파이프로 인해 발생하는 인터리빙 문제없이 현재 아이에게 지정되지 않은 메시지를 다시 쓸 수 있습니다. FIFO 메커니즘.

+0

응답 해 주셔서 감사합니다. – synic

+0

BTW, 파이프에 여러 독자가있는 것과 관련된 또 다른 문제는 파이프에 대한 읽기가 원자 적이 지 않다는 것입니다. 즉,'read' 호출이 있으면'read '에 대한 단일 호출에서 전체 메시지를 얻을 수 없습니다 '. 여러 독자와 경합이 있으면 데이터 손상 문제가 발생할 수 있습니다. 크기'PIPE_BUF '까지의'write' 연산은 원자 적입니다 만,'read'를 호출한다는 사실은 다중 바이트 또는 가변 길이 데이터를 읽는다면 단일 파이프의 다중 독자가 상당히 고통 스러울 수 있음을 의미하지 않습니다 -끈. – Jason

+0

아 좋아, 한 프로세스가 FD에서 읽기를 호출하면 데이터의 나머지 부분을 얻기 위해 다시 루프를 호출해야합니다.이 갭에서 다른 프로세스가 다른 프로세스가 읽기를 "훔쳐"다른 것을 잠글 수있는 가능성이 있습니까? – synic

2

파이프에 기록 된 데이터의 각 바이트는 정확히 한 번 읽혀집니다. 은 파이프의 읽기 끝이 열린 상태에서 모든 프로세스에 중복되어이 아닙니다.

데이터를 여러 대상 프로세스에 복제하려면 데이터를 명시 적으로 복제해야합니다. 예를 들어, 모든 "슬레이브"프로세스와의 파이프가있는 "마스터"프로세스를 하나 가질 수 있습니다. 슬레이브가 다른 슬레이브에게 메시지를 브로드 캐스팅하고자 할 때 마스터 프로세스로 보내면 슬레이브로 돌아가서 다른 슬레이브로가는 각 파이프에 한 번 씁니다.

관련 문제