티스토리 뷰

반응형

일반적인 FD 닫기를  구현 할 때 아래와 같이 구현 한다.

하지만 개발 할 때 우리가 지속적으로 생각해 봐야 할 부분에 대해서 작성해 보았습니다. 

void CloseSockFD()
{
	struct rlimit rlim;
	// 현재 프로세스에 대해 열 수 있는 파일 디스크립터의 최대 개수를 조회합니다.
	if (getrlimit(RLIMIT_NOFILE, &rlim) == -1)
		return; // 조회에 실패하면 함수를 종료합니다.

	for (rlim_t idxFD = 0; idxFD < rlim.rlim_cur; ++idxFD)
	{
		struct stat statbuf;
		// 각 파일 디스크립터에 대한 정보를 얻기 위해 fstat 함수를 호출합니다.
		if (fstat(idxFD, &statbuf) == -1)
		{
			if (errno == EBADF) // 유효하지 않은 파일 디스크립터인 경우 계속 진행합니다.
				continue;
			else
				return; // 그 외의 오류가 발생한 경우 함수를 종료합니다.
		}

		// fstat 호출이 성공하면, 해당 파일 디스크립터가 소켓인지 확인합니다.
		if (S_ISSOCK(statbuf.st_mode))
			close(idxFD); // 소켓이면 해당 파일 디스크립터를 닫습니다.
	}
}

1. 불필요한 검사 최소화

현재 구현에서는 유효하지 않은 파일 디스크립터에 대한 fstat 호출을 시도한 후 오류를 확인합니다. 대신, 열린 파일 디스크립터의 집합을 관리하고, 이 집합에 대해서만 fstat를 호출하는 방식으로 불필요한 시스템 호출을 줄일 수 있습니다.

2. /proc/self/fd/ 활용

리눅스 시스템에서는 /proc/self/fd/ 디렉토리를 통해 현재 프로세스의 모든 열린 파일 디스크립터를 확인할 수 있습니다. 이 방법을 사용하면 시스템이 관리하는 실제 열린 파일 디스크립터 목록을 직접 얻을 수 있어, 불필요한 반복과 검사를 피할 수 있습니다.

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>

void CloseSockFD()
{
    DIR *dp;
    struct dirent *dirp;

    if ((dp = opendir("/proc/self/fd")) == NULL)
        return;

    while ((dirp = readdir(dp)) != NULL) {
        int fd = atoi(dirp->d_name);
        if (fd > STDERR_FILENO) { // 표준 입출력을 제외
            struct stat statbuf;
            if (fstat(fd, &statbuf) != -1 && S_ISSOCK(statbuf.st_mode)) {
                close(fd);
            }
        }
    }

    closedir(dp);
}

 

3. 동시성 고려

만약 이 함수가 멀티스레드 환경에서 호출될 수 있다면, 동시성에 대한 고려가 필요합니다. 여러 스레드가 동시에 파일 디스크립터를 닫을 때 생길 수 있는 문제를 방지하기 위해, 해당 작업을 수행하는 동안 다른 스레드에서의 파일 디스크립터 변경을 제한할 수 있는 동기화 메커니즘이 필요합니다.

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>

pthread_mutex_t fd_lock = PTHREAD_MUTEX_INITIALIZER;

void CloseSockFD()
{
    DIR *dp;
    struct dirent *dirp;

    // /proc/self/fd를 열어 현재 프로세스의 열린 파일 디스크립터를 얻습니다.
    if ((dp = opendir("/proc/self/fd")) == NULL)
        return;

    while ((dirp = readdir(dp)) != NULL) {
        int fd = atoi(dirp->d_name);
        
        // 표준 입출력 파일 디스크립터를 건너뜁니다.
        if (fd <= STDERR_FILENO)
            continue;

        // 파일 디스크립터 닫기 전에 락을 걸어 동시성 문제를 방지합니다.
        pthread_mutex_lock(&fd_lock);

        struct stat statbuf;
        if (fstat(fd, &statbuf) != -1 && S_ISSOCK(statbuf.st_mode)) {
            close(fd);
        }

        // 작업이 끝나면 락을 해제합니다.
        pthread_mutex_unlock(&fd_lock);
    }

    closedir(dp);
}

 

4. 성능 테스트

최적화를 진행한 후에는 반드시 성능 테스트를 진행해야 합니다. 최적화가 실제로 성능 개선을 가져왔는지, 그리고 부작용이 없는지 확인해야 합니다. 특히, /proc/self/fd/를 사용하는 방식은 파일 시스템 접근을 필요로 하므로, 해당 접근이 성능에 미치는 영향을 평가해야 합니다.

5. 리소스 누수 방지

파일 디스크립터를 닫는 과정에서 발생할 수 있는 리소스 누수를 방지하기 위해, 닫힌 파일 디스크립터에 대한 참조를 제거하고, 필요한 경우에는 추가적인 정리 작업을 수행해야 합니다. 예를 들어, 소켓을 닫은 후에는 관련 자료구조를 정리하는 등의 작업이 필요할 수 있습니다.

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함