POSIX (Portable Operating System Interface)

POSIX는 유닉스 계열 운영체제의 공통 인터페이스 계약을 정리한 표준입니다. 이 문서는 POSIX의 역사, Issue 8 문서 구조, 시스템 인터페이스(System Interfaces)와 셸·유틸리티(Shell and Utilities)의 범위, Linux와 GNU 확장의 차이, 커널 개발자가 실제로 부딪히는 이식성 문제를 한 문서에서 연결해 설명합니다.

최신 기준: 현재 공개 최신 기준은 POSIX.1-2024 / IEEE Std 1003.1-2024이며, Issue 8 다운로드 안내에서 개인용 HTML 묶음을 받을 수 있습니다. 이 문서는 최신 공개판을 기준으로 설명하고, Linux 고유 확장과 표준 범위를 분리해서 다룹니다.
전제 조건: 시스템 콜(System Call), 프로세스(Process), VFS 문서를 먼저 읽으세요. POSIX는 커널 내부 구현 자체가 아니라 사용자 공간(User Space)과 커널이 맞춰야 하는 계약을 설명하므로, 실제 호출 경로와 파일 시맨틱을 먼저 알고 읽어야 표준 문구가 어떻게 코드로 내려오는지 이해하기 쉽습니다.
일상 비유: 이 개념은 전기 콘센트 규격서와 비슷합니다. 규격서가 벽 안의 배선 구현을 직접 강제하지는 않지만 플러그 모양, 전압 범위, 안전 조건을 정해 서로 다른 제품이 함께 동작하게 만들듯이, POSIX도 커널 내부 구현보다 인터페이스 계약을 정의합니다.

핵심 요약

  • POSIX — 운영체제 인터페이스와 셸·유틸리티 동작 계약을 정의하는 표준 계열입니다.
  • Issue 8 — 현재 공개 최신판이며 IEEE Std 1003.1-2024와 The Open Group Base Specifications Issue 8이 기술적으로 같은 문서입니다.
  • 표준 범위 — 시스템 콜만이 아니라 라이브러리 함수, 헤더, 셸 문법, 유틸리티까지 포함합니다.
  • Linux 확장 — epoll, io_uring, pidfd, signalfd, eventfd처럼 유용하지만 POSIX 밖에 있는 Linux 전용 인터페이스가 많습니다.
  • 이식성 — 커널 개발자도 사용자 공간 도구, 테스트 코드, 빌드 스크립트를 쓸 때 POSIX 경계를 이해해야 불필요한 GNU/Linux 종속성을 줄일 수 있습니다.

단계별 이해

  1. 표준의 위치 파악
    POSIX는 커널 소스가 아니라 사용자 프로그램과 운영체제 사이의 계약 문서라는 점부터 잡습니다.
  2. 문서 구조 익히기
    XBD, XSH, XCU, XRAT 네 덩어리로 나뉜다는 사실을 알면 필요한 조항을 훨씬 빨리 찾을 수 있습니다.
  3. 표준과 구현 분리
    open() 같은 이름이 표준 인터페이스인지, glibc 래퍼인지, Linux 시스템 콜인지 구분합니다.
  4. Feature Test Macro 설정
    #define _POSIX_C_SOURCE 200809L을 소스 맨 위에 두면 GNU 확장을 차단하고 POSIX 범위만 노출합니다. _GNU_SOURCE를 쓰면 반대로 모든 GNU/Linux 확장이 열립니다. 둘을 혼용하면 이식성 경계가 흐려집니다.
  5. 실전 적용
    poll() 대 epoll, sigaction() 대 signalfd처럼 POSIX 경로와 Linux 전용 경로를 코드에서 명시적으로 분리하면 버그 재현 범위를 좁히기 쉬워집니다.

개요

POSIX는 Portable Operating System Interface의 약자이며, 서로 다른 유닉스 계열 시스템이 공통된 인터페이스를 제공하도록 정리한 표준 집합입니다. 이름만 보면 시스템 콜 표준처럼 느껴지지만, 실제 범위는 그보다 훨씬 넓습니다. 함수(Function), 헤더(Header), 시그널(Signal), 파일 시맨틱(File Semantics), 스레드(Thread), 셸 문법, 표준 유틸리티, 빌드 도구의 최소 동작까지 함께 다룹니다.

리눅스 커널 개발자에게 POSIX가 중요한 이유는 두 가지입니다. 첫째, 커널은 사용자 공간이 기대하는 파일·프로세스·시그널 시맨틱을 구현해야 합니다. 둘째, 커널 개발 과정에서 쓰는 테스트 프로그램, 재현 코드, 셸 스크립트, 이식성 있는 도구는 POSIX 계약 위에서 움직입니다. 따라서 POSIX를 모르면 커널 내부는 알아도 사용자 공간 기대치와 어긋난 설계를 하기 쉽습니다.

