System Call Function VS Library Call Function

오늘 포스트는 개발자로써 반드시 짚고 넘어가야할 부분 중에 하나를 써보고자 합니다.



함수의 사용

우리는 콘솔 화면에 어떤 문자열을 출력하거나 파일에 입출력을 한다거나 문자열을 입력받게 하고 싶을 때 각각 사용하는 함수를 사용을 합니다. 그래서 그 함수가 구현되어 있는대로 인자값을 넣고, 그 인자값을 넣은대로 코딩을 하면 출력 결과가 나오게 됩니다.


그런데, 이 함수에도 두 가지의 존재로 나뉩니다.


가령 예를 들어보자면....


이런 코드가 있습니다. 위 코드는 만약 파일이 존재하지 않으면 오류를 나타내고, 프로그램을 종료하고 있습니다. 동시에 오류 코드를 출력하고 있죠.


다른 소스 코드를 한 번 보겠습니다. 이 코드도 역시 파일을 찾아서 읽고, 해당 파일의 내용을 출력하는 소스 코드이죠.

분명 하는 일은 같은 소스 코드지만, 코드 내용은 다릅니다. 어떤 차이가 있는 걸까요?


저 두 소스 코드의 Key Point는 바로 파일을 읽기 위해 사용한 함수입니다. 전자의 코드는 CreateFile이라는 함수를 사용하였고, 후자는 fopen이라고 하는 함수를 사용했습니다. 또한 파일이라는 대상이 전자는 HANDLE 구조체이고, 후자는 FILE 구조체입니다.


대충 아시는 분들은 저 두 코드가 Windows의 코드이냐, Unix 코드이냐를 가리는 것이라고 생각할 것입니다.



Library Call Function

여러분들이 단순하게 사용하는 fopen, fclose 등의 C 표준 함수들은 바로 일반 라이브러리 함수입니다. 라이브러리 함수들에는 프로그래밍 언어에서 제공하는 기본 함수들과 컴파일러 별로 추가적으로 제공하는 API들이 보통 라이브러리 함수에 속합니다.


때문에 프로그램 내부에서 메모리 할당/해제가 가능합니다. 즉, malloc 함수 등을 이용을해서 메모리의 할당과 해제가 자유롭다는 이야기지요.

그래서, 후자의 코드에는 파일을 열 때, 포인터가 내부에서 사용됩니다.



System Call Function

위에서 사용한 전자의 코드는 System Call 함수를 사용한 것입니다. 


System Call 함수는 응용 프로그램에서 운영체제에게 어떠한 자원을 요구하는 하나의 수단입니다. 컴퓨터에는 여러 가지 자원이 있습니다. 프로세서, 메모레, 하드디스크 .... 하지만 우리가 컴퓨터를 켜자마자 나오는 것은 BIOS, 그리고 그 다음은 운영체제입니다.


부팅 순서에 대해서 알고 계신 분들이라면, 아마 컴퓨터의 모든 자원은 운영체제에서 관리하고 있다는 것을 아실 것입니다. 그래서 프로그램에서 어떤 자원을 가지고 올 때도 운영체제 즉, 커널에서 가져와야 합니다.


System Call Function을 사용해야 하는 경우는 자원의 할당보다는 사실 어떤 운영체제에서의 특정한 자원을 가지고 싶을 때 주로 사용합니다. Windows의 경우, 자신이 만든 프로그램을 중복 실행하게 하고싶지 않습니다. 그러기 위해서는 현재 Windows에서 프로세스의 정보를 가져와 나와 같은 이름의 프로세스를 가지고 있다면, 현재 호출한 프로그램을 종료하는 방법으로 구현을 해야합니다.


비슷하게는, 어떤 온라인 게임에서 유저의 핵 실행을 방지하기 위해 어떤 특정한 디버거의 프로그램 실행을 방지하거나 특정 보안 프로그램의 경우, 어떤 프로세스를 실행하면 그에 반하는 프로세스를 죽이는 프로그램을 많이 봤을 것입니다. 이 또한 위와 같은 방법으로 구현해야 합니다.


그러기 위해서 Windows에는 Kernel Object가 존재하고, 이를 C Language나 C++ Language와 같은 고급 프로그래밍 언어에서 사용하기 위해 HANDLE 이라는 구조체를 사용해 이 오브젝트에 접근할 수 있습니다. 그리고 함수를 통해서 프로그램에서 원하는 Action을 취할 수가 있겠죠.


비슷하게 코드로 한 번 구현을 해봤습니다. Windows kernel에서 제공하는 System Call 함수인 Process32First 함수와 Process32Next 함수를 사용해 프로세스 목록에서 각 프로세스를 검색하고, 해당하는 이름의 프로세스를 발견하면, Kill, 그렇지 않으면 프로세스가 실행 중이지 않음을 콘솔 화면에 표시하게 하였습니다.


이처럼 System Call 함수는 어떤 하드웨어 장치나 운영체제에 대한 Low-Level 프로그래밍 특징을 몰라도, 커널에서 제공하는 함수만 알면, 쉽게 사용할 수 있어 굉장히 편하게 코딩할 수 있는 장점을 가지고 있습니다.



