WINDOW, MAC, LINUX 등 다양한 운영체제들이 공통 기능을 가지는 이유
POSIX, C 표준 라이브러리, OS 인터페이스의 역사적 통일성 때문이며, Windows조차도 일부 유닉스 스타일의 호환 계층을 갖거나, 유사하게 구현합니다. 즉, UNIX → POSIX → 공통 API의 역사적 발전 덕분에 가능한 겁니다.
UNIX의 탄생과 분열
- AT&T Bell Labs (Ken Thompson, Dennis Ritchie) 에서 작고 단순하며 이식성 있는 운영체제 개발
- UNIX 철학
- 모든 것은 파일이다 : 파일, 디바이스, 소켓 등 동일한 방식으로 접근
- 프로그램은 하나의 일을 잘하라 : 프로그램은 하나의 일을 잘하라
- 텍스트 기반 인터페이스 선호 : 로그, 설정, 파이프 연결의 표준화
- 표준 입출력 기반 통신 : 재사용성과 자동화에 유리
- 이후 BSD(버클리), System V(AT&T), Xenix(Microsoft), SunOS 등 다양한 UNIX 파생 OS 등장
POSIX 표준정립
- POSIX = Portable Operating System Interface (for uniX)
- IEEE + ISO + OpenGroup 등 기관의 주도로 UNIX 계열 시스템의 API와 명령어 표준을 정의한 것
- 유닉스 계열 운영체제의 API를 표준화해서 이식성 보장
- 포함 API : open, read, write, fork, exec, signal, wait, pipe 등
- Linux, macOS, *BSD, AIX, Solaris → 대부분 POSIX 준수
- Java, Python, C, Go 같은 언어들이 의존하는 OS 기능들(ex. open, read, fork)은 대부분 POSIX 기반
즉, POSIX는 "유닉스 스타일 OS는 최소한 이 정도는 지켜줘라"는 API/동작 방식의 약속
범주 | 예시 | 설명 |
1️⃣ 시스템 API (C 함수) | read(), write(), open(), fork(), wait() 등 | 프로세스, 파일, 디렉토리, 메모리, 스레드 등 시스템 기능에 접근하는 함수 |
2️⃣ 쉘과 유틸리티 | sh, echo, ls, find, test, awk, xargs 등 | 쉘 환경과 도구들의 명령어 이름, 옵션, 동작 방식 |
3️⃣ 파일 시스템 구조 및 동작 | 파일 경로 형식 (/usr/bin), /dev/null, 파일 권한, 링크 등 | 경로 규칙, 디바이스 파일, 파일 타입, 파일 퍼미션 |
4️⃣ 환경 변수와 런타임 환경 | PATH, HOME, TERM, TZ, LANG, errno 등 | 프로그램 실행 시 보장되어야 할 환경 변수와 시스템 상태 |
5️⃣ 멀티스레딩 (pthreads) | pthread_create(), pthread_mutex_lock() 등 | 스레드 생성, 동기화, 조건변수, 뮤텍스 등의 동작 규약 |
6️⃣ 시그널 및 에러 처리 | signal(), kill(), SIGINT, SIGTERM, errno | 프로세스 간 통신, 에러 반환 규칙, 시그널 처리 방식 등 |
위와 같은 이유로 현대 OS들은 기본 구조가 비슷
기능 | Linux | macOS | Windows |
파일 시스템 | ext4, xfs | APFS | NTFS |
프로세스 생성 | fork, exec | fork, exec | CreateProcess() ← 유사하지만 동등 기능 |
파일 읽기 | read() | read() | ReadFile() ← 내부적으로 유사 |
스레드 | POSIX Thread | POSIX Thread | Windows Thread ← Java가 감싸서 통일 |
소켓 | BSD Socket | BSD Socket | Winsock ← 거의 동일 API 구조 |
C 표준 라이브러리 (Lib C)
- C는 OS 개발에 가장 적합한 저수준 언어로 인정받아 왔음. 때문에 대부분의 OS 커널, 시스템 도구, 컴파일러가 C로 작성되어 있음. 때문에 운영체제와 다른 프로그램의 상호작용도 C언어로 이루어짐.
- libc(C 표준 라이브러리)는 운영체제가 제공하는 저수준 기능(POSIX에서 정의한 파일 I/O, 메모리, 프로세스 등)에 사용자 프로그램을 개발하는 C 언어 개발자가 접근할 수 있도록 만든 함수 집합입니다.
- 이 libc는 OS 내부 동작(시스템콜 등)을 추상화한 사용자용 인터페이스입니다. 예: read(), open(), malloc(), printf() 등
- 이 함수들은 내부적으로 운영체제의 시스템콜을 호출하며,개발자가 직접 커널에 접근하지 않아도 편리하게 자원을 사용할 수 있게 해줍니다.
- POSIX에서 합의한 것은 “시스템콜 인터페이스 (API)”입니다. 실제 커널 내부의 **시스템콜 진입 방식(syscall 진입점)**은 OS 구현마다 다르며, 보통 어셈블리어로 진입합니다.
POSIX를 기반으로 한 계층별 추상화 구조
계층 | 예시 | 역할 | 추상화 수준 |
시스템콜 계층 (파일 인터페이스) |
open, read, write, close | 커널과 직접 소통. 모든 자원의 최소 공통 인터페이스 |
낮음 (가장 근본) |
C 라이브러리 (libc) |
fopen, fread, fgets, fprintf | 시스템콜을 감싸고 버퍼링, 포맷 등 편의 기능 제공 |
중간 |
스트림 계층 (언어별 추상화) |
Java InputStream, Python file, bash 쉘의 stdin, stdout, stderr |
언어 레벨의 객체지향/ 이벤트 기반 API 제공 |
높음 (가장 추상적) |
계층별 동작 구조
쉘 레벨 (명령어 해석 계층)
$ cat file.txt
- bash는 내부적으로 execve("/bin/cat", ["cat", "file.txt"], ...) 호출
cat 프로그램의 코드 (C 프로그램)
(a) libc 고수준 구현:
FILE* fp = fopen("file.txt", "r");
while (fgets(buf, sizeof(buf), fp)) {
fputs(buf, stdout);}
(b) libc 저수준 구현:
int fd = open("file.txt", O_RDONLY);
while ((n = read(fd, buf, sizeof(buf))) > 0) {
write(STDOUT_FILENO, buf, n);
}
POSIX 시스템콜 API 계층
libc는 내부적으로 read() 시스템콜 API를 호출: glibc의 read()는 내부에서 어셈블리 명령어 사용:
ssize_t read(int fd, void *buf, size_t count);
mov eax, 0 ; 시스템콜 번호 (SYS_read)
mov edi, fd
mov rsi, buf
mov edx, count
syscall ; 커널로 진입
- 시스템콜(System Call) : 응용 프로그램(사용자 공간)이 운영체제 커널(커널 공간)에 서비스(자원 접근, 프로세스 생성, 파일 조작 등)를 요청하기 위해 사용하는 공식적이고 안전한 인터페이스입니다.
- 커널 공간 진입 : 시스템콜을 통해 사용자 모드 → 커널 모드 전환
- 권한 상승 : 커널만 가능한 작업 수행 (예: 파일 읽기, 프로세스 생성)
- 보안 경계 : 직접 하드웨어나 메모리에 접근하지 못하게 보호
- 중단 불가 : 시스템콜은 커널이 처리해야 하므로 반드시 응답이 필요
커널 내부 (sys_read)
- 리눅스 커널은 sys_read()라는 C 함수에서 이 요청을 처리
SYSCALL_DEFINE3(read, unsigned int fd, char __user *buf, size_t count) {
// 파일 디스크립터 유효성 확인
// 해당 파일 구조체 찾기
// 드라이버 또는 파일시스템에서 읽기
// 사용자 공간에 복사
}
JVM, Node, Cpython의 역할은 ??
JVM, CPython, Node.js는 모두 운영체제의 시스템콜을 직접 노출하지 않고, 개발자에게는 libc를 호출하거나 libc의 일부를 직접 구현하여 최적화한다.
'개발기술 > 운영체제 핵심개념' 카테고리의 다른 글
C언어 정리 (0) | 2025.05.18 |
---|---|
I/O 시스템과 스트림 (0) | 2025.05.16 |