질문POSIX가 답하는 범위POSIX가 직접 답하지 않는 범위
파일 열기open(), read(), write(), 권한 비트, 에러 코드, 파일 시간 갱신 규약VFS 내부 자료구조, 페이지 캐시(Page Cache) 알고리즘, ext4 저널 구현
프로세스 생성fork(), exec*, wait*, 프로세스 속성 상속 규약task_struct 레이아웃, 스케줄러(Scheduler) 큐 구조
시그널 처리sigaction(), 시그널 마스크, 기본 동작, 비동기 인터럽트(Interrupt) 계약Linux 시그널 전달 구현 상세, 태스크(Task) 웨이크업 내부 경로
셸 스크립트POSIX sh 문법, 유틸리티 옵션과 종료 코드Bash 배열, [[ ]], GNU Coreutils 확장 옵션
이벤트 처리poll(), select(), pselect()epoll, io_uring, eventfd, pidfd
중요한 구분: POSIX 호환 인터페이스를 많이 구현했다고 해서 모든 Linux 시스템이 자동으로 POSIX 인증 시스템이 되는 것은 아닙니다. 표준 문서, 구현체, 배포판 정책, 인증 절차는 서로 다른 층위입니다. 이 문서에서는 "Linux가 POSIX 시맨틱을 참고하고 많이 구현한다"는 수준과 "정식 인증 제품"을 엄격히 구분합니다.
POSIX가 규정하는 계약 층과 Linux 계열이 구현하는 층위 POSIX 계약 (규정) Linux 계열 구현 표준 문서 본문 XBD · XSH · XCU · XRAT 참조 매뉴얼 man-pages · glibc/musl 문서 · 커널 문서 C 언어 API 계약 함수 시그니처 · 헤더 · 에러 코드 · 매크로 libc / 런타임 glibc, musl — Feature Test Macro 처리 셸 · 유틸리티 계약 POSIX sh 문법 · make · awk · sed · find 셸 · 유틸리티 구현 dash · bash · coreutils · GNU make 시스템 인터페이스 계약의 경계 open · read · fork · wait · 시그널 시맨틱 시스템 콜 경계 + Linux 전용 syscall + epoll · io_uring · pidfd · eventfd POSIX: 무엇을 보장해야 하는가 (계약) → Linux: 어떻게 구현할 것인가 (구현) 화살표는 POSIX 계약(왼쪽)과 Linux 구현(오른쪽)을 층별로 대응시키며, 맨 아래가 커널 경계와 만나는 지점입니다.
POSIX 계약 층과 Linux 구현 층위 — 개념 개요. 응용 프로그램과 구현체 사이의 계약을 규정하고, Linux 커널 내부 자료구조나 알고리즘 자체는 표준화하지 않습니다.

역사와 표준 계보

POSIX는 여러 유닉스 계열 시스템 사이의 인터페이스 차이를 줄이기 위해 등장했습니다. 오늘날 개발자가 보는 온라인 문서는 The Open Group이 게시하지만, 표준 자체는 IEEE와 The Open Group이 함께 관리하며, 실제 기술 작업은 Austin Group이 주도합니다.

시기핵심 사건커널 개발자 관점의 의미
1980년대 후반유닉스 벤더별 API 차이가 커짐이식성 있는 도구와 응용 프로그램 작성 비용이 커짐
1990년초기 POSIX.1 계열 정립프로세스, 파일, 시그널 등 공통 OS 인터페이스 기준이 생김
2001년 이후POSIX, Single UNIX Specification, ISO 문서군이 점점 통합표준 참조점이 여러 갈래에서 하나로 수렴
2017년IEEE Std 1003.1-2017많은 문서와 교재가 아직 이 판본을 기본으로 인용
2024년IEEE Std 1003.1-2024 / The Open Group Base Specifications Issue 8현재 최신 공개판. 최신 링크와 설명은 이 판본 기준으로 잡는 것이 안전

Issue 8 rationale는 POSIX.1-2024가 Issue 8과 기술적으로 동일하며, Austin Group이 IEEE, The Open Group, ISO/IEC 협업 구조 안에서 작업했다는 점을 명확히 밝힙니다. 실무에서는 "POSIX", "Issue 8", "SUSv5"가 문맥에 따라 함께 언급될 수 있지만, 완전히 같은 범위로 느슨하게 써버리면 문서 참조가 흐려집니다.

POSIX 표준 계보와 현재 참조점 벤더별 API 차이에서 출발해, Austin Group이 하나의 표준 문서로 통합했습니다. 유닉스 벤더별 API System V · BSD · 상용 유닉스 POSIX 계열 표준화 공통 인터페이스 정리 Austin Group IEEE + The Open Group + ISO/IEC Issue 8 (현재) IEEE 1003.1-2024 · SUS v5 현재 공개 참조점: Issue 8 = POSIX.1-2024 = SUS Version 5 IEEE Std 1003.1-2024 · The Open Group Base Specs · Single UNIX Specification v5 Linux 개발자가 실무에서 참조하는 위치 온라인 표준 문서 (Issue 8 웹) + man-pages + glibc/musl 문서 커널 문서를 함께 읽어 규정과 실제 동작의 차이를 교차 확인
POSIX 표준 계보와 현재 참조점 — 개념 개요. 실무에서는 표준 문서와 Linux 구현 문서를 함께 봐야 정확한 판단이 됩니다.

