320 likes | 692 Views
Kernel Programming Kernel. VMware 설치 Debian 7 설치 커널 컴파일 newsyscall. Chapter 1 리눅스 커널 프로그래밍 환경 구축. VMware & Debian 설치 1. VMware Workstation 10 for Windows 설치 www.vmware.com http:// www.vmware.com/products/workstation/workstation-evaluation debian-7.6.0-i386-netinst.iso 다운로드
E N D
Kernel Programming Kernel VMware 설치 Debian 7 설치 커널 컴파일 newsyscall
VMware & Debian설치 1 • VMware Workstation 10 for Windows 설치 • www.vmware.com • http://www.vmware.com/products/workstation/workstation-evaluation • debian-7.6.0-i386-netinst.iso 다운로드 • www.debian.org • http://cdimage.debian.org/debian-cd/7.6.0/i386/iso-cd/ • New Virtual Machine • Custom • Workstation 10 • I will install … blank hard disk • Linux, Other Linux 3.2x kernel • VM name: Debian • 2440 • NAT • BUS • IDE • Create a new virtual disk • 30.0G 이상 권장 • VM settings • CD/DVD use image file, debian-7.6.0-i386-netinst.iso
VMware & Debian설치 2 • Power on • Graphical install 선택 • 한국어 • 대한민국 • 호스트이름 • 도메인 이름 • 루트 암호 • 사용자 계정 등록 • 자동 디스크 전체 사용 • 모두 한 파티션에 설치 • 바뀐 사항 디스크에 쓰기 • 디스크 설정 확인 예 • 베이스 시스템 설치 • 패키지 관리자 설정 ftp.kr.debian.org, 프록시빈칸 • 패키지 선택 계속 • 설치 시간? • 마스터 부트 레코드에 Grub 설치 $ vi /boot/grub/grub.cfg • I finished installing 선택후 재부팅
커널 컴파일 환경 구축 1 • 사용자로 로그인 후 root 부팅 설정 • $ su • $ vi /etc/pam.d/gdm3 • #auth require pam_succeed_if.so user != root quiet_success 라인을 /*주석처리*/ • root 로그인 • hello.c코딩 & 컴파일 • 유틸리티 설치 • # apt-getinstall vim • # apt-getinstall gcc • # apt-getinstall make • # apt-getinstall ctags
커널 컴파일 환경 구축 2 • 커널소스 설치 • http://www.kernel.org • ftp://ftp.kernel.org/pub • ftp://ftp.kernel.org/pub/linux/kernel/v3.x/ • linux-3.2.37 커널 소스 다운로드 후 /usr/src로 이동 (xz) $ unxz linux-3.2.37.tar.xz $ tar xvf linux-3.3.37.tar.xz (gz) $ tar xvfzlinux-3.3.37.tar.gz • 커널 버전의 의미 2. 6. 32- 71. el6. i686 <Major><Minor><Build><Revision><Version><Architecture> • <Major> 커널에 커다란 변화가 있을 때 바뀌는 번호 • <Minor> 짝수일 때 안정 버전, 홀수일 때 개발버전 • <Build> 패치 레벨을 나타내는 숫자 • <Revision> 패키지개정 횟수를 나타내는 숫자 • <Version> 패키지 버전 (예) el6 : Enterprise Linux (Red Hat Enterprise Linux/CentOS) • <Architecture> 프로세서 (예) i686 : Intel Pentiumprocessor (= x86 microarchitecture)
커널 컴파일 환경 구축 3 • 사용자로 로그인 후 root 부팅 설정 • $ su • $ vi /etc/pam.d/gdm3 • #auth require pam_succeed_if.so user != root quiet_success /*주석처리*/ gdm : GNOME Display Manager pam_succeed_if.so : 인증 계정 테스트 quiet : 시스템로그에 기록하지 않음 • root 로그인 • 쉘 환경설정 • $ vi .bashrc • C 프로그래밍 환경 설정 • $ vihello.c • $ gcchello.c–o hello (vi 환경 설정) $ vi /root/.vimrc set tabstop=2 set autoindent syntax off $ cat hello.c #include <stdio.h> main() { printf("Hello Linux\n"); }
커널 컴파일 환경 구축 3 • 커널소스 설치 • http://www.kernel.org • ftp://ftp.kernel.org/pub • ftp://ftp.kernel.org/pub/linux/kernel/v3.x/ • linux-3.2.37 커널 소스 다운로드 후 /usr/src로 이동 (xz) $ unxz linux-3.2.37.tar.xz $ tar xvf linux-3.3.37.tar.xz (gz) $ tar xvfzlinux-3.3.37.tar.gz • 태그생성 • # apt-getinstall ctags • # ctags –R • # vi –t task_struct (반드시 tags 파일있는 곳에서 실행) • Control-] 로 structuredefinition 탐색 이동
Windows와 VMware 사이 파일공유하려면 • VMware Tools 설치 • VM VMware Tools Install 선택 • 위치 cdrom0 VMwareTools-9.6.2-1688356.tar.gz 을/root에복사 (또는) • # mount –t iso9660 –o ro /dev/cdrom /mnt • # cp /mnt/VMwareTools-9.6.2-1688356.tar.gz /root • # cd /root • # tar xvfz VMwareTools-9.6.2-1688356.tar.gz • # cd vmware-tools-distrib • # perl vmware-install.pl (잘읽어보며 default 선택) • (구성수정) # perl bin/vmware-config-tools.pl • # lsmod • VM Settings Options Tab Shared Folders Add • Edit Preferences Shared VMs Enable Sharing
한글이 깨질 때 • LANG=ko_KR.UTF-8 ; export LANG • # dpkg-reconfigure locales • [*] ko_KR.EUC-KR EUC-KR • [*] ko_KR.UTF-8 UTF-8 • 기본로케일 ko_KR.UTF-8 설정
replace append insert open esc esc esc esc a A i I R oO $vi filename ~ ~ vi mode(vi 명령 모드) ~ $ :q! (기록 않음) ZZ(기록) :wq :x x dd r 커서이동 Vi 편집 개념 H J K L
make • 컴파일과정을 자동화 • Makefile target: dependencies [tab] system command (규칙) (반드시 tab 선행) • (예1) all: gcchello.c–o hello • (예2) CC = /usr/bin/gcc CFLAGS = -g all: hellohello2 hello: hello.cdummy.c $(CC) $(CFLAGS) $^ -o $@ hello2: $(CC) $(CFLAGS) $^ -o $@ • 매크로 사용 • CC : 컴파일러 • $^ : dependencies 대체 • $@ : target대체 • CFLAGS : 컴파일옵션
(참고) 다중 모듈 프로그램 • 단일 모듈 프로그램 • 코드의 재사용(reuse)이 어렵고, • 여러 사람이 참여하는 프로그래밍이 어렵다 • 예를 들어 다른 프로그램에서 copy 함수를 재사용하기 힘들다 • 다중 모듈 프로그램 • 여러 개의 .c 파일들로 이루어진 프로그램 • 일반적으로 복잡하며 대단위 프로그램인 경우에 적합
(참고) 다중 모듈 프로그램: main & copy • main 프로그램과 copy 함수를 분리하여 별도 파일로 작성 • main.c • copy.c • copy.h // 함수의 프로토타입을 포함하는 헤더 파일 • 컴파일 $ gcc -c main.c $ gcc -c copy.c $ gcc -o main main.ocopy.o 혹은 $ gcc -o main main.ccopy.c
main.ccopy.c #include <stdio.h> #include <string.h> #include "copy.h" char line[MAXLINE]; // 입력 줄 char longest[MAXLINE]; // 가장 긴 줄 /*입력 줄 가운데 가장 긴 줄 프린트 */ main() { intlen; int max; max = 0; while (fgets(line, MAXLINE, stdin) != NULL) { len = strlen(line); if (len > max) { max = len; copy(line, longest); } } if (max > 0) // 입력 줄이 있었다면 printf("…The logest…\n%s\n", longest); return 0; } #include "copy.h" /* copy: from을 to에 복사; to가 충분히 크다고 가정*/ void copy(char from[], char to[]) { inti; i = 0; while ((to[i] = from[i]) != '\0') ++i; } copy.h #define MAXLINE 100 void copy(char from[], char to[]);
(참고) make 시스템의 필요성 • 다중 모듈 프로그램을 구성하는 일부 파일이 변경된 경우? • 변경된 파일만 컴파일하고, 파일들의 의존 관계에 따라서 필요한 파일만 다시 컴파일하여 실행 파일을 만들면 좋다 • 예 • copy.c소스 코드를 수정 • 목적 파일 copy.o 생성 • 실행파일을 생성 • make 시스템 • 대규모 프로그램의 경우에는 헤더, 소스 파일, 목적 파일, 실행 파일의 모든 관계를 기억하고 체계적으로 관리하는 것이 필요 • make 시스템을 이용하여 효과적으로 작업
(참고) 메이크파일(Makefile) • 메이크파일 • 실행 파일을 만들기 위해 필요한 파일들과 그들 사이의 의존 관계, 만드는 방법 등을 기술 • make 시스템은 메이크파일을 이용하여 파일의 상호 의존 관계를 파악하여 실행 파일을 쉽게 다시 만듦 • $ make [-f 메이크파일] • 옵션이 없으면 Makefile혹은 makefile을 사용
(참고) 메이크파일의 구성 • 메이크파일의 구성 형식 대상리스트: 의존리스트 명령리스트 (반드시 tab으로시작) • 메이크파일예 $ vi makefile main: main.ocopy.o gcc -o main main.ocopy.o main.o: main.ccopy.h gcc -c main.c copy.o: copy.ccopy.h gcc -c copy.c $ make–f makefile • 매크로(Macros) • make에서 지원하는 기능: make 파일 내에 모든 $(token)는 replacementText로 대치 • token = replacementText • 규칙 .c.o: $(CC) $(CFLAGS) $(CPPFLAGS) -c $< CFLAGS = -O -g -p -O (optimize) -g (gdb를 위한 디버깅 정보 포함) -p (모든 매크로 정의 출력) • 그 외 정보들: $ man make • make 실행 $ make 혹은 $ make main gcc -c main.c gcc -c copy.c gcc -o main main.ocopy.o • copy.c파일이 변경된 후 $ touch copy.c $ make gcc -c copy.c gcc -o main main.ocopy.o
Cvs. Assembly $ cat helloc.c #include <unistd.h> int main(void) { write(1, "Hello, Kernel\n", 14); return 0; } $ gcchelloc. -o helloc $ ./helloc $ cat helloa.asm section .data hello: db 'hello, kernel', 10 helloLen: equ $-hello section .text global _start _start: moveax, 4 movebx, 1 movecx, hello movedx, helloLen int80h moveax, 1 movebx, 0 int80h $nasm –f asf hello.asm $ld –s –o helloa ./helloa
커널 컴파일 • /usr/src/ulinux-3.2.37 위치 • # makemrproper • # make menuconfig • 오류 나면 ncurses* 설치 • $ apt-get install ncurses* • # make • # make modules_install • (확인) # ls –l arch/x86/ • # make install • # vi /boot/grub/grub.cfg /* Windowstitle 편집 */ • # reboot
부트 로더보수 • 내 커널 버전 로그인 위한 grub 구성 파일생성 (SCSI disk) # update-grub /dev/sda또는 # grub-install /dev/sda (IDE disk) # update-grub /dev/had 또는 # grub-install /dev/hda • 다른수업 위해 실습실 부트 로더 정리 /* os.sh */ #!/bin/sh sudo mount /dev/sda7 /mnt sudo cp /mnt/boot/grub/grub.cfg /boot/grub/grub.cfg sudoumount /mnt sudo grub-install /dev/sda # chmod +x os.sh # os.sh • 하드디스크 파티션 정보 • $ fdisk –l • $ sudofdisk –l • $ lsblk • http://www.cyberciti.biz/
나만의시스템 호출newsyscall(349) • (32-bit) $ cd/usr/src/ulinux-3.2.37 • # vi /usr/src/linux-3.2.37/arch/x86/include/asm/unistd_32.h • #define __NR_newsyscall 349 • #define NR_syscalls 350 • # vi /usr/include/i386-linux-gnu/asm/unistd_32.h • #define __NR_newsyscall 349 • # vi /usr/src/linux-3.2.37/arch/x86/kernel/syscall_table_32.S • .long sys_newsyscall마지막라인에 추가 • # vi /usr/src/linux-3.2.37/include/linux/syscalls.h • asmlinkagelong sys_newsyscall(); • # vi /usr/src/linux-3.2.37kernel/newsyscall.c • /* 커널 코드 /usr/src/mylinux/kernel/newsyscall.c코딩*/ • # vi /usr/src/linux-3.2.37/kernel/Makefile수정 • obj -y = 라인 마지막에 newsyscall.o추가 • # cd /usr/src/linux-3.2.37 • # make bzImage • # cp arch/x86/boot/bzImage /boot/vmlinuz-3.2.37 • # vi /boot/grub.cfg부트로더 검토 • # reboot $ gcc test1.c –o test1 $ ./test1 $ dmesg | tail /* 커널 코드 /usr/src/mylinux/kernel/newsyscall.c코딩*/ #include <linux/linkage.h> #include <linux/kernel.h> asmlinkageintsys_newsyscall() { printk("Hello Linux Kernel byMYSung\n"); return 0; } /*사용자수준 응용 프로그램 test1.c*/ #include <linux/unistd.h> #include <stdio.h> main(void) { inti; i=syscall(__NR_newsyscall); printf("__NR_newsyscall = %d\n", __NR_newsyscall); printf("syscall return value = %d\n", i); return 0; }
나만의시스템 호출 newsysadd(350) • (32-bit) $ cd/usr/src/ulinux-3.2.37 • # vi /usr/src/linux-3.2.37/arch/x86/include/asm/unistd_32.h • #define __NR_newsysadd 350 • #define NR_syscalls 351 • # vi /usr/include/i386-linux-gnu/asm/unistd_32.h • #define __NR_newsysadd 350 • # vi /usr/src/linux-3.2.37/arch/x86/kernel/syscall_table_32.S • .long sys_newsysadd마지막라인에 추가 • # vi /usr/src/linux-3.2.37/include/linux/syscalls.h • asmlinkageintsys_newsysadd(int a, int b, int *to_user) • # vi /usr/src/linux-3.2.37kernel/newsysadd.c • /* 커널 코드 /usr/src/mylinux/kernel/newsysadd.c코딩*/ • # vi /usr/src/linux-3.2.37/kernel/Makefile수정 • obj -y = 라인 마지막에 newsysadd.o추가 • # cd /usr/src/linux-3.2.37 • # make bzImage • # cp arch/x86/boot/bzImage /boot/vmlinuz-3.2.37 • # vi /boot/grub.cfg부트로더 검토 • # reboot $ gcctest2.c –o test2 $ ./test2 $ dmesg | tail /*커널 코드 /usr/src/mylinux/kernel/newsysadd.c코딩*/ #include <linux/linkage.h> /*asmlinkage*/ #include <linux/kernel.h> /*printk*/ #include <../arch/x86/include/asm/uaccess.h> /*put_user*/ asmlinkageintsys_newsysadd(int a, int b, int *to_user) { int sum = 0; printk("(Kernel Message) a = %d, b = %d\n", a, b); sum = a + b; put_user(sum, to_user); return 0; } /*사용자수준 응용 프로그램test2.c*/ #include <linux/unistd.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> main(void) { inti, a = 100, b = 200, from_kernel; i=syscall(__NR_newsysadd, a, b, &from_kernel); printf("from_kernel = %d\n", from_kernel); printf("syscall return value = %d\n", i); printf("__NR_newsysadd = %d\n", __NR_newsysadd); return 0; }
나만의시스템 호출 getcpuinfo(351) /* /usr/src/linux-3.2.37/arch/x86/include/asm/processor.h*/ struct cpuinfo_x86 { __u8 x86; /* CPU family */ __u8 x86_vendor; /* CPU vendor */ __u8 x86_model; __u8 x86_mask; #ifdef CONFIG_X86_32 char wp_works_ok; /* It doesn't on 386's */ /* Problems on some 486Dx4's and old 386's: */ char hlt_works_ok; char hard_math; char rfu; char fdiv_bug; char f00f_bug; char coma_bug; char pad0; #else /* Number of 4K pages in DTLB/ITLB combined(in pages): */ int x86_tlbsize; #endif __u8 x86_virt_bits; __u8 x86_phys_bits; __u8 x86_phys_bits; /* CPUID returned core id bits: */ __u8 x86_coreid_bits; /* Max extended CPUID function supported: */ __u32 extended_cpuid_level; /* Maximum supported CPUID level, -1=no CPUID: */ intcpuid_level; __u32 x86_capability[NCAPINTS]; char x86_vendor_id[16]; char x86_model_id[64]; /* in KB - valid for CPUS which support this call: */ int x86_cache_size; int x86_cache_alignment; /* In bytes */ int x86_power; unsigned long loops_per_jiffy; #ifdef CONFIG_SMP /* cpus sharing the last level cache: */ cpumask_var_tllc_shared_map; #endif /* cpuid returned max cores value: */ u16 x86_max_cores; u16 apicid; u16 initial_apicid; u16 x86_clflush_size; #ifdef CONFIG_SMP /* number of cores as seen by the OS: */ u16 booted_cores; /* Physical processor id: */ u16 phys_proc_id; /* Core id: */ u16 cpu_core_id; /* Compute unit id */ u8 compute_unit_id; /* Index into per_cpu list: */ u16 cpu_index; #endif } __attribute__((__aligned__(SMP_CACHE_BYTES))); • (32-bit) $ cd/usr/src/ulinux-3.2.37 • # vi /usr/src/linux-3.2.37/arch/x86/include/asm/unistd_32.h • #define __NR_getcpuinfo 351 • #define NR_syscalls352 • # vi /usr/include/i386-linux-gnu/asm/unistd_32.h • #define __NR_getcpuinfo 352 • # vi /usr/src/linux-3.2.37/arch/x86/kernel/syscall_table_32.S • .long sys_getcpuinfo마지막라인에 추가 • # vi /usr/src/linux-3.2.37/include/linux/syscalls.h • asmlinkage void sys_getcpuinfo(structcpu_info *info) • # vi /usr/src/linux-3.2.37kernel/getcpuinfo.c • /* 커널 코드 /usr/src/mylinux/kernel/getcpuinfo.c코딩*/ • # vi /usr/src/linux-3.2.37/kernel/Makefile수정 • obj-y = 라인 마지막에getcpuinfo.o추가 • # cd /usr/src/linux-3.2.37 • # make bzImage • # cp arch/x86/boot/bzImage /boot/vmlinuz-3.2.37 • # vi /boot/grub.cfg부트로더 검토 • # reboot $ gcctest3.c –o test3 $ ./test3 $ dmesg | tail $ cat /proc/cpuinfo • 커널에 항상struct cpuinfo_x86 데이터를 유지하고 있음 /usr/src/linux-3.2.37/arch/x86/include/asm/processor.h • (참고) $ cat /proc/cpuinfo /usr/src/linux-3.2.37/arch/x/ke86rnel/cpu/proc.c
/* 커널 코드 /usr/src/mylinux/kernel/getcpuinfo.c*/ #include <linux/kernel.h> #include <asm/processor.h> #include <asm/uaccess.h> structcpu_info { char vendor_id[16]; char model_id[64]; intcache_size; }; asmlinkage void sys_getcpuinfo(structcpu_info *info) { structcpu_infosrc; sprintf(src.vendor_id, "%s", boot_cpu_data.x86_vendor_id); sprintf(src.model_id, "%s", boot_cpu_data.x86_model_id); src.cache_size = boot_cpu_data.x86_cache_size; copy_to_user(info, &src, sizeof(structcpu_info)); } /*사용자수준 응용 프로그램test3.c*/ #include <linux/unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> structcpu_info { char vendor_id[16]; char model_id[64]; intcache_size; }; intmain(intargc, char* argv[]) { inti; structcpu_info info; i = syscall(__NR_getcpuinfo, &info); printf("Vendor id: %s\n", info.vendor_id); printf("Model id: %s\n", info.model_id); printf("Cache size in KB: %d\n", info.cache_size); return 0; }
나만의시스템 호출 getstat(352) • (32-bit) $ cd/usr/src/ulinux-3.2.37 • # vi /usr/src/linux-3.2.37/arch/x86/include/asm/unistd_32.h • #define __NR_getstat 352 • #define NR_syscalls 353 • # vi /usr/include/i386-linux-gnu/asm/unistd_32.h • #define __NR_getstat 353 • # vi /usr/src/linux-3.2.37/arch/x86/kernel/syscall_table_32.S • .long sys_getstat마지막라인에 추가 • # vi /usr/src/linux-3.2.37/include/linux/syscalls.h • asmlinkageintsys_getstat(int id, structmystat *user_buf) • # vi /usr/src/linux-3.2.37kernel/getstat.c • /* 커널 코드 /usr/src/linux-3.2.37/kernel/getstat.c코딩*/ • # vi /usr/src/linux-3.2.37/kernel/Makefile수정 • obj -y = 라인 마지막에 getstat.o추가 • # cd /usr/src/linux-3.2.37 • # make bzImage • # cp arch/x86/boot/bzImage /boot/vmlinuz-3.2.37 • # vi /boot/grub.cfg부트로더 검토 • # reboot $ vi mystat,h gcc test4.c –o test4 $ ./test4 $ dmesg | tail /* 헤더 파일 mystat.h */ structmystat { intpid; intppid; /* * pid_tpid; * pid_tppid; */ int state; int priority; int policy; long utime; long stime; long starttime; unsigned long min_flt; unsigned long maj_flt; intopen_files; };
/* 커널 코드 /usr/src/mylinux/kernel/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> asmlinkageintsys_getstat(intid, structmystat *user_buf) { structmystat *buf; structtask_struct *search; search = &init_task; while(search->pid != id) { search = list_entry((search)->tasks.next, structtask_struct, tasks); if(search->pid == init_task.pid) return(-1); } buf = kmalloc(sizeof(structmystat), 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(structmystat)); return 0; } /* 사용자 수준 응용 프로그램 test4.c */ #include <linux/unistd.h> #include <stdio.h> #include <errno.h> #include "mystat.h" #include <stdlib.h> structmystat *mybuf; int main(intargc, char* argv[]) { inttask_number, i; if(argc != 2) { printf("USAGE: a.outpid\n"); exit(1); } task_number = atoi(argv[1]); mybuf = (structmystat *)malloc(sizeof(structmystat)); if(mybuf == NULL) { printf("Out of Memory\n"); exit(1); } printf("PID %d\n",task_number); i = syscall(__NR_getstat, task_number, mybuf); printf(“__NR_getstat=%d, return value=%d\n", __NR_getstati); 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 = %u\n", mybuf->open_files); return 0; }
next_task prev_task next_task prev_task prev_task init_task … task_struct task_struct 태스크 리스트 • current (/usr/src/kernels/mylinux/arch/x86/include/asm/current.h 17행) 전역 변수가task_struct (/usr/src/mylinux/include/linux/sched.h : 1167행) 자료구조를 포인팅
fork() 시스템 호출의 흐름 Hanbit Media(c)