Linux Programmer

리눅스 시스템 프로그래밍에서 꼭 알아야 할 사전지식 본문

저술 관련/리눅스 시스템 네트워크

리눅스 시스템 프로그래밍에서 꼭 알아야 할 사전지식

sunyzero 2017. 9. 13. 18:22

리눅스 시스템 프로그래밍을 배울때 꼭 알아야 할 사전 지식, 혹은 체크 항목

 

리눅스 시스템 프로그래밍을 배울때는 다음과 같은 항목을 미리 체크해보고, 방향과 교재, 항목을 체크해봐야 한다. 만일 방향을 잘못 잡으면 처음부터 잘못된 길을 갈 수 있으니 공부를 시작하기 전에 최소한 이 정도는 체크해보는 것이 좋다.

 

1. 어떤 국제 표준에 맞춰서 배울것인가?

리눅스(Linux)는 기본적으로 UNIX standard을 준수한다. 따라서 UNIX 표준과의 호환성을 염두에 둔다면 어떤 표준 레벨에 맞춰서 배울 것인지를 결정해야 한다. 유닉스 표준은 다음과 같은 것들이 있다.[1]

 

System V (SysV)
Svr4
Svr42
43BSD (4.3 BSD)
POSIX (POSIX.1b, POSIX.1c, POSIX.1j ... 버전이 많다)
SUS (Single UNIX Spec.) : SUSv1, SUSv2, SUSv3, SUSv4 ...
== SUSv4는 IEEE std 1003.1-2008 혹은 POSIX 1003.1-2008로도 표기된다.

 

여기에 리눅스/GNU의 비표준 기능도 배울 것인지를 생각해야 한다. 주로 많이 쓰이는 비표준 기능으로는 epoll, mmap extensions, eventfd, signalfd, timerfd, arbitrary kernel buffer, RTS, file cookie 등등이 있다. 컴파일러 비표준, GCC 비표준 문법을 배워야 하는 경우에는 추가로 몇가지를 더 배우기도 한다.

 

위의 표준 중에 현재 가장 많이 쓰이고, 표준으로서 정립된 부분은 SUSv2 (1998년도)이후의 버전이다. 현재 대부분의 교재에서는 SUSv3, 혹은 SUSv4의 기능을 기준으로 학습하도록 권고하고 있다. SUSv3 (2002년)이후에는 C99의 restricted pointer를 사용하기 때문에 restricted pointer에 대한 기법과 이에 맞춰서 프로그래밍을 해야 한다.

 

간혹 대학이나 회사에서 유닉스, 혹은 리눅스 시스템 프로그래밍을 배우면서 Svr4 (1988년)의 옛날 기능으로 학습하는 경우가 있는데, 이는 너무 오래되었기 때문에 권장하지 않는다. 이제는 SVR4기반의 시스템 프로그래밍은 구식이 되었다. 기본적으로 6~70% 정도는 비슷하지만 변경된 부분의 차이가 크다.

 

SUSv4에서 변경되어 과거 버전과 다른 점은 이 링크(POSIX Document)에서 볼 수 있다.[2]

 

 

1.1 최신 국제표준을 배우는 이유는 안전한 프로그래밍을 위해.

최신 표준을 배워야 하는 이유는 보안과 성능 때문이다. 보안적인 측면에서, 혹은 성능 때문에 새로운 함수가 옛날 구식 함수의 기능을 대체하는 경우가 있는데, 호환성 때문에 옛날 구식 함수도 그대로 제공되는게 문제다. 그러다보니 구식 표준으로 배우면 보안에 취약하거나 성능이 안좋은 코드를 만들 수 있는 것이다. 차라리 컴파일이 안되면 그나마 다행인데, 문제가 있는 코드가 얼핏 보기엔 돌아가니까 더 큰 문제를 낳는다.

 

