fork() 썼을때 소켓 discriptor를 어떻게 구분할까요?
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <netinet/in.h> #define SERVER_PORT 5125 int main() { struct sockaddr_in serverAddr; int listenSocket, connectSocket, status, count=0; socklen_t serverAddrSize = (socklen_t)sizeof(struct sockaddr_in); pid_t pid; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(SERVER_PORT); serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); bzero( &(serverAddr.sin_zero), 8 ); listenSocket = socket(AF_INET, SOCK_STREAM, 0); bind( listenSocket, (struct sockaddr *)&serverAddr, (socklen_t)sizeof(struct sockaddr_in) ); listen(listenSocket, 5); while(1) { connectSocket = accept( listenSocket, (struct sockaddr *)&serverAddr, &serverAddrSize ); count = count + 1; pid = fork(); // 부모 if( pid > 0 ) { printf("부모 %d\n", connectSocket); # close(connectSocket); } // 자식 else if( pid == 0 ) { printf("자식 %d %d\n", count, connectSocket); return 0; } } return 0; }
위의 코드는 다중연결을 위한 서버를 만들기 위해 fork()를 사용하는 경우, 자식 프로세스에서 소켓 디스크립터를 어떻게 구분할까..에 관해 궁금증을 가지고 간단히 짜본 것입니다.
첫번째로, # 부분을 주석처리 하고 서버를 실행한 후, 동시에 2개의 클라이언트가 접속을 하게 되면, 다음과 같은 결과가 나오게 됩니다.
자식 1 4
부모 4
자식 2 5
부모 5
즉, 당연한 결과라고 생각을 하지만 2개의 자식 프로세스에선 각각 다른 소켓 디스크립터 번호를 가지고 있다는 걸 알 수 있습니다.
헌데, # 부분을 주석처리 안 하고 서버를 실행한 후, 동시에 2개의 클라이언트가 접속을 하게 되면, 다음과 같은 결과가 나오게 됩니다.
자식 1 4
부모 4
자식 2 4
부모 4
즉, 2개의 자식 프로세스에선 같은 소켓 디스크립터 번호를 가지고 있다는 걸 알 수 있습니다. 그런데, 이와 같이 자식 프로세스가 가지고 있는 소켓 디스크립터 번호가 같음에도 불구하고, 각각 자식 프로세스는 각각에 접속된 클라이언와만 통신을 하더군요. 분명 read() 혹은 write() 할 때, 첫번 째 인자로 소켓 드스크립트 번호를 넘겨주는 걸로 알고 있는데요. 이 번호가 같으면 같은 소켓이라고 이해하고 있었습니다만, 위의 결과를 보니 아닌거 같습니다. 제가 잘 못 이해하고 있었던거 같네요.
어떻게 소켓 디스크립터 번호는 동일한데, 어떻게 소켓을 구분하는 건지요? 이에 대해서 속 시원하게 설명해 주시면 감사하겠습니다.
같은 소켓(파일) 디스크립트 번호가 같은 소켓을 가리키는 것은 같은 프로
같은 소켓(파일) 디스크립트 번호가 같은 소켓을 가리키는 것은 같은 프로세스 내부에 한해서 입니다.
그러므로 부모 프로세스가 accept에 의해 반환된 fd를 닫고 나서 다시 accept 해서 할당받은 fd는 방금 close 한 fd와는 관계가 없지요 (사실 같은 listenfd 에 의해 나온 녀석이므로 아예 관계가 없는 것은 아니겠지요) 따라서 자식 1과 자식 2가 가진 fd는 결국 다른것입니다.
(0, 1, 2 번은 fork에 의해 복사(dup) 가 이루어진 것이므로 같은 대상을 가리키는 것이지요)
또다른 예로 0, 1, 2 번 fd를 close 할 경우 socket() 에 의해 0이 반환 될 수 있는데, 이것 역시 0번 (stdout) 과는 관계가 없는 fd이지요.
언제나 삽질 - http://tisphie.net/typo/
프로그래밍 언어 개발 - http://langdev.net
프로세스와 소켓
fork()를 하게되면 프로세스가 생성되고 스택과 힙 영역이 복사가 됩니다. 각 프로세스 마다 서로 상관없이 쓸 수 있다는 얘기죠
하지만 소켓을 생성한 후 fork()를 하게 되면 각 프로세스의 소켓은 하나의 소켓이 됩니다. 즉 참조카운터가 1증가된 것이지요
때문에 부모프로세스에서 소켓이 필요없다면 fork() 후에 당연히 소켓을 닫아야 할 것입니다. 그렇게 되면 소켓이 완전 닫히는 것이 아니라 참조 카운터가 1 감소됨에따라 결국 참조카운터는 2-1=1이 되는것입니다.
/***************************************************
* 가장 심플한 것이 가장 아름다운 것이다.
***************************************************/
per process file descriptor 테이블이랑 kernel
per process file descriptor 테이블이랑 kernel file descriptor 테이블의 차이를 찾아보면 됩니다.
screen + vim + ctags 좋아요~
질문이 있습니다.
안녕하세요.
질문이 있는데요.
제가 짠 프로그램의 순서가 다음과 같습니다.
1) fork를 해서 부모가 자식이 생겼습니다.
2) 공유메모리공간을 설정해서 자식과 부모가 서로 데이터를 주고 받을수
있게 되었습니다.
3) 부모에서 accept를해서 소켓을 획득한다음에 이것을 공유메모리로 복사
했습니다.
4) 자식이 공유메모리를 통해서 소켓을 얻은다음 사용하였습니다.
이런식으로 만들었습니다.
자식이 공유메모리를 통해서 얻은 소켓을 이용하여 쓸수 있나요?
해보니까 저장된 값은 같은데 실제로 write함수를 쓰면
잘못된 파일 디스크립터라고 메세지가 나옵니다.
이런 문제는 어떻게 해결해야 하나요?
OTL 즐!!!! (좌절 금지!!!)
recvmsg와 sendmsg를 이용하면 됩니다.자세한 내용은 스티븐
recvmsg와 sendmsg를 이용하면 됩니다.
자세한 내용은 스티븐 아저씨의 unp이나 apue에 passing descriptors로
나와 있을 겁니다.
집에나 갈까?
저 죄송한데요.
sendmsg 함수를 사용한 예제같은것을 구할수 없나요?
지금 unp나 apue 책이 없습니다.
OTL 즐!!!! (좌절 금지!!!)
제대로 작동하는지 모르겠습니다.예전에 스티븐 아저씨 책에서 배껴 온거
제대로 작동하는지 모르겠습니다.
예전에 스티븐 아저씨 책에서 배껴 온거 같은데...
크게 문제될게 없어 보이는데 한번 확인해보세요.
그리고 한번에 여러개의 디스크립터도 전송할수 있습니다.
집에나 갈까?
소켓 id
fork를 하는 순간부터 자식과 부모는 서로 다른
File Discriptor Table을 갖습니다.
이 table은 (fd : 해당 파일 link ) 의 쌍으로 되어있습니다.
(물론 또 다른 정보도 있을수 있겠지만 여기선 상관없으니...)
부모가 accept했을 경우에 부모는 이 table에
(해당 소켓 fd : 해당 소켓 link ) 를 기술합니다.
그러므로 부모는 이 fd값만 있으면 언제라도 해당 소켓에 link할
수 있습니다.
그러나 이 시점에서 자식의 table에는 이러한 내용이 없겠죠!
그러니 자식에게 단순히 fd값 만을 넘겨준다고 해서
해당 소켓에 link할 수는 없습니다.
그래서 일반적으로 fork후에 accept를 하는 것이 아니라
accept를 한 후에 fork를 합니다.
왜냐면 fork 할때 자식은 완전히 초기화된 테이블을 갖는게 아니라
부모의 복사본을 갖기때문에. fork할때 accept된 fd값이 기술된
table을 갖기 때문입니다. 그래서 해당 fd로 소켓에 link할 수 있습니다.
좀 두서없긴 했는데.. 암튼 그렇습니다 ㅋ
답변주신 모든 분들께 감사드립니다.
궁금한게 있는데요.
local_fd는 어떻게 생긴 건가요?
pipe로 만들어야 하나요?
저는 이미 shmget,shmat 로 부모와 자식간에 공유메모리를 지정하였거든요.
이걸 이용할 수는 없나요?
OTL 즐!!!! (좌절 금지!!!)
저는 그냥 유닉스 도메인 소켓을 이용했습니다.일반 소켓이나 파이프를
저는 그냥 유닉스 도메인 소켓을 이용했습니다.
일반 소켓이나 파이프를 쓰셔도 되구요...
고양이 귀엽네요 ^-^
집에나 갈까?
감사드립니다. ^^ 좋은 하루 되세요.
감사드립니다. ^^ 좋은 하루 되세요.
OTL 즐!!!! (좌절 금지!!!)
댓글 달기