Issue 8 문서 구조와 읽는 법

POSIX 문서는 처음 보면 방대해 보이지만, 실제로는 네 덩어리만 익히면 됩니다. 대부분의 커널 개발자는 함수 하나를 찾다가 문서 전체에 압도되는데, 먼저 "어느 권(volume)에 있는가"를 분리하면 탐색 비용이 크게 줄어듭니다.

권의미무엇을 찾을 때 보는가대표 링크
XBDBase Definitions용어 정의, 공통 개념, 헤더, 파일 접근 규칙, 에러 번호, 옵션 이름Base Definitions
XSHSystem Interfacesopen(), fork(), sigaction(), pthread_*, mq_* 같은 함수 규약System Interfaces
XCUShell and UtilitiesPOSIX sh 문법, make, find, awk, sed, test 같은 유틸리티Shell and Utilities
XRATRationale왜 이런 규칙이 생겼는지, 이전 판본과의 호환성, 모호한 부분의 배경Rationale

커널 개발자에게 특히 중요한 읽기 순서는 다음과 같습니다. 함수 의미가 애매하면 먼저 XSH를 봅니다. 에러 코드나 타입 정의가 헷갈리면 XBD로 올라갑니다. 왜 Linux 구현과 다르게 보이는지 이해가 안 되면 XRAT를 확인합니다. 셸 스크립트 이식성 문제라면 XCU를 봅니다.

문서 읽기 팁: POSIX는 규정 문서이므로 shall, should, may 같은 표현 차이가 중요합니다. shall은 필수, should는 권고, may는 허용을 뜻합니다. 커널 코드 리뷰에서 "표준상 보장"을 말할 때 이 구분을 흐리면 쉽게 과장된 주장이 됩니다.
표현의미실무 해석
shall반드시 보장해야 함호환성 주장에 근거로 써도 되는 강한 규정
should권장좋은 구현 관행이지만 미준수만으로 비호환 단정은 어려움
may허용구현마다 다를 수 있으므로 가정하면 위험
implementation-defined구현이 선택하되 문서화해야 함glibc, musl, 배포판 문서를 같이 봐야 함
unspecified표준이 결과를 정하지 않음동작에 의존하면 이식성 버그가 생기기 쉬움

Feature Test Macro — POSIX 범위 제어

POSIX 함수를 사용하기 전에 반드시 이해해야 하는 것이 기능 테스트 매크로(Feature Test Macro)입니다. 이 매크로는 libc 헤더가 어느 수준의 인터페이스를 노출할지 컴파일러에게 알려줍니다. 매크로를 지정하지 않으면 glibc 기본값은 _DEFAULT_SOURCE로 GNU 확장 상당 부분이 노출되어, 이식성 문제가 있는 코드를 컴파일 오류 없이 작성해버리기 쉽습니다.

매크로노출 범위권장 상황
_POSIX_C_SOURCE 200809LPOSIX.1-2008 (Issue 7) 인터페이스만 노출이식성 있는 도구·테스트 코드 작성 시 기본값
_POSIX_C_SOURCE 202405LPOSIX.1-2024 (Issue 8) 인터페이스만 노출최신 표준 함수(ppoll, getentropy 등) 사용 시
_XOPEN_SOURCE 700POSIX + X/Open (XSI) 확장 포함XSI 옵션 인터페이스가 필요할 때
_GNU_SOURCEPOSIX + GNU 확장 + Linux 전용 전부 노출Linux 전용 코드. 이식성 버그가 숨기 쉬움
_DEFAULT_SOURCEglibc 기본값 (GNU + POSIX 혼합)매크로 미지정 시 glibc의 기본 동작
구성 1 — _POSIX_C_SOURCE 200809L · 이식성 우선 (POSIX 범위만) 소스 코드 #define _POSIX_C_SOURCE 200809L libc 헤더 — 심볼별 노출 여부 POSIX 심볼 노출 ✓ XSI/GNU 확장 숨김 ✗ Linux 전용 (epoll 등) 숨김 ✗ POSIX만 노출 epoll·signalfd·eventfd 등은 선언되지 않음 구성 2 — _GNU_SOURCE · Linux 특화 (모든 심볼 노출) 소스 코드 #define _GNU_SOURCE 모든 확장 포함 libc 헤더 — 심볼별 노출 여부 POSIX 심볼 노출 ✓ XSI/GNU 확장 노출 ✓ Linux 전용 (epoll 등) 노출 ✓ 전부 노출 GNU/Linux 전용 API까지 선언되어 보임 주의: _GNU_SOURCE와 _POSIX_C_SOURCE를 함께 정의하면 glibc는 _GNU_SOURCE를 우선 혼용 시 POSIX 제한 효과가 사라지므로, 의도에 맞는 매크로 하나만 정의
Feature Test Macro가 libc 헤더의 심볼 노출 범위를 제어하는 원리
/* 이식성을 최우선으로 할 때 — POSIX.1-2008 범위만 사용 */
#define _POSIX_C_SOURCE 200809L
#include <unistd.h>
#include <fcntl.h>

