컴퓨터/운영체제(주로 리눅스)

[리눅스] 시스템의 한계를 구하는 방법 - sysconf, pathconf

REAKWON 2023. 7. 31. 14:24

리눅스의 전반적인 더 많은 정보와 예제를 담은 리눅스 교재를 배포했습니다. 아래의 페이지에서 리눅스 교재를 받아가세요.

https://reakwon.tistory.com/233

 

리눅스 프로그래밍 note 배포

티스토리에 리눅스에 관한 내용을 두서없이 여지껏 포스팅했었데요. 저도 제 포스팅을 찾기가 어렵기도 하고 티스토리에서 코드삽입을 하게 되면 이게 일자로 쭉 쓰여져있는 x같은 현상이 생겨

reakwon.tistory.com

 

한계

리눅스에서 프로그램을 다른 리눅스에서 실행할 수 있습니다. 혹은 다른 유닉스 계열(BSD나 솔라리스, MAC OS X 등)의 시스템에서도 동작이 될 수가 있지요. 그런데 이렇게 시스템마다 지원하는 한계라는 것이 다 제 각각이거든요. 예를 들면 어떤 시스템에서는 한 프로세스 당 열 수 있는 파일 디스크립터의 갯수가 256개를 지원하는데, 다른 시스템에서는 1024개까지 지원할 수 있는 등 시스템마다 지원할 수 있는 한계가 존재합니다. 그렇다면 어떤 시스템에서 사용자가 생성할 수 있는 자식 프로세스의 수라던가, 최대 파일 경로의 이름이라던가,  로그인이 가능한 이름의 최대 길이라던가를 알아내면 그 프로그램이 효율적으로 동작할 수 있을 것 같은데요. 과연 어떻게 알아낼 수 있을까요?

한계는 세가지로 구분할 수가 있습니다. 

  • 컴파일 시점에서 한계(헤더 파일에 명시) : 이러한 한계들은 limits.h에 명시되어 있습니다. 시스템마다 불변하는 한계를 의미하게 됩니다. 예를 들어 POSIX를 준수하는 시스템에서는 int 자료형에서 지원가능한 값은 2,147,483,647이 적어도 만족이 되어야합니다. INT_MAX 상수로 확인이 가능합니다. 여기서는 설명하지 않겠습니다. 
  • 파일이나 디렉토리와 연관되지 않은 실행 시점의 한계(sysconf 함수) : 파일과 연관이 없는 시스템에서 실제 지원할 수가 있는 한계를 확인하려면 sysconf함수를 이용해서 확인할 수 있습니다. 함수의 원형을 볼까요?
#include <unistd.h>

long sysconf(int name);

   sysconf의 인자인 name을 전달하게 되면 그에 따른 값이 나옵니다. name은 앞에 _SC_로 시작하는 매크로 상수입니다. SC는 SysConf의 약자입니다. 아래의 소개한 매크로보다 더 다양한 name이 있습니다. 여기서는 요만큼만 설명합니다.

매크로 상수 설명
_SC_ARG_MAX exec 함수의 인수 길이 최대값
_SC_CHILD_MAX uid 당 동시에 실행할 수 있는 프로세스의 최대값
_SC_HOST_NAME_MAX hostname의 최대 값, gethostname으로 구할 수 있습니다
_SC_LOGIN_NAMX_MAX 로그인 이름의 최대 길이 
_SC_OPEN_MAX 프로세스가 열 수 있는 파일의 최대 갯수
_SC_PAGE_SIZE
_SC_PAGESIZE
페이지의 크기
_SC_STREAM_MAX 프로세스가 열 수 있는 파일 스트림의 최대 갯수
_SC_TTY_NAME_MAX 터미널 디바이스 이름의 최대 길이
_SC_TZNAME_MAX 타임 존 이름의 최대 길이 
_SC_LINE_MAX 유틸리티 프로그램에서 입력 줄로 받을 수 있는 최대 길이(stdin으로든 file으로 든)
_SC_SIGQUEUE_MAX 한 프로세스에 신호 큐에 담을 수 있는 신호의 최대 갯수
_SC_SEM_VALUE_MAX 세마포어의 최대값
_SC_SEM_NSEMS_MAX 한 프로세스가 동시에 사용할 수 있는 세마포어의 최대 개수
_SC_CLK_TCK 1초 클록 틱 개수

 

  • 파일이나 디렉토리와 연관된 실행 시점의 한계(pathconf 함수 혹은 fpathconf 함수) : 파일과 관련된 한계를 알아낼 때는 pathconffpathconf 함수를 사용할 수 있습니다. 예를 들어 어떤 터미널에 대한 한계를 알고 싶다면 /dev/ 하위의 터미널 파일을 입력으로 주어 확인할 수 있습니다. 
