170 likes | 437 Views
System Call 구현 기초. Linux 커널의 새로운 시스템 호출 구현은 아래 두 가지 작업으로 구성 커널 수정 시스템 호출 번호 할당 시스템 호출 테이블 등록 시스템 호출 처리 함수 구현 커널 컴파일 및 리부팅 새로운 시스템 호출을 이용하는 사용자 수준 응용 시스템 호출을 사용하는 프로그램 작성 라이브러리 작성 ( 꼭 필요한 것은 아님 ) 커널을 수정하기 위해서는 커널 소스 필요
E N D
System Call 구현 기초 • Linux 커널의새로운 시스템 호출 구현은 아래 두 가지 작업으로 구성 • 커널 수정 • 시스템 호출 번호 할당 • 시스템 호출 테이블 등록 • 시스템 호출 처리 함수 구현 • 커널 컴파일 및 리부팅 • 새로운 시스템 호출을 이용하는 사용자 수준 응용 • 시스템 호출을 사용하는 프로그램 작성 • 라이브러리 작성(꼭 필요한 것은 아님) • 커널을 수정하기 위해서는 커널 소스 필요 • Fedora 13 이상의 경우 커널 개발 패키지를 설치하면 /usr/src/kernels 디렉토리 밑에 설치됨 (커널 버전 2.6.35.6) • 다운로드 http://www.kernel.org $ wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.35.6.tar.gz $ ftp ftp.kernel.org • 커널 소스를 구해서 /usr/src/kernels 디렉토리 밑에 설치 $ tar xvfz linux-2.6.35.6.tar.gz 또는 $ tar xvfj linux-2.6.35.6.tar.bz2 • 설치한 소스 디렉토리를 mylinux로 심볼릭 링크 $ ln –s linux-2.6.35.6 mylinux • $ ftp ftp.kernel.org • id: anonymous • password: [Enter] • ftp> binary • ftp> cd pub/linux/kernel/v2.6 • ftp> get linux-2.6.35.6.tar.gz (또는 bz2 파일)
시스템 호출 과정 ENTRY(system_call) /*arch/x86/kernel/entry_32.S SAVE_ALL …. call *SYMBOL_NAME(sys_call_table)(,%eax,4) …. ret_from_sys_call(schedule, signal, bh_active, nested interrupt handling) main { … syscall (__NR_newsyscall); } IDT(IVT) divide_error() 0x0 degug() nmi() sys_call_table … … newsyscall() { … movl 338, %eax int $0x80 … } … /*real handler*/ sys_newsyscall() { printk(…); } system_call() 0x80 sys_exit() 1 2 3 4 … sys_fork() sys_read() sys_write() 338 sys_newsyscall() IDT: Interrupt Descriptor Table = IVT: Interrrupt Vector Table
커널 수정 (1) • 시스템 호출 번호 할당 • Linux 커널이 제공하는 시스템 호출은 각각 고유한 번호를 갖는다 • /usr/src/kernels/mylinux/arch/x86/include/asm/unistd_32.h 파일에 구현되어 있다 • #define __NR_newsyscall 338 을추가한다 • 시스템 호출 개수를 0~338 즉 339로수정한다 /* arch/x86/include/asm/unistd_32.h 파일의 내용 * #ifndef _ASM_I386_UNISTD_H_ #define _ASM_I386_UNISTD_H_ #define __NR_exit 1 #define __NR_fork 2 #define __NR_read 3 … #define __NR_recvmmsg 337 #define __NR_newsyscall 338 #ifdef __KERNEL__ #define NR_syscalls 339 • /usr/include/asm/unistd_32.h 파일도 똑같이 수정해 준다.
커널 수정 (2) • 시스템 호출 테이블 등록 • 시스템 호출처리 함수는 sys_call_table이라는 테이블에 등록이 되어 있어야 한다 • sys_call_table 정보는 /usr/src/kernels/mylinux/arch/x86/kernel/syscall_table_32.S 파일에 구현되어 있다 • /*335*/ 찾은다음 /*338*/ 라인에 .long sys_newsyscall 라인을 추가한다 ENTRY(sys_call_table) .long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */ .long sys_exit .long sys_fork .long sys_read .long sys_write .long sys_open /* 5 */ … .long sys_rt_tgsigqueueinfo /* 335 */ .long sys_perf_event_open .long sys_recvmmsg .long sys_newsyscall
커널 수정 (3) • 시스템 호출 처리 함수 구현 • 일반 적으로 태스크 관리자 관련 함수는 /usr/src/kernels/mylinux/kernel/ 파일 시스템 관련 함수는 /usr/src/kernels/mylinux/fs/ 밑에 구현 되어 있다. • 구현할 함수를 /usr/src/kernels/mylinux/kernel/ 디렉토리 밑에 newsyscall.c라는 이름으로 구현한다. /* /usr/src/mylinux/kernel/newsyscall.c */ #include <linux/linkage.h> #include <linux/unistd.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/sched.h> asmlinkage int sys_newsyscall() { printk("Hello Linux, 7777777 SUNG MeeYoung is in Kernel\n"); return 0; } • /usr/src/kernels/mylinux/kernel/Makefile 수정 • obj -y = 라인에 newsyscall.o 추가
커널 수정 (4) • 커널 구성 $ cd /usr/src/kernels/mylinux (커널 소스의 상위 디렉토리로 이동) $ make mrproper (기존의 커널 설정 제거) • 커널버전 2.6부터 makedep 과 makeclean 불필요 • 아래 3 가지 중한 가지만 실행 (재구성할 때는 .config 파일 삭제하고 구성) • $ make menuconfig (범용적 커널 설정 스크립트 실행) -*- Networking supprot - - - > (Select) WiMax, RF switch subsystem 체크 해제 • (또는 $ make xconfig (그래픽 환경) • (또는 $ cp ../2.6.35.6-45.fc14.i686.PAE /.config ./ $ make oldconfig (실행후 선택문 모두 엔터)
커널 수정 (5) • 커널 컴파일 • 옵션은 아래 사이트 참고 http://doc.kldp.org/KoreanDoc/html/Kernel24_Intro-KLDP/Kernel24_Intro-KLDP-2.html $ make bzImage (컴파일, 새 커널 생성) • 커널 인스톨 • 모듈로 구성된 커널 내부 구성 요소를 알려주고, 이후 구성 요소들이 사용될 때 자동으로 커널에 적재되게 함 $ make modules (모듈 컴파일) $ make modules_install (모듈 인스톨) • 생성된 커널을 /boot 디렉토리에 복사 $ make install (아래 작업 자동 수행) $ cp arch/x86/boot/bzImage /boot/vmlinuz-2.6.35.6 $ cp System.map /boot/System.map-2.6.35.6 (새 System.map으로 대치 ) • 부트로더 설정 $ vi /etc/grub.conf default=0 title Fedora (2.6.35.6) 수정 $ reboot 부팅시에 F5로 커널 선택화면 전환 후 부트로드에서 새로 컴파일 한 커널 선택 후 부팅
사용자 수준 응용 작성 • 라이브러리 작성(선택사항) • $ gcc –c newsys.c • $ ar –r libnew.a newsys.o • $ ranlib libnew.a • $ vi test.c • $ gcc test.c –L /root(libnew.a가 있는 디렉토리) -lnew • $ ./a.out • /* contents of newsys.c */ …독립된 파일 • #include <linux/unistd.h> • #include <errno.h> • call() • { • return syscall(__NR_newsyscall); • } • /* contents of test.c */ …독립된 파일 • main() • { • int i; • i = call(); • } • 사용자프로그램 test.c 작성 #include <linux/unistd.h> #include <errno.h> #include <stdio.h> main() { int i; i=syscall(__NR_newsyscall); printf(“%d\n”, i); } $ gcc –o test test.c 컴파일 $./test 실행 • Control-Alt F5consol 모드 전환 Control-Alt F1graphic 모드복귀 • $ dmesg | tail 결과 확인 /* /usr/unistd.h : line 1025 */ extern long int syscall (long int __sysno, ...) __THROW; /* /usr/src/mylinux/include/asm-i386/unistd.h (oldversion)*/ #define _syscall0(type,name) \ type name(void) \ { \ long __res; \ __asm__ volatile ("int $0x80" \ : "=a" (__res) \ /* eax register */ : "0" (__NR_##name)); \ __syscall_return(type,__res); \ }
실습 1: sys_gettaskinfo() • sys_gettaskinfo() 라는 커널정보를 출력하는 새로운 시스템 호출(339번) 구현 • current(/usr/src/kernels/mylinux/arch/x86/include/asm/current.h : 17행)라는 전역 변수가 task_struct (/usr/src/mylinux/include/linux/sched.h : 1167행) 자료구조를 포인팅 • #include <linux/linkage.h> • #include <linux/unistd.h> • #include <linux/errno.h> • #include <linux/kernel.h> • #include <linux/sched.h> • //#include <linux/file.h> • asmlinkage int sys_gettaskinfo() • { • int i,cnt =0; • printk("PID: %d\n",current->pid); • printk("PPID: %d\n",current->parent->pid); • if(current->state == -1) printk("Unrunable state\n"); • else if (current->state == 0) printk("Runable state\n"); • else if (current->state == 1) printk("Interruptable state\n"); • else if (current->state == 2) printk("Uninterruptable state\n"); • else if (current->state == 4) printk("Stopped state\n"); • else if (current->state == 8) printk("Zombie state\n"); • else if (current->state == 16) printk("Dead state\n"); • else printk("Unkown sate\n"); • printk("Priority: %lu\n", current->rt_priority); • printk("Scheduling Policy: %lu\n",current->policy); • printk("User CPU time: %lu ticks\n", current->utime); • printk("System CPU time: %lu ticks\n", current->stime); • printk("Start time: %lu \n", current->start_time); • printk("Number of major faults: %lu\n", current->maj_flt); • printk("NUmber of minot faults: %lu\n", current->min_flt); • return(0); • } • 사용자 응용 test1.c 작성 • #include <linux/unistd.h> • #include <errno.h> • #include <sys/syscall.h> • main() • { • int i; • i=syscall(__NR_gettaskinfo); • }
next_task prev_task next_task prev_task prev_task init_task … task_struct task_struct 태스크 리스트
실습 2: getstat() • sys_getstat(int id, struct mystat *user_buf) 시스템 호출(340번) 구현 • [참고서적] 리눅스 매니아를 위한 커널 프로그래밍 p70~74 • /* 헤더 파일 mystat.h */ • struct mystat { • int pid; • int ppid; • /* • * pid_t pid; • * pid_t ppid; • */ • int state; • int priority; • int policy; • long utime; • long stime; • long starttime; • unsigned long min_flt; • unsigned long maj_flt; • int open_files;};
실습 2 • 사용자 응용 test2.c • #include <linux/unistd.h> • #include <stdio.h> • #include <errno.h> • #include "mystat.h" • #include <stdlib.h> • struct mystat *mybuf; • int i; • int main(int argc, char* argv[]) • { • int task_number; • if(argc != 2) • { • printf("USAGE: a.out pid\n"); • exit(1); • } • task_number = atoi(argv[1]); • mybuf = (struct mystat *)malloc(sizeof(struct mystat)); • if(mybuf == NULL) • { • printf("Out of Memory\n"); • exit(1); • } • printf("PID %d\n",task_number); • i = syscall(__NR_getstat, task_number, mybuf); • printf("%d\n", i); • printf("PID = %d\n", mybuf->pid); • printf("PPID = %d\n", mybuf->ppid); • if(mybuf->state == -1) printf("Unrunable state\n"); • else if(mybuf->state == 0) printf("Running state\n"); • else if(mybuf->state == 1) printf("Interruptable state\n"); • else if(mybuf->state == 2) printf("Uninterruptable state\n"); • else if(mybuf->state == 4) printf(" Stopped state\n"); • else if(mybuf->state == 8) printf(" Zombie state\n"); • else if(mybuf->state == 16) printf("Dead state\n"); • else printf("Unknown state\n"); • printf("Priority = %d\n", mybuf->priority); • printf("Policy = %d\n", mybuf->policy); • printf("Task.utime = %lu\n", mybuf->utime); • printf("Task.stime = %lu\n", mybuf->stime); • printf("Task.starttime = %lu\n", mybuf->starttime); • printf("minor fault = %lu\n", mybuf->min_flt); • printf("major fault = %lu\n", mybuf->maj_flt); • printf("opened files = %lu\n", mybuf->open_files); • return 0; • } • 시스템 호출 처리함수 getstat.c • /* getstat.c */ • #include <linux/unistd.h> • #include <linux/errno.h> • #include <linux/sched.h> • #include <../arch/x86/include/asm/uaccess.h> • #include "mystat.h" • #include <linux/slab.h> • #include <linux/file.h> • asmlinkage int sys_getstat(int id, struct mystat *user_buf) • { • struct mystat *buf; • int i = 0, cnt = 0; • struct task_struct *search; • search = &init_task; • while(search->pid != id) • { • search = list_entry((search)->tasks.next, struct task_struct, tasks); • if(search->pid == init_task.pid) • return(-1); • } • buf = kmalloc(sizeof(struct mystat), GFP_KERNEL); • if(buf == NULL) • return(-1); • buf->pid = search->pid; • buf->ppid = search->parent->pid; • buf->state = search->state; • buf->priority = search->rt_priority; • buf->policy = search->policy; • buf->utime = search->utime; • buf->stime = search->stime; • buf->min_flt = search->min_flt; • buf->maj_flt = search->maj_flt; • copy_to_user((void *)user_buf, buf, sizeof(struct mystat)); • return 0; • }
커널 컴파일과 커널 교체 • 기존의 컴파일된 커널을 수정된 커널로 교체할 때 사용한다 • 모듈 컴파일이 완료 된 상태에서 모듈 설정없이 커널만 수정할 경우 모듈 컴파일은 하지 않고 커널 컴파일과 커널 교체 작업만 한다 • $ pwd • /usr/src/kernels/mylinux • $ make mrproper (기존의 커널 설정 제거) • $ cp ../2.6.35.6-45.fc14.i686.PAE/.config ./ • $ make oldconfig (실행 후 선택문 모두 [Enter]) • $ make bzImage • 생성된 커널을 /boot 디렉토리에 복사 • $ make install (아래 작업 자동 수행하고새 커널 버전 생성) • $ cp arch/x86/boot/bzImage /boot/vmlinuz-2.6.35.6 • $ cp System.map /boot/System.map-2.6.35.6 (새 System.map으로 대치 ) • 새 커널 버전 생성 원하지 않으면 커널 이미지만 복사 • $ cp arch/x86/boot/bzImage /boot/vmlinuz-2.6.35.6 • 부트로더 설정 • $ vi /etc/grub.conf • default=0 • title Fedora (2.6.35.6) mylinux • $ reboot (부팅시에 F5로 커널 선택화면 전환 후 부트로드에서 새로 컴파일 한 커널 선택 후 부팅)
Vi 편집기에서 한글이 깨져서 나올시 • 접속한원격 호스트에서 아래 시행하여 지원 폰트 확인 $ unset LANG $ LANG=C $ locale –a /* locale: locale specific information */ • 로컬 linux 호스트의 /etc/sysconfig/i18n 설정을다음과 같이 변경 LANG=“ko_KR” (또는 “ko_KR.eucKR”, “ko_KR.EUC”, “ko_KR.euckr” 등 $locale 결과)SUPPORTED="en_US.iso885915:en_US:en:ko_KR.eucKR:ko_KR:ko" SYSFONT="lat0-sun16" SYSFONTACM="iso15" • 관리자가 바꿔주지 않는다면 ~/.bashrc 에 다음을추가 LANG=“ko_KR” SUPPORTED=“en_US:en_US.iso885915:en:ko_KR.eucKR:ko_KR:ko” SYSFONT=“latarcyrheb-sun16” exportLANG SUPPORTED SYSFONT • Desktop GUI 메뉴에서도 터미널 설정 메뉴 한글 코딩 방식을 “eucKR” 로 설정
네트워크 설정방법 • netconfig 또는 redhat-config-network명령을 실행하거나 메뉴에서 시스템 설정에 네트워크를 실행 후 eth0 선택 후 편집을 누른다 • (위와 같이 장치가 잡혀있지 않으면 커널 컴파일 후 모듈설치)
네트워크 설정 • 그림과 같이 설정한다 (ip주소에는 해당 컴퓨터의 주소 입력) • 입력이 완료 되면 확인을 누른후 DNS설정을 한다
네트워크 설정 • 호스트명에는 해당 자리의 컴퓨터 이름을 넣는다 (예)stu01 • 설정 완료 후 재부팅하거나장치를 비활성화 시켰다가 다시 활성화 시킨다 • ping 117.16.244.56 실행 후 응답이 제대로 오면 성공