/* epoll_create1()은 POSIX 범위 밖 → 이 설정에서는 선언이 보이지 않음 */

/* Linux 전용 API까지 허용할 때 — GNU/Linux 특화 코드에서만 사용 */
#define _GNU_SOURCE
#include <sys/epoll.h>
/* epoll_create1(), signalfd(), eventfd() 등 모두 보임 */
혼용 금지: _GNU_SOURCE와 _POSIX_C_SOURCE를 같은 파일에 함께 정의하면 glibc는 _GNU_SOURCE를 우선하므로 POSIX 제한 효과가 사라집니다. 의도를 명확히 하려면 하나만 사용하십시오.

핵심 시스템 인터페이스

커널 개발자 입장에서 POSIX의 중심은 결국 시스템 인터페이스입니다. 다만 "시스템 인터페이스"가 곧 "시스템 콜"은 아닙니다. 어떤 함수는 libc 래퍼가 직접 시스템 콜에 연결되고, 어떤 함수는 사용자 공간에서 조합되며, 어떤 함수는 스레드 라이브러리와 커널 기능이 함께 구현합니다.

주제대표 POSIX 인터페이스Linux 구현에서 주의할 점관련 문서
프로세스fork(), execve(), waitpid()fork()는 POSIX 계약이고 실제 구현은 clone 계열을 활용프로세스, 시스템 콜
파일 I/Oopen(), read(), write(), fsync()표준 파일 시맨틱과 ext4/XFS 실제 보장 범위를 구분해야 함VFS, ext4, XFS
시그널sigaction(), sigprocmask(), sigsuspend()비동기 안전(async-signal-safe) 제약은 설계 실수의 흔한 원인시그널 처리
IPCpipe(), mq_open(), shm_open(), sem_open()System V IPC와 POSIX IPC는 자원 모델과 API가 다름IPC, shmem / tmpfs
스레드pthread_create(), pthread_mutex_lock()사용자 API는 POSIX지만 실제 구현은 NPTL과 futex에 크게 의존커널 스레드(Kernel Thread), Futex
시간clock_gettime(), nanosleep(), timer_create()vDSO 경량 경로와 hrtimer 기반 커널 구현을 함께 봐야 함ktime / Clock, 타이머(Timer)
네트워크소켓(Socket) API, poll(), select()epoll과 io_uring은 Linux 확장이므로 별도 분리 표기 필요네트워크 스택(Network Stack), io_uring 네트워킹

예를 들어 pthread_mutex_lock()은 POSIX 인터페이스이지만 Linux 커널 안에 동일 이름의 시스템 콜이 있는 것은 아닙니다. 반대로 epoll_wait()는 Linux에서 실무적으로 매우 중요하지만 POSIX 표준 함수는 아닙니다. 따라서 커널 문서나 코드 예제에서 "POSIX API"와 "Linux syscall"을 같은 말처럼 쓰면 설명이 무너집니다.

#define _POSIX_C_SOURCE 200809L
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

static volatile sig_atomic_t stop;

static void on_sigint(int sig)
{
    (void)sig;
    stop = 1;
}

int main(void)
{
    struct sigaction sa;
    int fd;
    char buf[64];
    ssize_t nr;

    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = on_sigint;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGINT, &sa, 0);

    fd = open("posix-demo.txt", O_RDONLY);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    nr = read(fd, buf, sizeof(buf) - 1);
    if (nr >= 0) {
        buf[nr] = '\0';
        write(STDOUT_FILENO, buf, nr);
    }

    close(fd);
    return stop ? 130 : 0;
}
코드 설명
  • 1행_POSIX_C_SOURCE는 어느 판본 수준의 POSIX 인터페이스를 노출할지 libc에 알리는 기능 매크로(Macro)입니다. GNU 확장 노출 범위를 줄일 때 유용합니다.
  • 11-16행시그널 핸들러(Handler)는 최소한의 상태 변경만 수행합니다. 비동기 안전 제약을 어기지 않기 위한 전형적 패턴입니다.
  • 23-25행sigaction()은 POSIX 표준 시그널 인터페이스의 핵심이며, Linux에서도 전통적인 signal()보다 이 경로가 기준입니다.
  • 27-35행open(), read(), write(), close()는 가장 대표적인 POSIX 파일 인터페이스입니다. 커널 내부에서는 VFS와 파일시스템 구현체가 이 계약을 만족시킵니다.
