POSIX (Portable Operating System Interface)
POSIX는 유닉스 계열 운영체제의 공통 인터페이스 계약을 정리한 표준입니다. 이 문서는 POSIX의 역사, Issue 8 문서 구조, 시스템 인터페이스(System Interfaces)와 셸·유틸리티(Shell and Utilities)의 범위, Linux와 GNU 확장의 차이, 커널 개발자가 실제로 부딪히는 이식성 문제를 한 문서에서 연결해 설명합니다.
핵심 요약
- 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 종속성을 줄일 수 있습니다.
단계별 이해
- 표준의 위치 파악
POSIX는 커널 소스가 아니라 사용자 프로그램과 운영체제 사이의 계약 문서라는 점부터 잡습니다. - 문서 구조 익히기
XBD, XSH, XCU, XRAT 네 덩어리로 나뉜다는 사실을 알면 필요한 조항을 훨씬 빨리 찾을 수 있습니다. - 표준과 구현 분리
open()같은 이름이 표준 인터페이스인지, glibc 래퍼인지, Linux 시스템 콜인지 구분합니다. - Feature Test Macro 설정
#define _POSIX_C_SOURCE 200809L을 소스 맨 위에 두면 GNU 확장을 차단하고 POSIX 범위만 노출합니다._GNU_SOURCE를 쓰면 반대로 모든 GNU/Linux 확장이 열립니다. 둘을 혼용하면 이식성 경계가 흐려집니다. - 실전 적용
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는 여러 유닉스 계열 시스템 사이의 인터페이스 차이를 줄이기 위해 등장했습니다. 오늘날 개발자가 보는 온라인 문서는 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"가 문맥에 따라 함께 언급될 수 있지만, 완전히 같은 범위로 느슨하게 써버리면 문서 참조가 흐려집니다.
Issue 8 문서 구조와 읽는 법
POSIX 문서는 처음 보면 방대해 보이지만, 실제로는 네 덩어리만 익히면 됩니다. 대부분의 커널 개발자는 함수 하나를 찾다가 문서 전체에 압도되는데, 먼저 "어느 권(volume)에 있는가"를 분리하면 탐색 비용이 크게 줄어듭니다.
| 권 | 의미 | 무엇을 찾을 때 보는가 | 대표 링크 |
|---|---|---|---|
| XBD | Base Definitions | 용어 정의, 공통 개념, 헤더, 파일 접근 규칙, 에러 번호, 옵션 이름 | Base Definitions |
| XSH | System Interfaces | open(), fork(), sigaction(), pthread_*, mq_* 같은 함수 규약 | System Interfaces |
| XCU | Shell and Utilities | POSIX sh 문법, make, find, awk, sed, test 같은 유틸리티 | Shell and Utilities |
| XRAT | Rationale | 왜 이런 규칙이 생겼는지, 이전 판본과의 호환성, 모호한 부분의 배경 | Rationale |
커널 개발자에게 특히 중요한 읽기 순서는 다음과 같습니다. 함수 의미가 애매하면 먼저 XSH를 봅니다. 에러 코드나 타입 정의가 헷갈리면 XBD로 올라갑니다. 왜 Linux 구현과 다르게 보이는지 이해가 안 되면 XRAT를 확인합니다. 셸 스크립트 이식성 문제라면 XCU를 봅니다.
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 200809L | POSIX.1-2008 (Issue 7) 인터페이스만 노출 | 이식성 있는 도구·테스트 코드 작성 시 기본값 |
_POSIX_C_SOURCE 202405L | POSIX.1-2024 (Issue 8) 인터페이스만 노출 | 최신 표준 함수(ppoll, getentropy 등) 사용 시 |
_XOPEN_SOURCE 700 | POSIX + X/Open (XSI) 확장 포함 | XSI 옵션 인터페이스가 필요할 때 |
_GNU_SOURCE | POSIX + GNU 확장 + Linux 전용 전부 노출 | Linux 전용 코드. 이식성 버그가 숨기 쉬움 |
_DEFAULT_SOURCE | glibc 기본값 (GNU + POSIX 혼합) | 매크로 미지정 시 glibc의 기본 동작 |
/* 이식성을 최우선으로 할 때 — 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/O | open(), read(), write(), fsync() | 표준 파일 시맨틱과 ext4/XFS 실제 보장 범위를 구분해야 함 | VFS, ext4, XFS |
| 시그널 | sigaction(), sigprocmask(), sigsuspend() | 비동기 안전(async-signal-safe) 제약은 설계 실수의 흔한 원인 | 시그널 처리 |
| IPC | pipe(), 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와 파일시스템 구현체가 이 계약을 만족시킵니다.
파일 디스크립터(File Descriptor)와 I/O 모델
POSIX I/O의 핵심은 파일 디스크립터(File Descriptor, FD)입니다. FD는 프로세스가 열린 파일·파이프·소켓·파일시스템(Filesystem) 디바이스를 참조하는 음수가 아닌 정수입니다. POSIX는 FD의 의미, 상속 규칙, 에러 시맨틱을 규정하지만 커널 내부 자료구조는 강제하지 않습니다.
POSIX FD vs. stdio 스트림
C 표준 라이브러리의 FILE * 스트림(stdio)과 POSIX FD는 다른 수준의 인터페이스입니다. stdio는 내부적으로 POSIX FD를 사용하지만 버퍼(Buffer) 계층을 추가합니다.
| 특성 | POSIX FD | stdio (FILE *) |
|---|---|---|
| 인터페이스 | open / read / write / close | fopen / fread / fwrite / fclose |
| 버퍼링(Buffering) | 없음 (커널 페이지 캐시만 적용) | 사용자 공간(User Space) 내부 버퍼 있음 |
| 표준 | POSIX XSH | ISO 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_errno
errno는 다음 함수 호출에서 덮어써지므로 즉시 복사해 두는 것이 올바른 패턴입니다. - strerror()errno 값을 사람이 읽을 수 있는 문자열로 변환합니다. 멀티스레드 환경에서는
strerror_r()을 사용하십시오.
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 배열, [[ ]], 프로세스 치환, local | Bash 셸 스크립팅, BusyBox |
| 빌드 | make의 기본 규칙과 동작 | GNU Make 함수, 패턴 규칙 확장, 병렬 기능 | GNU Make, 빌드 시스템(Build System) |
| 텍스트 처리 | sed, awk, grep, find, xargs | GNU 전용 옵션, 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 전용이라고 드러내야 함 |
대표적인 예로 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.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년 수정판 포함)과 비교해 약 7년 만의 메이저 갱신이며, 이 7년 동안 glibc/musl/Linux syscall 생태계에 관습적으로 정착된 다수의 API가 표준에 편입되었습니다.
ppoll(), pthread_mutex_clocklock(), strlcpy/strlcat, getentropy(), memset_explicit() 등 과거 GNU/BSD 확장이던 함수들이 표준 함수가 되었고, 언어 바인딩 기준이 C11 → C17로 갱신되었습니다.
문서 구조와 공개 방식
- ISO/IEC/IEEE 9945:2024와 The Open Group Base Specifications Issue 8이 같은 본문을 공유합니다.
- Open Group 공개 URL: pubs.opengroup.org/onlinepubs/9799919799/ — 숫자 9799919799가 Issue 8을 의미합니다 (Issue 7은 9699919799).
- Corrigendum 1(P1003.1-2024/Cor 1)은 Austin Group 이슈 트래커에서 후속 정리 작업이 계속되고 있으므로, 정확한 반영 시점은 Austin Group 공개 상태를 다시 확인하는 편이 안전합니다.
- IEEE Xplore는 유료, Open Group 온라인판은 무료 HTML입니다.
표준화된 주요 신규 인터페이스
| 함수/유틸 | 이전 출처 | 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 수준 엔트로피를 표준 인터페이스로 제공 |
memset_explicit() | C23 유래 | 컴파일러가 제거할 수 없는 시큐어 zero-fill |
posix_getdents() | 없음(신규) | 이식성 있는 디렉터리 엔트리 열거 — Linux getdents64의 표준판 |
futex_* 계열 (제한적) | Linux 전용 | 경량 동기화 프리미티브의 표준 플래그 일부 반영 |
| SCCS 관련 유틸리티 | Issue 7까지 남아있던 레거시 | Issue 8에서 삭제 |
gettimeofday(), asctime/ctime | 지원 | Issue 8에서 obsolescent로 강등 — clock_gettime(), strftime() 사용 권장 |
C 언어 정합성 갱신
- Issue 7은 C99, 이후 TC는 C11을 부분 반영. Issue 8은 C17 (ISO/IEC 9899:2018) 기반으로 용어/헤더를 통일했습니다.
<stdnoreturn.h>,<threads.h>는 여전히 optional로 유지 — 커널이나 고성능 서버에서는pthread계열을 계속 사용.- C23 도입은 차기 Issue(Issue 9, 시기 미정)에서 검토.
셸 · 유틸리티 주요 변경
- POSIX sh:
typebuilt-in이 권장 상태로 표준화,printf %q가 정식,local키워드 논의 진행 중(Issue 8까지는 미포함). - awk:
getline var < cmd용법 명확화,length(array)가 표준으로 확정. - sed:
-EERE 옵션이 표준(이전엔-r은 비표준). - find:
-print0,-delete는 여전히 비표준 — POSIX-pure 스크립트는-exec rm {} +가 기준.
2026 시점 Linux와 POSIX의 간극
| 기능 | Linux 제공 | POSIX 상태 |
|---|---|---|
io_uring | 5.1+ 완전 구현 | 표준 미편입 — 차기 Issue 후보 |
pidfd_* | 5.3+ 완비 | 미편입 — 프로세스 핸들 추상화 논의 중 |
epoll, inotify, signalfd, timerfd | 표준 Linux | 미편입 — POSIX는 poll/sigwaitinfo로 커버 |
statx() | 4.11+ | 미편입 — stat() 계열 유지 |
clone3() | 5.3+ | 미편입 |
mseal() | 6.10+ 신규 | 미편입 — 메모리 보호 분야에서 표준화 논의 |
io_uring, epoll 등)는 feature-test 매크로 __linux__ 블록 안에 두고, poll()/pthread 기반 대안을 동일 인터페이스로 제공하는 것이 현실적입니다. 반대로 "이식성 필요 없음"이 명확한 커널 tools/ 코드라면 Issue 8 준수보단 Linux native API가 항상 더 빠르고 풍부합니다.
참고자료
- POSIX.1-2024 / The Open Group Base Specifications Issue 8 — 최신 공개 온라인 표준 본문입니다.
- Issue 8 Download — 개인용 HTML 묶음을 받을 수 있는 공식 다운로드 안내입니다.
- System Interfaces — 함수 규약, 에러, 헤더, 시맨틱을 확인할 때 보는 권입니다.
- POSIX Shell Command Language — POSIX sh 문법의 핵심 기준입니다.
- POSIX Utilities — 셸과 표준 유틸리티 전체 목록입니다.
- Issue 8 Rationale — 표준 배경과 변경 이유를 설명하는 권입니다.
- Linux man-pages project — POSIX 인터페이스가 Linux에서 실제로 어떻게 노출되는지 확인하는 데 유용합니다.
- 참고자료 - POSIX & ABI — 이 사이트의 관련 표준 허브입니다.
- 시스템 콜, 시그널 처리, IPC, VFS, Bash 셸 스크립팅, GNU Make — POSIX가 실제 구현과 만나는 지점을 상세히 다룹니다.
- 시스템 콜 — POSIX 인터페이스가 Linux syscall 경계와 어떻게 연결되는지 상세히 봅니다.
- VFS — POSIX 파일 시맨틱이 커널 내부에서 어떤 구조로 구현되는지 확인합니다.
- Bash 셸 스크립팅 — POSIX sh와 Bash 확장의 실제 차이를 실전 코드로 비교합니다.