리눅스의 전반적인 더 많은 정보와 예제를 담은 리눅스 교재를 배포했습니다. 아래의 페이지에서 리눅스 교재를 받아가세요.
https://reakwon.tistory.com/233
한계
리눅스에서 프로그램을 다른 리눅스에서 실행할 수 있습니다. 혹은 다른 유닉스 계열(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 함수) : 파일과 관련된 한계를 알아낼 때는 pathconf와 fpathconf 함수를 사용할 수 있습니다. 예를 들어 어떤 터미널에 대한 한계를 알고 싶다면 /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
'컴퓨터 > 운영체제(주로 리눅스)' 카테고리의 다른 글
[리눅스] 코드로 이해하는 저장된 사용자 ID(Saved UID)가 있는 이유 (0) | 2023.08.16 |
---|---|
[리눅스] 환경 변수 개념과 환경 변수를 다루는 방법 - 환경 변수는 어쩌면 맛있는게 아닐까? (1) | 2023.08.03 |
[리눅스] 프로세스 통신 FIFO(이름있는 pipe) 개념과 예제 (1) | 2023.07.13 |
[리눅스] 코드로 알아보는 uid(real uid, effective uid, saved uid) 관계 (0) | 2023.06.30 |
[리눅스] 메모리 대응 입출력 기본 설명 및 사용법 예제(mmap, memcpy, munmap, msync) (2) | 2022.12.06 |