예를 들어 signal이라는 함수를 생각해보자. signal 함수는 신뢰성에 문제가 있어서 새로운 함수인 sigaction이 나왔다. 그래서 표준안을 보면 signal 함수는 되도록이면 쓰지 말고 sigaction을 쓰라고 쓰여있다. 하지만 아직도 많은 학생들이 구식의 signal 함수로 배우는 경우가 많다.

 

시간을 측정하던 gettimeofday도 이젠 구식 함수가 되어 clock_gettime으로 대체되었다.

참고 링크 - https://sunyzero.tistory.com/161
 
sleep같은 함수도 이제 nanosleep으로 대체되었다. 이외에 mktemp 함수는 보안 때문에 mkstemp가 제공된다.

구식 함수인 valloc도 이젠 쓰이지 않는다. 대신 posix_memalign과 aligned_alloc이 제공된다.

 

심지어 memcpy는 옛날 함수의 프로토타입이 변경되었다. 현재 새로운 memcpy는 restricted pointer가 적용되어있다. 따라서 memcpy와 memmove를 쓰임새에 따라서 골라서 써야 한다.

 

이 외에도 세보지는 않았지만 적어도 백여개 이상의 함수들이 새롭게 디자인되고 변경되었다. 특히 2000년 들어서 꽤 많이 변했기 때문에 과거의 시스템 프로그래밍 함수와는 다른 경우가 꽤 많다.

 

더 큰 문제는 구식 함수도 호환성 때문에 계속 제공되므로 학습할 때 딱히 문제가 없어보인다는 점이다. 하지만 자신도 모르는 사이에 보안이나 성능에 문제가 있는 코드를 생산하기 때문에, 새로운 프로그래밍 표준을 보고 학습하는 것은 매우 중요하다. 그러므로 적어도 시스템 프로그래밍을 하기 전에 http://www.opengroup.org에서 UNIX standard (IEEE std 1003.1)을 다운받아서 살펴보도록 하자. (리눅스 man page 영문판도 IEEE std 1003.1의 최신 버전의 내용을 보여주긴 하지만, 간혹 버전 낮은 리눅스에서는 man page가 옛날 표준을 보여주는 경우도 있으니 조심하자. 특히 한글 man page는 99퍼 옛날 버전이므로 보지 말자.)

 

만일 현재 시스템 프로그래밍을 공부하면서 위의 함수들이나 구식 함수인 bzero, bcopy 뭐 이런 것들이 남발되고 있다면, 즉시 멈추고 UNIX 표준에 대해 다시 한번 조사해보자. 표준에 대한 정확한 이해 없이 공부하면 헛공부가 될 수 있다.

 

 

2. 네트워크 프로그래밍을 포함할 것인가?

원래 network programming은 시스템 프로그래밍의 부분 집합이다. 하지만 워낙 덩치가 크기 때문에 네트워크 프로그래밍이라는 분야로 따로 설명되기도 한다.

 

네트워크 프로그래밍 대신에 소켓 프로그래밍 혹은 TCP/IP 프로그래밍이라고 부르기도 하는데, 엄밀하게는 다르지만, 유닉스/리눅스 분야에서는 그들은 같은 의미로 쓰인다. 따라서 리눅스 네트워크 프로그래밍, 리눅스 소켓 프로그래밍, 리눅스 TCP/IP 프로그래밍은 거의 같은 의미로 쓰인다.

 

네트워크 프로그래밍을 배울 때에도 버전별 특징이 다르다. 예를 들어 SUSv3이후로는 IPv6 지원이 강화되면서 옛날에 쓰이던 소켓 프로그래밍 기법인 gethostbyname같은 구식 함수는 표준에서 제거되고, 대신에 새로운 함수인 getaddrinfo를 사용하도록 하고 있다. 따라서 네트워크 프로그래밍을 배울 것이라면 새로운 함수체계에 익숙해져야 한다. (심지어 Apple의 iOS는 기본적으로 구버전의 gethostbyname 같은 기능은 지원하지도 않는다. 그래서 애플 마켓에서는 구버전 API를 사용하면 등록조차 거부된다.)

 

