오랜만에 리눅스 카테고리에 포스팅합니다.
학부 때 시스템 프로그래밍 이후에 리눅스를 쓸 일이 없을 거라고 생각했는데, 이번 학기에 관련 수업을 들어서 다시 사용하게 되었네요.
이번 포스팅에서는 우분투 환경에서의 시스템 호출을 구현해보도록 하겠습니다.
(사실 포스팅이 귀찮아서 안 하는데 인터넷에 있는 정보가 대부분 예전 정보라 공부도 할 겸 정리용 포스팅입니다.)
본 포스팅에서 사용한 시스템 환경입니다.
- Ubuntu 14.04.4 LTS (64 bit)
- Oracle VM VirtualBox 5.0.16
※ VMware Player 테스트에서는 커널 빌드후 부팅 불가능 문제가 있었음.
우분투 설치에 대해서는 이전에 포스팅한 #Step 01. 리눅스 설치 포스팅을 참고하세요.
VirtualBox 셋팅 환경은 다음과 같습니다.
이 부분은 여러분의 PC 환경에 따라 알아서 설정하시면 됩니다.
본 포스팅에서는 VIM 설치가 되어있다는 가정하에 설명합니다.
VIM 설치는 이전에 포스팅한 #Step 03. VIM 설치하고 설정하기 포스팅을 참고하세요.
이제 본격적으로 시스템 호출을 구현하도록 하겠습니다.
이 포스팅을 보시는 분들이라면 당연히 시스템 호출에 대해 알고 있는 걸로 알겠습니다.
[ 0 ] 들어가기 앞서...
다음과 같은 과정으로 시스템 호출을 구현합니다.
1.) 새로운 커널 다운로드
2.) 다운로드 받은 커널 압축해제
3.) 시스템 호출 함수 정의
4.) 시스템 호출 번호 등록
5.) 시스템 호출 함수 등록
6.) 커널 컴파일 및 설치
7.) 시스템 호출 테스트
[ 1 ] 커널 다운로드
우분투를 실행시키고, FireFox를 실행시켜 http://www.kernel.org 에 접속합니다.
Least Stable Kernel 4.5 버튼을 눌러 Kernel 4.5를 다운로드합니다.
[ 2 ] 커널 압축 풀기
터미널을 실행시켜, 다운로드 받은 커널이 있는 Downloads 폴더로 들어갑니다.
$ ls
확인하시고 압축 풀기에 앞서 이제 계속 루트 권한을 써야 하니
$ sudo su
루트 권한으로 작업합니다.
* 루트 권한으로 작업할 때에는 주의 하셔야 합니다.
이제 커널 압축파일을 /usr/src에 풀어보겠습니다.
$ tar -xvf linux-4.5.tar.xz -C/usr/src
이제 /usr/src로 이동하여 압축 푼 커널 디렉터리가 있나 확인해봅니다.
$ cd /usr/src
$ ls
[ 3 ] 시스템 호출 함수 정의
/usr/src/linux-4.5/kernel로 이동합니다.
$ cd /usr/src/linux-4.5/kernel/
여기에서 새로운 시스템 호출 함수를 정의합니다.
$ vi mincall.c
이 mincall.c에 아래 코드를 입력하여 새로운 시스템 호출 함수를 정의합니다.
#include <linux/kernel.h> /* 인자를 가지는 시스템 호출은 sys_mincall(int a ...) 방식으로 코딩하시면 됩니다. */ asmlinkage long sys_mincall(void) { printk("System Call Example!\n"); return 0; }
저장 후 vi 에디터를 종료합니다.
Makefile을 엽니다.
$ vi Makefile
# # Makefile for the linux kernel. # obj-y = fork.o exec_domain.o panic.o \ cpu.o exit.o softirq.o resource.o \ ... async.o range.o smpboot.o mincall.o obj-$(CONFIG_MULTIUSER) += groubs.o ...
obj-y 마지막 부분에 mincall.o 를 추가합니다.
마찬가지로 저장 후 vi 에디터를 종료합니다.
[ 4 ] 시스템 호출 번호 등록
/usr/src/linux-4.5/arch/x86/entry/syscalls로 이동합니다.
$ cd /usr/src/linux-4.5/arch/x86/entry/syscalls
여기에서 보면 syscall_32.tbl, syscall_64tbl 파일이 있습니다.
각각 32비트, 64비트 파일입니다. 이 예제에서는 64비트를 사용하니 syscall_64.tbl을 수정합니다.
$ vi syscall_64.tbl
파일을 열고 쭉 내리다 보면 for native 64-bit operation 위에 새로운 번호를 등록하고, [ 3 ]에서 정의한 함수를 적습니다.
시스콜 번호는 기존의 마지막 번호 +1로 적습니다.
마지막 항목에는 sys_함수명을 적습니다.
32비트의 경우 맨 마지막에 추가하시면 됩니다.
326 ... 327 common mincall sys_mincall # # x32-specific system call number start at 512 to avoid cache impact # for native 64-bit operation. # 512 ...
저장 후 vi 에디터를 종료합니다.
[ 5 ] 시스템 호출 함수 등록
/usr/src/linux-4.5/include/linux로 이동합니다.
$ cd /usr/src/linux-4.5/include/linux
시스템 호출 함수의 헤더 파일인 syscalls.h 파일을 수정합니다.
$ vi syscalls.h
이제 syscalls.h 파일 마지막 부분인 #endif 바로 윗줄에 시스템 호출 함수를 적습니다.
... asmlinkage long sys_mincall(void); #endif
저장 후 vi 에디터를 종료합니다.
이로써 시스템 호출 정의와 등록은 끝났습니다.
이제 커널을 컴파일 하고, 설치하여 적용해 봅시다.
[ 6 ] 커널 컴파일 및 빌드
우선적으로 다음 커맨드를 사용하여 최신 상태로 업데이트 하고, 패키지를 설치한다.
$ apt-get install gcc $ apt-get install libncurses5-dev $ apt-get update $ apt-get upgrade
먼저, 커널 경로로 이동한다.
$ cd /usr/src/linux-4.5
그리고 커널 설정은 기본 설정으로 셋팅하여 컴파일 하겠다.
$ make defconfig
자 이제 컴파일 하기 전에, 멀티코어 사용자라면 다음 커맨드를 통해서 코어수를 확인후 작업하자.
작업이 더 빨라진다.
$ cat /proc/cpuinfo
그리고 나오는 화면은 다음과 같다.
... cpu cores : 4 ...
이 항목이 컴퓨터의 코어수 이다.
(가상머신의 경우 셋팅값에 따라 다를수 있다.)
이제 Make -j 코어수 옵션을 사용하여 컴파일 하자.
필자의 가상머신의 코어수는 4개라 다음과 같이 사용하였다.
$ make -j4
이제 기다리자....
기다리자...
정상적으로 작업이 완료되면 아래와 같이 나온다.
자 이제 커널을 설치하자.
[ 7 ] 커널 설치
$ make modules_install install
위 명령어를 입력하여 설치한다.
설치가 완료되면 위 사진처럼 뜬다.
재부팅 전, 현재 커널 버전을 확인한다.
$ uname -r
그리고 이제 아래 명령어를 이용하여 재부팅 한다.
$ shutdown -r now
[ 8 ] 시스템 호출 테스트
$ uname -r
재부팅이 완료되면 마찬가지로 위 명령어를 입력하여 커널 버전을 확인한다.
정상적으로 설치가 완료되었다면 4.5.0으로 나온다.
이제, 앞에서 구현한 시스템 호출을 프로그램상에서 호출하여 테스트 해봅시다.
$ vi syscall_test.c
#include <stdio.h> #include <sys/syscall.h> int main(void) { /* 앞서 등록한 시스템 호출 번호를 적습니다. a에는 327번 시스템 호출의 반환값이 저장됩니다. 만약 인자를 가지는 시스템 호출의 경우에는 syscall(327, argument) 방식으로 호출하면 됩니다. */ long int a = syscall(327); // 327 syscall의 반환값은 printf()를 이용하여 출력합니다. printf("System Call returned %ld\n", a); return 0; }
위 코드를 입력하고 vi 에디터를 종료 합니다.
$ gcc syscall_test.c
gcc를 이용하여 컴파일및 빌드 합니다.
$ ./a.out
파일을 실행하여 시스템 호출이 정상적으로 이루어졌는지 확인합니다.
예제를 그대로 따라 하셨다면, 0이 리턴되어야 정상입니다.
시스템 호출에 작성했던 printk( )부분은 다음 명령어를 사용하여 확인합니다.
$ dmesg
그러면 "System Call Example!" 이란 메시지를 보실 수 있습니다.
이제 모든 작업이 완료되었습니다.
수고하셨습니다. ^^
본 포스팅은 아래 글들을 참고로 작성되었습니다.
만약 잘못된 내용이 있다면 댓글로 알려주시면 감사하겠습니다.
https://tssurya.wordpress.com/2014/08/19/adding-a-hello-world-system-call-to-linux-kernel-3-16-0/
http://stackoverflow.com/questions/17652555/where-is-the-system-call-table-in-linux-kernel