실무 포인트: 재현용 테스트 프로그램은 먼저 POSIX 범위 안에서 최소 사례를 작성하고, 문제가 Linux 확장에만 나타나는지 별도로 분리하세요. 이렇게 하면 버그 리포트가 "표준 시맨틱 위반"인지 "Linux 전용 기능 상호작용"인지 더 빨리 가려집니다.

파일 디스크립터(File Descriptor)와 I/O 모델

POSIX I/O의 핵심은 파일 디스크립터(File Descriptor, FD)입니다. FD는 프로세스가 열린 파일·파이프·소켓·파일시스템(Filesystem) 디바이스를 참조하는 음수가 아닌 정수입니다. POSIX는 FD의 의미, 상속 규칙, 에러 시맨틱을 규정하지만 커널 내부 자료구조는 강제하지 않습니다.

POSIX 규정 영역 커널 구현 영역 프로세스 FD 테이블 fd 0 · stdin fd 1 · stdout fd 2 · stderr fd 3 · open() 열린 파일 기술자 (Open File Description) 파일 오프셋 접근 · 상태 플래그 참조 카운트 dup()/fork()로 공유 inode 파일 메타데이터 퍼미션 · 크기 · 블록 커널 내부 구현 POSIX 미규정
FD 테이블 → 열린 파일 기술자 → inode 계층. FD와 열린 파일 기술자는 POSIX가 정의하고, inode와 그 내부는 커널 구현 영역입니다.

POSIX FD vs. stdio 스트림

C 표준 라이브러리의 FILE * 스트림(stdio)과 POSIX FD는 다른 수준의 인터페이스입니다. stdio는 내부적으로 POSIX FD를 사용하지만 버퍼(Buffer) 계층을 추가합니다.

특성POSIX FDstdio (FILE *)
인터페이스open / read / write / closefopen / fread / fwrite / fclose
버퍼링(Buffering)없음 (커널 페이지 캐시만 적용)사용자 공간(User Space) 내부 버퍼 있음
표준POSIX XSHISO C (POSIX도 포함)
FD 번호 접근직접 사용fileno(fp)로 꺼낼 수 있음
시그널 인터럽트 처리EINTR 재시도 필요내부적으로 처리하는 경우도 있음
주 사용처커널 인터페이스, 소켓, 저수준 I/O텍스트 처리, 이식성 높은 파일 작업

POSIX 에러 모델 — errno

POSIX 함수가 실패하면 일반적으로 -1을 반환하고 errno에 에러 코드를 설정합니다. errno는 스레드 로컬 변수이므로 멀티스레드 환경에서도 각 스레드가 독립적으로 확인합니다.

#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(void)
{
    int fd = open("/nonexistent", O_RDONLY);
    if (fd == -1) {
        /* errno는 open() 직후에 즉시 읽어야 합니다.
         * 다른 함수 호출 사이에 errno가 덮어써질 수 있습니다. */
        int saved_errno = errno;
        fprintf(stderr, "open 실패: %s (errno=%d)\n",
                strerror(saved_errno), saved_errno);
        return 1;
    }
    close(fd);
    return 0;
}
코드 설명
  • open() == -1POSIX 관행: 실패 시 -1 반환. fd >= 0이어야 유효한 FD입니다.
  • saved_errnoerrno는 다음 함수 호출에서 덮어써지므로 즉시 복사해 두는 것이 올바른 패턴입니다.
  • strerror()errno 값을 사람이 읽을 수 있는 문자열로 변환합니다. 멀티스레드 환경에서는 strerror_r()을 사용하십시오.
async-signal-safe 제약: 시그널 핸들러 안에서는 비동기 안전(async-signal-safe) 함수만 호출할 수 있습니다. printf(), malloc(), strerror()는 재진입(Re-entrant)이 불가능해 시그널 핸들러에서 호출하면 교착 상태(Deadlock)나 데이터 손상이 생길 수 있습니다. 시그널 핸들러에서 안전한 대표적인 함수는 write(), _exit(), sig_atomic_t 전역 플래그 설정입니다. 전체 목록은 POSIX XSH Signal Actions 참조.

셸·유틸리티·빌드 도구

POSIX는 C API만 다루지 않습니다. 셸 명령어 언어와 표준 유틸리티도 큰 비중을 차지합니다. 커널 개발 환경에서 이 부분이 중요한 이유는 빌드 스크립트, 테스트 스크립트, initramfs 초기화 스크립트, 패키징 도구가 예상보다 자주 /bin/sh 이식성 문제를 일으키기 때문입니다.

영역POSIX가 정의하는 것Linux 현장에서 흔한 확장관련 문서
셸POSIX sh 문법, 리디렉션, 파이프, 확장 최소 집합Bash 배열, [[ ]], 프로세스 치환, localBash 셸 스크립팅, BusyBox
빌드make의 기본 규칙과 동작GNU Make 함수, 패턴 규칙 확장, 병렬 기능GNU Make, 빌드 시스템(Build System)
텍스트 처리sed, awk, grep, find, xargsGNU 전용 옵션, BusyBox 축약 구현, BSD 차이정규표현식, diff & patch
# POSIX sh 호환 예시: /bin/sh에서 동작하는 형태
set -eu