System Call vs Library Call

대충 시스템 콜 함수와 라이브러리 콜 함수에 대해 어느 정도는 이해하셨을 것이라 생각합니다. 그러면, 위에 처럼 운영체제가 가지고 있는 어떤 특정한 정보를 가지고 싶을 때 System Call 함수를 사용하면 됩니다. 


그렇다면, 파일과 같은 리소스를 가지고 코딩을 하였을 때, System Call 함수를 사용하는 것과 라이브러리 함수를 사용하는 것에 어떠한 차이점이 있을까요?


사실 프로그램 내부에서 메모리를 할당하는 malloc 함수나 memset 함수는 내부에서 System Call Function을 사용합니다. 그래서 차이로 말씀을 드리자면 많은 차이는 나지 않지만, 실행 면에서 본다면, 분명한 차이가 있습니다.


바로 사용자 모드로 실행하는 것과 커널 모드로 실행하는 것인데요. 라이브러리는 사용자 모드 즉, 파일에 존재하고, System Call 함수는 커널에 존재하기 때문에 프로그램을 실행하는 데 있어 그 모드가 구분됩니다.


그래서 프로세서는 System Call 함수를 만나면, 이를 커널 모드로 바꾸고, 커널 함수를 호출하게 됩니다. System Call 함수인지를 구분하기 위해 EAX 레지스터(고급 프로그래밍 언어로 말하자면 return 값)를 사용하게 됩니다.


우리가 프로그래밍할 때 처음으로 사용하는 함수인 main 함수도 System Call 함수에 속하며, System Call 함수는 return type이 int이며, 오류는 -1이고, 정상이면 0 혹은 양의 정수를 반환합니다. (하지만 exit 함수는 System Call function이 아닙니다!)


또 한가지 차이점은 Library Call 함수에서 fopen이나, scanf 등을 사용할 때, 저장소로 사용하는 것이 있죠. 바로 Buffer를 사용한다는 것입니다.


흔히 우리가 char형 배열을 사용해 문자열을 입력받다가 그 공간이 남으면, 계속 입력을 받는 상황이나 띄어쓰기를 해 문자열을 입력 받을 경우, 띄어쓰기 한 뒷 부분은 짤려서 출력이 되는 경우를 많이 봤을 것입니다. 혹은 배열의 크기보다 너무 많은 문자 수를 입력해 그 입력한 갯수를 초과하여 다음 입력 버퍼에도 영향을 끼치는 경우가 있었죠. 그래서 사용했던 함수들 중에는 fflush 함수를 썼던 경우도 있었을 것입니다.


그래서 C 표준 함수를 흔히 Buffered I/O Function이라고 이야기들을 많이 합니다. 모두 버퍼를 사용하기 때문이죠. 예를 들어, scanf 함수를 이용해서 문자 하나씩 1 byte 씩 읽는다고 가정할 때, 컴파일러는 커널로부터 C 라이브러리 내부적으로 정한 버퍼 크기만큼만 우선 읽어오고, 그리고 다음번 읽어올 때는 버퍼에 있는 만큼의 수가 남았다면 커널에서의 자원을 사용하지 않고, C 라이브러리에 존재하는 버퍼만 사용하기 때문에 시스템의 리소스 부담량을 줄여주기 때문에 효율적이라고 볼 수 있습니다.


하지만 System Call 함수는 실제 컴퓨터의 리소스를 사용하는 것이기 때문에 반복작업을 할수록 그만큼 시스템에 무리가 가게 됩니다. 한마디로 이런 것이겠죠. 시스템 콜 함수를 호출하다가 종료하면 다시 리소스를 반납하고, 또 부르면 또 불러서 썼다가 다시 반납하고, 대충 이런 식입니다.


그러므로 같은 파일을 읽어온다고 하더라도, C 표준 함수를 쓰는 것이 다른 플랫폼이나 운영체제의 호환성도 컴퓨터 자원 사용의 효율성도 그만큼 높다는 것이 증명이 됩니다. (물론 상황에 따라서 다르기 때문에 반드시 그렇다는 것은 아닙니다.)



마치며....

여기까지 System Call 함수, Library Call 함수에 대해서 알아봤습니다.


아마 프로그램을 개발하다보면, 여러가지 방법이 있으셨다는 것은 다들 아시는 이야기일 것이라 생각이 됩니다. 특히 이제 C나 C++ 언어를 가지고 시스템 프로그래밍을 하다보면 여러 가지 기존에 썼던 표준 함수와 별반 차이 없는 함수들을 많이 만나실텐데요.


대충 얼핏보면, 그렇게 어려운 개념은 아닙니다. (내용으로 길게 쓰다보니... 조금 어렵게 느껴질 뿐이지만.) 코딩을 하다보면, 아무래도 자신이 만든 프로그램이 플랫폼 위, 운영체제 위에서 동작하는 응용 프로그램이기 때문에, 그들의 리소스와 특징을 잘 구별해서 코딩을 하면, 좋은 프로그램이 나올거라고 생각합니다.

comments powered by Disqus

Tistory Comments 0