#include <unistd.h>

long fpathconf(int fd, int name);
long pathconf(const char *path, int name);

 이 둘의 동작은 같습니다. 단지 파일을 알려주는 첫 인자를 파일 디스크립터로 전달하느냐(fd), 파일의 경로와 이름을 사용하여 전달하느냐(path)에 따른 것만 다르죠. name은 확인하려는 한계의 이름을 넣어주면 됩니다. 앞에 _PC_로 시작합니다. Path Config의 약자겠죠? 

매크로 상수 설명
_PC_LINK_MAX 파일에 최대 링크 갯수를 의미하며, 만약 파일이라면 파일에 대한 최대 링크 갯수를 가져오고, 디렉토리를 지정하면 디렉토리의 링크 최대 갯수를 가져옵니다.
_PC_MAX_CANON 터미널의 서식화된 입력줄의 최대 길이로 파일을 반드시 터미널 파일을 지정해야합니다.
_PC_MAX_INPUT 터미널의 입력줄의 최대 길이로 반드시 터미널 파일을 지정해야합니다.
_PC_NAME_MAX 지정한 디렉토리에서 파일 입력의 최대 길이로 인자를 디렉토리로 주어야합니다.
_PC_PIPE_BUF 하나의 파이프에 원자적으로 쓸 수 있는 최대 바이트 수로 pipe나 fifo 타입의 파일, 혹은 디렉토리를 주어야합니다. 디렉토리를 주었을 때는 디렉토리 안에 생성된 임의의 fifo에 대한 한계를 가져옵니다.
_PC_PATH_MAX path나 fd가 현재 작업 디렉토리일 경우 상대 경로의 최대길이

 

위 세 함수(sysconf, pathconf, fpathconf)는 실패일 경우 -1이 반환되지만 1) 실제 지원하지 않는 name이라서 실패한 경우2)확정할 수 없는 한계에 의한 실패가 있습니다. 지원하지 않는 실패의 경우 errno가 EINVAL로 설정되구요. 확정할 수 없는 한계에 의한 실패는 errno가 변하지 않습니다.

한계를 가져오는 소스 코드

//print_conf.c
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>

static void print_sysconf(char *str, int name){
        long val;
        errno = 0;
        printf("%s ", str);

        if((val = sysconf(name)) < 0){ 
                if(errno != 0){
                        if(errno == EINVAL)
                                printf(" (not supported)\n");
                        else printf("syconf error\n");
                }else
                        printf(" (no limit)\n");

        }else 
                printf(" %ld\n", val);
}