file="${1:-Makefile}"
if [ ! -r "$file" ]; then
    printf '읽을 수 없는 파일: %s\n' "$file" >&2
    exit 1
fi

case "$file" in
    *.c|*.h) printf 'C 계열 파일: %s\n' "$file" ;;
    *)       printf '기타 파일: %s\n' "$file" ;;
esac

count=0
for line in $(wc -l < "$file"); do
    count="$line"
done
printf '줄 수: %s\n' "$count"

위 스크립트는 Bash에서는 너무 평범해 보이지만, 핵심은 Bash 전용 문법에 기대지 않는다는 점입니다. 커널 트리의 많은 보조 스크립트는 이 수준의 보수성을 전제로 작성됩니다. /bin/sh가 bash가 아닐 수 있다는 사실을 기준으로 보면, POSIX 셸 문법이 왜 여전히 중요한지 바로 이해됩니다.

Linux와 POSIX의 차이

Linux는 POSIX를 많이 구현하지만 POSIX 그 자체는 아닙니다. 또한 실무에서는 POSIX 코어, XSI 옵션, GNU 확장, Linux 전용 API가 겹쳐 쓰입니다. 이 층을 분리하지 않으면 문서 설명도, 코드 리뷰도, 버그 재현도 모두 흐려집니다.

분류예시의미
POSIX 코어open(), read(), fork(), sigaction(), POSIX sh이식성 기준선으로 삼기 좋은 공통 인터페이스
XSI / 옵션 영역ucontext 계열 일부, X/Open 계열 기능, 특정 유틸리티 옵션구현 옵션 또는 추가 프로파일이라 모든 시스템에서 당연하다고 보면 위험
GNU 확장getline(), strdupa(), GNU Make 함수, Coreutils 장옵션GNU 환경에서는 흔하지만 다른 libc나 BSD 계열에서 깨질 수 있음
Linux 전용epoll, eventfd, signalfd, io_uring, pidfd, openat2강력하지만 문서와 코드에서 명시적으로 Linux 전용이라고 드러내야 함
POSIX 코어, GNU 확장, Linux 전용 API의 관계 안쪽일수록 이식성이 높고 널리 호환되며, 바깥일수록 플랫폼 전용입니다. Linux 전용 안쪽일수록 이식성 높음 epoll · eventfd · signalfd · timerfd · pidfd · io_uring · openat2 · memfd_create · clone3 GNU 확장 getline · strdupa · Bash 확장 · GNU Make 함수 · Coreutils 장옵션 POSIX 코어 open · read · write · fork sigaction · pthread · poll clock_gettime · POSIX sh
POSIX 코어 · GNU 확장 · Linux 전용 API 관계 — 개념 개요. 표준 범위와 확장 범위를 명확히 구분해야 기능 설명과 이식성 기대치가 함께 맞습니다.

대표적인 예로 ioctl()은 POSIX에서 구체적 시맨틱을 강하게 규정한 인터페이스가 아닙니다. 반면 open()과 read()는 규정이 비교적 강합니다. 또 epoll은 Linux에서 사실상 표준처럼 쓰이지만 POSIX poll()의 대체 확장으로 이해해야 합니다. 문서에서 이런 층을 명시하지 않으면 독자가 "휴대 가능한 계약"과 "Linux 최적화 인터페이스"를 섞어 받아들이게 됩니다.

이식성 실전 예제

이식성은 거창한 목표가 아니라, 문제가 생겼을 때 범위를 좁히기 쉽게 만드는 작업입니다. 커널 개발자 입장에서는 테스트 코드와 보조 스크립트만 POSIX 중심으로 작성해도 재현 환경이 훨씬 넓어집니다.

상황휴대성 높은 선택Linux 전용 선택권장 전략
이벤트 대기poll(), pselect()epoll, io_uring기준 경로는 POSIX로 두고, 성능 경로를 Linux 전용으로 분리
타이머clock_gettime(), timer_create()timerfd기본 시맨틱 설명은 POSIX, 이벤트 루프(Event Loop) 최적화는 Linux 별도 문단
파일 공유shm_open(), mmap()memfd_create()표준 경로와 익명 메모리 파일 확장을 문서에서 분리
프로세스 제어fork(), waitpid()pidfd_open(), clone3()재현용 예제는 POSIX 우선, 관리 자동화는 Linux 확장 허용
셸 스크립트POSIX sh 문법Bash 확장/bin/sh 대상인지 명확히 선언
/* Linux 확장 대신 POSIX 우선 경로를 잡는 예시 */
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <unistd.h>