그리고 epoll 같은 비표준 기능은 리눅스에서 네트워크 프로그래밍을 하려면 꼭 필요하므로 배워둬야 한다. 이외에 성능을 위해 sendfile, splice같은 기능들도 같이 배우는 경우가 많다.

 

 

3. 병렬 프로그래밍을 포함할 것인가?

위의 네트워크 프로그래밍과 비슷하게 병렬 프로그래밍도 시스템 프로그래밍의 일부지만, 덩치가 크기 때문에 따로 떼어내서 배우는 경우가 많다다.

 

현재 리눅스에서 C기반으로 사용하는 병렬 프로그래밍은 POSIX threadpthread (POSIX 1003.1c-1995)라고 불리는 기능을 의미한다. pthread는 네이티브 스레드 프로그래밍에 준하는 유닉스 계열 표준 스레드 프로그래밍 API이다. 사용하는 방법은 조금 복잡하다. 따라서 C11에 들어와서 pthread의 랩핑형태의 스레드 표준 함수가 제공되지만, 아직까지는 pthread를 더 많이 쓰고 있다.

 

이 외에 OpenMP도 있다. OpenMP는 C, C++, Fortran을 지원한다. 간단한 스레드 작업은 OpenMP를 쓰는 것이 좋을 수도 있다.

그리고 C++을 대상으로 하는 경우는 전통적인 C API 시스템 프로그래밍과는 조금 다르다. (이건 여기서 논할 내용은 아니므로 스킵.)

 

4. IPC는 어떤 표준을 배울 것인가?

IPC(Inter-Process Communication)는 프로세스간의 데이터 통신에서 굉장히 중요하게 쓰이는 기법이다. 유닉스 계열에서는 좁은 의미로 세가지 기법이 제공되는데, 세마포어(semaphore), 공유메모리(shared memory), 메세지큐(message queue)가 바로 그것이다.

 

하지만 IPC의 경우에는 2가지 구현 기법이 SUS표준에 속하는데, 옛날에 SysV 유닉스로부터 유래한 기능을 XSI IPC라고 부르며, POSIX 표준이 만들어지면서 새롭게 디자인된 IPC를 POSIX IPC라고 부른다. 둘다 표준이므로 두가지 기능을 다 배워야 한다. 그래서 XSI 세마포어, POSIX 세마포어, XSI 공유메모리, POSIX 공유메모리, XSI 메세지큐, POSIX 메세지큐 등이 있다. 즉 공유메모리라고 해도 XSI공유메모리인지, 아니면 POSIX공유메모리인지에 따라 다르다.

 

참고로 옛날 방식인 XSI IPC는 예전에는 주로 사용되다가 2000년대 후반부터는 점점 POSIX IPC를 사용하는 방식으로 바뀌고 있다. 하지만 여전히 둘다 쓰인다. 이에 대한 조금 자세한 글은 아래 링크로 대신한다.

 

리눅스에서 XSI와 POSIX 메세지큐의 비교 : http://sunyzero.tistory.com/168

 

이 외에 비표준이지만 리눅스 전용의 IPC기능인 timer fd, signal fd, event fd, inotify같은 기능도 있다.

 

=======

 

요약하자면, 프로그래밍 표준안을 어디에 맞출 것인지? 비표준을 어디까지 수용할 것인지? 네트워크나 병렬 프로그래밍도 포함해서 배울 것인지를 결정하는 것이다. 이 정도를 미리 조사해보고 공부해야 실수하지 않는다.

 

[1] UNIX standards, http://www.opengroup.org

[2] IEEE std 1003.1-2008,2016 Rationale sections http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xsh_chap03.html

 

 

히스토리

2017-09-13 첫버전

반응형
Comments