Linux 시스템의 모든 것은 파일입니다. 모든 기본 레이어는 VFS(가상 파일 시스템) 메커니즘을 통해 보호됩니다. 이때 각 파일은 표시할 참조가 필요합니다. 파일 설명자는 창의 핸들과 유사합니다. 파일에 대한 대부분의 작업(예: 읽기 및 쓰기)은 이 설명자를 통해 수행됩니다. 각 파일 설명자에 대해 커널은 이를 관리하기 위해 세 가지 데이터 구조를 사용합니다.
(1) 각 프로세스는 프로세스 테이블에 레코드 항목을 가지며, 각 레코드 항목은 열린 파일 설명자 테이블을 가지며, 각 설명자는 하나의 항목을 차지합니다. 각 파일 설명자와 관련된 항목은 다음과 같습니다.
(a) 파일 설명자 플래그. (현재는 하나의 파일 설명자 플래그 FD_CLOEXEC만 정의되어 있습니다.)(b) 파일 테이블 항목에 대한 포인터입니다.
(2) 커널은 열려 있는 모든 파일에 대한 파일 테이블을 유지 관리합니다. 각 파일 테이블 항목에는 다음이 포함됩니다.
(a) 파일 상태 플래그(읽기, 쓰기, 추가-쓰기, 동기, 비차단 등).
(b) 현재 파일 변위. (즉, lseek 함수로 연산되는 값)
(c) 파일의 v-node 항목을 가리키는 포인터.
(3) 열려 있는 각 파일(또는 장치)은 v 노드 구조를 갖습니다. v 노드에는 파일 유형에 대한 포인터 정보와 파일에 대해 다양한 작업을 수행하는 함수가 포함되어 있습니다. 대부분의 파일에서 v 노드에는 파일의 i 노드(인덱스 노드)도 포함됩니다. 이 정보는 파일이 열릴 때 디스크에서 메모리로 읽혀지므로 파일에 대한 모든 정보를 빠르게 사용할 수 있습니다. 예를 들어, i-노드에는 파일 소유자, 파일 길이, 파일이 있는 장치, 디스크의 파일이 사용하는 실제 데이터 블록에 대한 포인터 등이 포함됩니다.
위 파일 시스템의 3계층 캡슐화 후, 각 계층은 위에서 아래로 서로 다른 책임을 담당하며 첫 번째 계층은 파일 식별에 사용되고 두 번째 계층은 관리에 사용됩니다. 독립적인 데이터를 처리하고 세 번째 계층 파일 시스템 메타데이터를 관리하고 파일을 직접 연결합니다. 이러한 계층적 아이디어의 한 가지 장점은 상위 계층이 하위 계층의 구조를 재사용할 수 있다는 것입니다. 동일한 파일 테이블 항목을 가리키는 파일 설명자 항목이 여러 개 있을 수 있고 동일한 V 노드를 가리키는 파일 테이블 항목이 여러 개 있을 수 있습니다.
두 개의 독립적인 프로세스가 동일한 파일을 여는 경우 파일을 여는 각 프로세스는 파일 테이블 항목을 얻지만 두 파일 테이블 항목의 V 노드 포인터는 동일한 V 노드를 가리킵니다. 파일의 현재 위치를 변경하고 다양한 열기 방법(O_RDONLY, O_WRONLY, ORDWR)을 지원합니다.
프로세스가 포크를 통해 하위 프로세스를 생성하면 상위 및 하위 프로세스의 파일 설명자가 동일한 파일 테이블 항목을 공유합니다. 이는 상위 및 하위 프로세스의 파일 설명자가 동일한 지점을 가리킨다는 의미입니다. 일반적으로 포크 후에는 필요하지 않은 fd를 닫습니다. 예를 들어 부모 프로세스와 자식 프로세스가 파이프나 소켓 쌍을 통해 통신할 때 읽거나 쓸 필요가 없는 끝을 닫는 경우가 많습니다. 현재 파일 항목을 참조하는 파일 설명자가 없는 경우에만 닫기 작업이 실제로 현재 파일 항목 데이터 구조를 파괴하는데, 이는 참조 카운팅 개념과 다소 유사합니다. 이는 네트워크 프로그래밍에서 닫기 기능과 종료 기능의 차이점이기도 합니다. 전자는 소켓 핸들을 사용하는 마지막 프로세스가 닫힐 때만 실제로 연결을 끊는 반면, 후자는 아무런 논의 없이 연결의 한쪽을 직접 연결 해제합니다. 그러나 멀티 스레드 환경에서는 아버지 스레드와 아들 스레드가 주소 공간을 공유하므로 파일 설명자가 공동으로 소유되고 복사본이 하나만 있으므로 스레드에서 필요하지 않은 FD를 닫을 수 없습니다. 이로 인해 FD가 필요한 다른 파일도 영향을 받습니다. 부모 프로세스와 자식 프로세스에서 열린 파일 디스크립터는 동일한 파일 테이블 항목을 공유하기 때문에 일부 시스템의 서버 프로그래밍에서 사전 포크 모델을 사용하는 경우(서버는 여러 자식 프로세스를 미리 파생시키고 각 자식 프로세스는 Listenfd를 수신하여 수락함) 연결) 이로 인해 서버에서 파생된 여러 하위 프로세스가 각 호출을 수락하므로 첫 번째 클라이언트 연결이 도착하면 하나의 프로세스만 연결을 얻더라도 모든 프로세스가 절전 모드로 전환됩니다. 깨어나 성능이 저하됩니다. UNP P657을 참조하십시오.
동시에 포크 후에 exec가 호출되면 모든 파일 설명자는 계속 열린 상태로 유지됩니다. 이는 exec 후에 특정 파일 설명자를 프로그램에 전달하는 데 사용할 수 있습니다. 동시에 파일 설명자 플래그 FD_CLOEXEC는 exec를 닫을 때 열려 있는 파일 설명자를 유지하는 데 사용되는 옵션입니다.
dup 또는 fcntl을 통해 파일 설명자를 명시적으로 복사할 수도 있으며, 이는 동일한 파일 테이블 항목을 가리킵니다. dup2를 통해 파일 설명자를 지정된 값으로 복사합니다.
각 프로세스에는 프로세스 간에 독립적인 파일 설명자 테이블이 있습니다. 두 프로세스 간의 파일 설명자 사이에는 직접적인 관계가 없으므로 파일 설명자는 프로세스 내에서 직접 전달될 수 있지만, 이를 통해 전달되는 경우 의미를 잃은 Unix는 sendmsg/recvmsg를 통해 특수 파일 설명자를 전달할 수 있습니다(UNP 섹션 15.7 참조). 각 프로세스의 처음 세 파일 설명자는 표준 입력, 표준 출력 및 표준 오류에 해당합니다. 그러나 프로세스가 열 수 있는 파일 디스크립터의 수에는 제한이 있습니다. 열린 파일 디스크립터가 너무 많으면 "열린 파일이 너무 많습니다"라는 문제가 발생합니다. 네트워크 서버에서 Listenfd를 통해 accept를 호출하면 EMFILE 오류가 발생하는데, 이는 파일 디스크립터가 시스템의 중요한 리소스이기 때문에 시스템이 파일 디스크립터의 기본값을 제한하기 때문입니다. 단일 프로세스는 1024이며 ulimit -n 명령을 사용하여 볼 수 있습니다. 물론 프로세스 파일 디스크립터 수를 늘릴 수도 있지만 이는 동시성이 높은 서비스를 다룰 때 서버 리소스가 제한되어 리소스 고갈이 불가피하기 때문에 영구적인 솔루션이 아닌 일시적인 솔루션입니다.
lisenfd 연결을 수신하기 위해 epoll의 수평 트리거 방법과 결합하면 Listenfd가 처리되지 않으면 많은 수의 소켓 연결이 TCP 연결 대기열에 플러딩되어 서버를 바쁜 대기 상태로 만듭니다. C++ 오픈소스 네트워크 라이브러리 muduo의 저자 Chen Shuo가 하는 일은 유휴 파일 설명자를 미리 준비하는 것입니다. EMFILE 오류가 발생하면 먼저 유휴 파일을 닫고 파일 설명자 할당량을 얻은 다음 파일을 수락합니다. 그런 다음 즉시 닫아 클라이언트 연결을 정상적으로 끊고 마지막으로 이런 일이 다시 발생할 경우를 대비해 유휴 파일을 다시 열어 "구멍"을 메웁니다.
1 //在程序开头先”占用”一个文件描述符 2 3 int idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC); 4 ………… 5 6 //然后当出现EMFILE错误的时候处理这个错误 7 8 peerlen = sizeof(peeraddr); 9 connfd = accept4(listenfd, (struct sockaddr*)&peeraddr, &peerlen, SOCK_NONBLOCK | SOCK_CLOEXEC);10 11 if (connfd == -1)12 {13 if (errno == EMFILE)14 {15 close(idlefd);16 idlefd = accept(listenfd, NULL, NULL);17 close(idlefd);18 idlefd = open("/dev/null", O_RDONLY | O_CLOEXEC);19 continue;20 }21 else22 ERR_EXIT("accept4");23 }
위 내용은 서버 프로그래밍에서의 파일 작업에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!