int wait_for_input(int fd, int timeout_ms)
{
    struct pollfd pfd;

    pfd.fd = fd;
    pfd.events = POLLIN;
    pfd.revents = 0;

    for (;;) {
        int ret = poll(&pfd, 1, timeout_ms);
        if (ret >= 0)
            return ret;
        if (errno != EINTR)
            return -1;
    }
}

이 코드는 Linux에서 epoll보다 느릴 수 있지만, 인터페이스 계약은 단순하고 넓게 호환됩니다. 커널 버그 재현이나 최소 예제에서는 이런 코드가 훨씬 가치가 큽니다. 반대로 고성능 서버 경로에서는 Linux 전용 API를 적극 써도 되지만, 그 사실을 문서와 코드 주석에서 숨기지 말아야 합니다.

자주 틀리는 지점

오해왜 틀렸는가올바른 관점
"POSIX 함수는 전부 시스템 콜이다"많은 함수는 libc 조합 또는 스레드 라이브러리와 커널 기능의 결합입니다표준 인터페이스, libc 구현, Linux syscall을 분리해서 말해야 합니다
"Linux에서 되니까 POSIX일 것이다"Linux는 강력한 전용 API를 많이 제공합니다pidfd, epoll, io_uring 같은 확장은 별도로 표시합니다
"rename()만 하면 영속성이 보장된다"표준은 저널 구현이나 저장 장치 flush를 그렇게 단순하게 보장하지 않습니다파일시스템 문맥과 fsync() 요구를 함께 설명해야 합니다
"EAGAIN과 EWOULDBLOCK은 항상 같은 값이다"Linux에서는 같지만 POSIX는 다를 수 있음을 허용합니다이식성 있는 코드는 둘 다 검사합니다
"POSIX sh는 Bash의 부분집합이니 아무 Bash 문법이나 써도 된다"[[ ]], 배열, 프로세스 치환은 POSIX가 아닙니다/bin/sh 대상이면 POSIX 문법만 사용합니다
"POSIX는 Linux 커널 내부 자료구조까지 정한다"표준은 결과와 인터페이스 계약을 정의합니다구현 알고리즘과 자료구조는 커널 설계 선택입니다
문서 작성 기준: 앞으로 이 사이트에서 어떤 인터페이스를 설명할 때 POSIX 표준, GNU 확장, Linux 전용 중 어디에 속하는지 먼저 밝히면 독자의 기대치를 정확히 맞출 수 있습니다. 커널 문서에서 이 층 분리를 해두면 성능 최적화 설명과 이식성 설명이 서로 충돌하지 않습니다.

POSIX.1-2024 (Issue 8) 핵심 변경 요약

IEEE Std 1003.1-2024 / The Open Group Base Specifications Issue 8은 2024년 6월 14일에 공식 발행되었습니다. 직전 버전인 Issue 7 (POSIX.1-2008, 2017년 수정판 포함)과 비교해 약 6년여 만의 메이저 갱신이며, 이 6년여 동안 glibc/musl/Linux syscall 생태계에 관습적으로 정착된 다수의 API가 표준에 편입되었습니다.

한 줄 요약: ppoll(), pthread_mutex_clocklock(), strlcpy/strlcat, getentropy(), reallocarray() 등 과거 GNU/BSD 확장이던 함수들이 표준 함수가 되었고, 언어 바인딩 기준이 C11 → C17로 갱신되었습니다. 반대로 gettimeofday()·gets()·ioctl() 같은 레거시는 삭제되었습니다.

문서 구조와 공개 방식

표준화된 주요 신규 인터페이스

함수/유틸이전 출처Issue 8 의미
ppoll()Linux/glibc 확장시그널 마스크와 함께 타임아웃 poll 표준화
pthread_mutex_clocklock() / pthread_cond_clockwait()glibc 2.30+ GNU 확장CLOCK_MONOTONIC 기반 락 대기 표준화
strlcpy() / strlcat()OpenBSD 유래길이 안전 문자열 복사가 표준 — 커널 문자열 관용 그대로
getentropy()OpenBSD/glibc 확장/dev/urandom 수준 엔트로피를 표준 인터페이스로 제공
reallocarray()OpenBSD/glibc 확장곱셈 오버플로를 검사하는 리얼로케이션(Reallocation) 표준화
posix_getdents()없음(신규)이식성 있는 디렉터리 엔트리 열거 — Linux getdents64의 표준판
posix_close()없음(신규)close()의 EINTR/EBADF 시맨틱을 명시적으로 선택하는 표준판
sig2str() / str2sig()BSD/glibc 확장시그널 번호와 문자열 이름 간 변환 표준화
pthread_rwlock_clockrdlock() / clockwrlock(), sem_clockwait()Linux/glibc 확장클록 기반 타임아웃 대기 프리미티브 확장
batch queue(q*) 유틸리티, fort77Issue 7까지 남아있던 레거시Issue 8에서 삭제 — 배치 큐(qsub/qstat 등)와 Fortran 컴파일러 래퍼
gettimeofday()지원Issue 8에서 삭제(removed) — clock_gettime() 사용 필수
asctime() / ctime()지원이미 Issue 7에서 obsolescent로 지정됨 — Issue 8에서도 obsolescent([OB]) 유지, strftime() 사용 권장
POSIX.1-2024 (Issue 8) 인터페이스 분류 Issue 8에서 인터페이스가 겪는 세 가지 변화 — 확장 편입, 강등, 제거 신규 표준화 GNU/BSD 확장 → 표준 편입 ppoll · posix_getdents posix_close strlcpy / strlcat getentropy · reallocarray pthread_mutex_clocklock sem_clockwait sig2str / str2sig Obsolescent [OB] — 향후 제거 예고 inet_addr · inet_ntoa encrypt · setkey asctime · ctime 향후 삭제 삭제 (Removed) 표준에서 제거 · Linux syscall 유지 gettimeofday gets getitimer/setitimer utime · ulimit ftw · rand_r tempnam qsub 등 · fort77 · ioctl ※ '표준에서 삭제'는 'Linux 비동작'이 아닙니다 — gettimeofday 등은 여전히 Linux syscall로 제공됩니다
POSIX.1-2024 Issue 8 인터페이스 분류 — 신규 표준화 · obsolescent 강등 · 삭제 (출처: POSIX.1-2024 XSH Rationale의 신규/강등/삭제 목록). Issue 8은 GNU/BSD 확장을 표준에 편입하고, 레거시는 강등·삭제했습니다.