static void print_pathconf(char *str, char *path, int name){
        long val;
        printf("%s ", str);
        errno = 0;
        if((val = pathconf(path,name)) < 0){ 
                if(errno != 0){
                        if(errno == EINVAL)
                                printf(" (not supported)\n");
                        else printf("pathcon error(%s)\n", path);
                }else
                        printf(" (no limit)\n");
        }else
                printf(" %ld\n", val);

}
int main(int argc, char *argv[]){
        if(argc != 2){
                printf("Usage : %s <filename> \n", argv[0]);
                return 1;
        }


        printf("======== sysconf ==========\n");
#ifdef _SC_ARG_MAX
        print_sysconf("ARG_MAX :", _SC_ARG_MAX);
#endif
#ifdef _SC_CHILD_MAX
        print_sysconf("CHILD_MAX : ", _SC_CHILD_MAX);
#endif
#ifdef _SC_HOST_NAME_MAX
        print_sysconf("HOST_NAME_MAX : ", _SC_HOST_NAME_MAX);
#endif
#ifdef _SC_LOGIN_NAMX_MAX
        print_sysconf("LOGIN_NAMX_MAX : ", _SC_LOGIN_NAMX_MAX);
#endif
#ifdef _SC_OPEN_MAX
        print_sysconf("OPEN_MAX : ", _SC_OPEN_MAX);
#endif
#ifdef _SC_PAGESIZE
        print_sysconf("PAGESIZE : ", _SC_PAGESIZE);
#endif
#ifdef _SC_STREAM_MAX
        print_sysconf("STREAM_MAX : ", _SC_STREAM_MAX);
#endif
#ifdef _SC_TTY_NAME_MAX
        print_sysconf("TTY_NAME_MAX : ", _SC_TTY_NAME_MAX);
#endif
#ifdef _SC_TZNAME_MAX
        print_sysconf("TZNAME_MAX : ", _SC_TZNAME_MAX);
#endif
#ifdef _SC_LINE_MAX
        print_sysconf("LINE_MAX : ", _SC_LINE_MAX);
#endif
#ifdef _SC_SIGQUEUE_MAX
        print_sysconf("SIGQUEUE_MAX : ", _SC_SIGQUEUE_MAX);
#endif
#ifdef _SC_SEM_VALUE_MAX
        print_sysconf("SEM_VALUE_MAX : ", _SC_SEM_VALUE_MAX);
#endif
#ifdef _SC_SEM_NSEMS_MAX
        print_sysconf("SEM_NSEMS_MAX : ", _SC_SEM_NSEMS_MAX);
#endif
#ifdef _SC_CLK_TCK
        print_sysconf("CLK_TCK : ", _SC_CLK_TCK);
#endif

        printf("======== pathconf ==========\n");
#ifdef _PC_LINK_MAX
        print_pathconf("LINK_MAX : ", argv[1], _PC_LINK_MAX);
#endif
#ifdef _PC_MAX_CANON
        print_pathconf("MAX_CANON : ", argv[1], _PC_MAX_CANON);
#endif
#ifdef _PC_MAX_INPUT
        print_pathconf("MAX_INPUT : ", argv[1], _PC_MAX_INPUT);
#endif
#ifdef _PC_NAME_MAX
        print_pathconf("NAME_MAX : ", argv[1], _PC_NAME_MAX);
#endif
#ifdef _PC_PIPE_BUF
        print_pathconf("PIPE_BUF : ", argv[1], _PC_PIPE_BUF);
#endif
#ifdef _PC_PATH_MAX
        print_pathconf("PATH_MAX : ", argv[1], _PC_PATH_MAX);
#endif

}

 

위의 소스코드는 sysconf와 pathconf 함수를 사용해서 한계를 출력해주는 프로그램입니다. 다른 시스템에서는 정의되지 않은 name이 존재할 수 있으므로 #ifdef로 정의되어있는지 파악하여 사용합니다. 

실패할 경우에는 errno이 EINVAL이면 지원하지 않는 한계 이름입니다. 그런데 errno가 0으로 변하지 않았다면 불확정인 한계로 볼 수 있습니다. 

errno = 0;
//...
if((val = sysconf(name)) < 0){ 
    if(errno != 0){
            if(errno == EINVAL)
                    printf(" (not supported)\n");
            else printf("syconf error\n");
    }else
            printf(" (no limit)\n");

 

아래는 실행 결과입니다.

# ./a.out /etc
======== sysconf ==========
ARG_MAX :  2097152
CHILD_MAX :   15044
HOST_NAME_MAX :   64
OPEN_MAX :   1024
PAGESIZE :   4096
STREAM_MAX :   16
TTY_NAME_MAX :   32
TZNAME_MAX :   (no limit)
LINE_MAX :   2048
SIGQUEUE_MAX :   15044
SEM_VALUE_MAX :   2147483647
SEM_NSEMS_MAX :   (no limit)
CLK_TCK :   100
======== pathconf ==========
LINK_MAX :   65000
MAX_CANON :   255
MAX_INPUT :   255
NAME_MAX :   255
PIPE_BUF :   4096
PATH_MAX :   4096

 

반응형