Issue 8에서 제거된 레거시 함수

Issue 8은 문제가 많거나 오래된 인터페이스를 대거 정리했습니다. 커널 개발자가 유지보수하거나 포팅하는 코드에서 아래 이름을 보면 Issue 8 기준으로는 비표준이므로 주의해야 합니다. Linux 자체는 여전히 대부분의 시스템 콜(gettimeofday(), getitimer()/setitimer() 등)을 제공하지만, POSIX 표준에서는 삭제된 상태입니다.

제거된 함수Linux에서의 상태대체/권장 경로
gettimeofday()시스템 콜/glibc 래퍼는 여전히 존재clock_gettime(CLOCK_REALTIME)
getitimer() / setitimer()시스템 콜 유지timer_create()/POSIX 타이머, timerfd
utime()glibc 래퍼 유지(현대 커널은 utimensat 경유)utimensat(), futimens() (나노초 정밀도)
ulimit()glibc 래퍼 유지getrlimit()/setrlimit()
gets()제거 경고 — 경쟁 조건(Race Condition) 생성 위험fgets(), getline()
ioctl()Linux에서는 사실상 필수표준에는 미포함 — Linux 전용으로 명시
ftw()glibc 유지nftw()
rand_r(), tempnam(), pthread_setconcurrency() 등glibc에서 deprecated/유지rand()의 스레드 안전 대안, mkstemp()

특히 gettimeofday()는 Issue 7까지도 널리 쓰였지만 Issue 8의 System Interfaces에서 완전히 빠졌습니다. 2026년 기준 신규 코드에서 POSIX 이식성을 목표로 한다면 시간은 clock_gettime()/clock_getres()로 통일하는 것이 맞습니다. 단, Linux 커널 자체와 많은 도구는 여전히 내부적으로 gettimeofday() 시스템 콜을 사용하므로, "표준 삭제"가 "Linux 지원 중단"을 의미하지는 않는다는 점도 함께 구분해야 합니다.

C 언어 정합성 갱신

셸 · 유틸리티 주요 변경

2026 시점 Linux와 POSIX의 간극

기능Linux 제공POSIX 상태
io_uring5.1+ 완전 구현표준 미편입 — 차기 Issue 후보
pidfd_*5.3+ 완비미편입 — 프로세스 핸들 추상화 논의 중
epoll, inotify, signalfd, timerfd표준 Linux미편입 — POSIX는 poll/sigwaitinfo로 커버
statx()4.11+미편입 — stat() 계열 유지
clone3()5.3+미편입
mseal()6.10+ 신규미편입 — 메모리 보호 분야에서 표준화 논의
이식성 작성 원칙: 2026 시점에서 POSIX-portable 코드를 목표로 한다면, Linux 전용 API(io_uring, epoll 등)는 feature-test 매크로 __linux__ 블록 안에 두고, poll()/pthread 기반 대안을 동일 인터페이스로 제공하는 것이 현실적입니다. 반대로 "이식성 필요 없음"이 명확한 커널 tools/ 코드라면 Issue 8 준수보단 Linux native API가 항상 더 빠르고 풍부합니다.

참고자료

다음 학습:
  • 시스템 콜 — POSIX 인터페이스가 Linux syscall 경계와 어떻게 연결되는지 상세히 봅니다.
  • VFS — POSIX 파일 시맨틱이 커널 내부에서 어떤 구조로 구현되는지 확인합니다.
  • Bash 셸 스크립팅 — POSIX sh와 Bash 확장의 실제 차이를 실전 코드로 비교합니다.