Kernel Programming Kernel VMware 설치 Debian 7 설치 커널 컴파일 newsyscall
Chapter 1 리눅스 커널 프로그래밍 환경 구축 MeeYoung Sung Cmputer Science & Engineering Incheon National University
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 선택 후 재부팅
Chapter 2 커널 컴파일 및 개발 환경 구축
커널 컴파일 환경 구축 1 사용자로 로그인 후 root 부팅 설정 유틸리티 설치 $ su $ vi /etc/pam.d/gdm3 #auth require pam_succeed_if.so user != root quiet_success 라인을 /*주석 처리*/ root 로그인 hello.c 코딩 & 컴파일 유틸리티 설치 # apt-get install vim # apt-get install gcc # apt-get install make # apt-get install 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 xvfz linux-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 Pentium processor (= 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 프로그래밍 환경 설정 $ vi hello.c $ gcc hello.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.2.37.tar.xz (gz) $ tar xvfz linux-3.2.37.tar.gz 태그 생성 # apt-get install ctags # ctags –R # vi –t task_struct (반드시 tags 파일 있는 곳에서 실행) Control-] 로 structure definition 탐색 이동
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 설정
$ Vi 편집 개념 a A i I esc oO R ~ ~ vi mode(vi 명령 모드) $vi filename :q! (기록 않음) ZZ(기록) :wq :x a A i I esc x r dd 커서이동 append insert oO open R replace H J K L
make 컴파일 과정을 자동화 Makefile 매크로 사용 CC : 컴파일러 target: dependencies [tab] system command (규칙) (반드시 tab 선행) (예1) all: gcc hello.c –o hello (예2) CC = /usr/bin/gcc CFLAGS = -g all: hello hello2 hello: hello.c dummy.c $(CC) $(CFLAGS) $^ -o $@ hello2: 매크로 사용 CC : 컴파일러 $^ : dependencies 대체 $@ : target 대체 CFLAGS : 컴파일 옵션
(참고) 다중 모듈 프로그램 단일 모듈 프로그램 다중 모듈 프로그램 코드의 재사용(reuse)이 어렵고, 여러 사람이 참여하는 프로그래밍이 어렵다 예를 들어 다른 프로그램에서 copy 함수를 재사용하기 힘들다 다중 모듈 프로그램 여러 개의 .c 파일들로 이루어진 프로그램 일반적으로 복잡하며 대단위 프로그램인 경우에 적합
(참고) 다중 모듈 프로그램: main & copy copy.c copy.h // 함수의 프로토타입을 포함하는 헤더 파일 컴파일 $ gcc -c main.c $ gcc -c copy.c $ gcc -o main main.o copy.o 혹은 $ gcc -o main main.c copy.c
main.c copy.c #include <stdio.h> #include <string.h> #include "copy.h" char line[MAXLINE]; // 입력 줄 char longest[MAXLINE]; // 가장 긴 줄 /*입력 줄 가운데 가장 긴 줄 프린트 */ main() { int len; 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[]) { int i; i = 0; while ((to[i] = from[i]) != '\0') ++i; } copy.h #define MAXLINE 100 void copy(char from[], char to[]);
(참고) make 시스템의 필요성 다중 모듈 프로그램을 구성하는 일부 파일이 변경된 경우? 예 make 시스템 변경된 파일만 컴파일하고, 파일들의 의존 관계에 따라서 필요한 파일만 다시 컴파일하여 실행 파일을 만들면 좋다 예 copy.c 소스 코드를 수정 목적 파일 copy.o 생성 실행파일을 생성 make 시스템 대규모 프로그램의 경우에는 헤더, 소스 파일, 목적 파일, 실행 파일의 모든 관계를 기억하고 체계적으로 관리하는 것이 필요 make 시스템을 이용하여 효과적으로 작업
(참고) 메이크파일(Makefile) 메이크파일 $ make [-f 메이크파일] 실행 파일을 만들기 위해 필요한 파일들과 그들 사이의 의존 관계, 만드는 방법 등을 기술 make 시스템은 메이크파일을 이용하여 파일의 상호 의존 관계를 파악하여 실행 파일을 쉽게 다시 만듦 $ make [-f 메이크파일] 옵션이 없으면 Makefile 혹은 makefile을 사용
(참고) 메이크파일의 구성 메이크파일의 구성 형식 대상리스트: 의존리스트 명령리스트 (반드시 tab으로 시작) 메이크파일 예 $ vi makefile main: main.o copy.o gcc -o main main.o copy.o main.o: main.c copy.h gcc -c main.c copy.o: copy.c copy.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.o copy.o copy.c 파일이 변경된 후 $ touch copy.c $ make
C vs. Assembly $ cat helloa.asm section .data hello: db 'hello, kernel', 10 helloLen: equ $-hello section .text global _start _start: mov eax, 4 mov ebx, 1 mov ecx, hello mov edx, helloLen int 80h mov eax, 1 mov ebx, 0 $ nasm –f asf hello.asm $ ld –s –o helloa ./helloa $ cat helloc.c #include <unistd.h> int main(void) { write(1, "Hello, Kernel\n", 14); return 0; } $ gcc helloc. -o helloc $ ./helloc
Chapter 3 리눅스 시스템 호출
커널 컴파일 /usr/src/ulinux-3.2.37 위치 # make mrproper # make menuconfig 오류 나면 ncurses* 설치 $ apt-get install ncurses* # make # make modules_install (확인) # ls –l arch/x86/ # make install # vi /boot/grub/grub.cfg /* Windows title 편집 */ # 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 sudo umount /mnt sudo grub-install /dev/sda # chmod +x os.sh # os.sh 하드디스크 파티션 정보 $ fdisk –l $ sudo fdisk –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 # 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 asmlinkage int 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> asmlinkage int sys_newsyscall() { printk("Hello Linux Kernel by MYSung\n"); return 0; } /*사용자 수준 응용 프로그램 test1.c*/ #include <linux/unistd.h> #include <stdio.h> main(void) int i; i=syscall(__NR_newsyscall); printf("__NR_newsyscall = %d\n", __NR_newsyscall); printf("syscall return value = %d\n", i);
나만의 시스템 호출 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 asmlinkage int sys_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 $ gcc test2.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*/ asmlinkage int sys_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) int i, 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);
나만의 시스템 호출 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; /* 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: */ int cpuid_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_t llc_shared_map; /* cpuid returned max cores value: */ u16 x86_max_cores; u16 apicid; u16 initial_apicid; u16 x86_clflush_size; /* 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; } __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_syscalls 352 # 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(struct cpu_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 $ gcc test3.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> struct cpu_info { char vendor_id[16]; char model_id[64]; int cache_size; }; asmlinkage void sys_getcpuinfo(struct cpu_info *info) { struct cpu_info src; 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(struct cpu_info)); } /*사용자 수준 응용 프로그램 test3.c*/ #include <linux/unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> struct cpu_info { char vendor_id[16]; char model_id[64]; int cache_size; }; int main(int argc, char* argv[]) { int i; struct cpu_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) /* 헤더 파일 mystat.h */ struct mystat { int pid; (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 asmlinkage int sys_getstat(int id, struct mystat *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 */ 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; };
/* 커널 코드 /usr/src/mylinux/kernel/getstat.c 코딩*/ /* 사용자 수준 응용 프로그램 test4.c */ #include <linux/unistd.h> #include <stdio.h> #include <errno.h> #include "mystat.h" #include <stdlib.h> struct mystat *mybuf; int main(int argc, char* argv[]) { int task_number, i; 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"); 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; /* 커널 코드 /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> asmlinkage int sys_getstat(int id, struct mystat *user_buf) { struct mystat *buf; 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) 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;
태스크 리스트 current (/usr/src/kernels/mylinux/arch/x86/include/asm/current.h 17행) 전역 변수가task_struct (/usr/src/mylinux/include/linux/sched.h : 1167행) 자료구조를 포인팅 init_task prev_task next_task … task_struct
fork() 시스템 호출의 흐름 2018-11-13 Hanbit Media(c)
(참고) 리눅스 방화벽 관리 방화벽 정지 방화벽 시작 # /sbin/service iptables stop 또는 # /etc/init.d/iptables stop 21번 (ftp) 포트 개방 # iptables -I INPUT 1 -p tcp --dport 21 -j ACCEPT /*I(아이)*/ # iptables -I OUTPUT 1 -p tcp -–dport 21 -j ACCEPT /*I(아이)*/ 22번 (ssh) 포트 개방 # iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT /*I(아이)*/ # iptables -I OUTPUT 1 -p tcp -–dport 22 -j ACCEPT /*I(아이)*/ 23번 (telnet) 포트 개방 # iptables -I INPUT 1 -p tcp --dport 23 -j ACCEPT /*I(아이)*/ # iptables -I OUTPUT 1 -p tcp --dport 23 -j ACCEPT /*I(아이)*/ 방화벽 시작 # /sbin/service iptables start 또는 # /etc/init.d/iptables start
Chapter 4 모듈 프로그래밍
모듈 개발 절차 1. 모듈 프로그램 작성 2. 모듈 프로그램 컴파일 3. 모듈 로드(insmod 명령) 4. 로드된 모듈 확인(lsmod 명령) 5. 모듈 제거(rmmod 명령) # dmesg | tail 확인 커널 버전에 맞추어 모듈 빌드 # cat /usr/src/linux-3.2.37/include/linux/version.h #define LINUX_VERSION_CODE 197157 #define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) 2018-11-13 Hanbit Media(c)
hello 모듈 작성 (32-bit) $ cd /root # vi hello.c # vi Makefile # make #include <linux/init.h> #include <linux/module.h> int __init init_hello(void) { printk( KERN_ALERT "[ Module Message ] Hello, Module.\n" ); return 0; } void __exit exit_hello(void) printk( KERN_ALERT "[ Module Message ] Do you really want to break up with me?\n" ); module_init( init_hello ); module_exit( exit_hello ); MODULE_LICENSE( "GPL" ); /* Makefile 코딩 */ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = hello.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o (32-bit) $ cd /root # vi hello.c # vi Makefile # make # lsmod # insmod hello.ko # rmmod hello # dmesg | tail # grep hello /proc/kallsyms $(shell) function in Makefile calls out to the shell to execute a command
callee 모듈 작성 (32-bit) $ cd /root # vi callee.c # vi caller.c # vi Makefile # make # lsmod # insmod callee.ko # insmod caller.ko # rmmod caller.ko # dmesg | tail # grep add /proc/kallsyms # grep sub /proc/kallsyms /* Makefile 코딩 */ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = callee.o caller.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o
/* callee.c 코딩*/ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> int __init init_callee(void) { return 0; } void __exit exit_callee(void) int add( int a, int b ) printk( KERN_ALERT "[callee Module] add called...\n" ); return a + b; int sub( int a, int b ) printk( KERN_ALERT "[callee Module] sub called...\n" ); return a - b; EXPORT_SYMBOL(add); EXPORT_SYMBOL(sub); module_init( init_callee ); module_exit( exit_callee ); MODULE_LICENSE( "GPL" ); /* caller.c 코딩 */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> int add( int, int ); int sub( int, int ); int __init init_caller(void) { printk( KERN_ALERT "[caller Module] I'll call add(), sub() from caller.\n" ); printk( KERN_ALERT "[caller Module] add: %d\n", add( 3, 2 ) ); printk( KERN_ALERT "[caller Module] sub: %d\n", sub( 3, 2 ) ); return 0; } void __exit exit_caller(void) module_init( init_caller ); module_exit( exit_caller ); MODULE_LICENSE( "GPL" );
parm 모듈 작성 (32-bit) $ cd /root # vi parm3.c # vi parm4.c # vi Makefile # lsmod # insmod parm3.ko # insmod parm4.ko # rmmod parm3.ko # rmmod parm4.ko # insmod parm3.ko a=100 str=hello # insmod parm4.ko b=11,22,33,44,55 # dmesg | tail # grep param /proc/kallsyms /* Makefile 코딩 */ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = parm1.o parm2.o parm3.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o
/* [2.4] parm1.c 코딩*/ /* [2.4] parm2.c 코딩 */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> int a; // 변수 a를 선언 MODULE_PARM( a, "i" ); // 변수 a를 int 형 커널 매개변수로 사용 // 초기화 루틴 int __init init_param( void ) { printk( KERN_ALERT "[Module Message] parameter a = %d\n", a ); return 0; } // 종료 루틴 void __exit exit_param( void ) module_init( init_param ); module_exit( exit_param ); MODULE_LICENSE( "GPL" ); /* [2.4] parm2.c 코딩 */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> int a; // 변수 a를 선언 char *str; MODULE_PARM( a, "i" ); // 변수 a를 int 형 커널 매개변수로 사용 MODULE_PARM( str, "s" ); int __init init_param( void ) { printk( KERN_ALERT "[Module Message] parameter a = %d\n", a ); printk( KERN_ALERT "[Module Message] parameter str = %s\n", str ); return 0; } void __exit exit_param( void ) module_init( init_param ); module_exit( exit_param ); MODULE_LICENSE( "GPL" );
/* [2.6] parm3.c 코딩 */ /* [2.6] parm4.c 코딩*/ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> int a; char *str; module_param( a, int, 0 ); // module_param(name, type, perm) module_param( str, charp, 0 ); int __init init_param( void ) { printk( KERN_ALERT "[Module Message] parameter a = %d\n", a ); printk( KERN_ALERT "[Module Message] parameter str = %s\n", str ); return 0; } void __exit exit_param( void ) module_init( init_param ); module_exit( exit_param ); MODULE_LICENSE( "GPL" ); /* [2.6] parm4.c 코딩*/ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> int b[] = { 1, 2, 3, 4, 5 }; module_param_array( b, int, NULL, 0 ); // module_param_array(name, type, nump, perm); int __init init_param( void ) { printk( KERN_ALERT "[Module Message] %d %d %d %d %d\n", b[0], b[1], b[2], b[3], b[4] ); return 0; } void __exit exit_param( void ) module_init( init_param ); module_exit( exit_param ); MODULE_LICENSE( "GPL" );
wrapping 모듈 작성 (32-bit) $ cd /root # vi wrapping.c # vi Makefile # lsmod # insmod wrapping.ko # dmesg | tail # grep wrapping /proc/kallsyms Debbugging 필요 /* Makefile 코딩 */ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m =wrapping.o KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o
< Hacks > # depmod –an 모듈 디렉터리를 자동 검색하여 모듈 의존성 설정 /* [수정본] wrapping.c 코딩*/ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/syscalls.h> void **sys_call_table; asmlinkage int (*org_sys_open)( const char*, int, int ); asmlinkage int (*org_sys_getuid)( void ); asmlinkage int sys_our_open( char *fname, int flags, int mode ) { printk( KERN_ALERT "%s file is opened by %d\n", fname, org_sys_getuid()); return (org_sys_open( fname, flags, mode )); } static void **find_system_call_table( void ) unsigned long ptr; extern int loops_per_jiffy; unsigned long *p; for ( ptr = ( unsigned long )&loops_per_jiffy; ptr < ( unsigned long )&boot_cpu_data ; ptr += sizeof( void* ) ) p = ( unsigned long * )ptr; if ( p[ 6 ] == ( unsigned long ) sys_close ) return (void**)p; return NULL; int __init sct_init( void ) sys_call_table = find_system_call_table(); if( sys_call_table != NULL ) printk( KERN_ALERT "I think sys_call_table is at 0x%p\n", (void*)sys_call_table ); org_sys_open = sys_call_table[ __NR_open ]; sys_call_table[ __NR_open ] = sys_our_open; org_sys_getuid = sys_call_table[ __NR_getuid ]; else printk( KERN_ALERT "Failed to find sys_call_table\n" ); return 0; void __exit sct_exit( void ) sys_call_table[ __NR_open ] = org_sys_open; module_init( sct_init ); module_exit( sct_exit ); MODULE_LICENSE( "GPL" ); < Hacks > # depmod –an 모듈 디렉터리를 자동 검색하여 모듈 의존성 설정 모듈이 존재하지 않는 디렉터리에서도 로드 가능하게 지원 insmod는 모듈이 존재하는 데렉터리에서만 로드 가능 (예) callee와 caller를 어디에서나 로드 # mkdir /lib/modules/3.2.37/misc # cp callee.ko /lib/modules/3.2.37/misc # cp caller.ko /lib/modules/3.2.37/misc # depmod –an /* 반드시 –a 포함 */ # lsmod # cd # modprobe callee # modprobe caller # ls What the Hack ! A clever solution to a tricky problem
Chapter 5 문자 디바이스 프로그래밍
디바이스 드라이버 하드웨어에 대한 추상화를 제공 리눅스에서는 가상 파일 시스템(VFS)을 사용하여 하드웨어 추상화를 지원 2018-11-13 Hanbit Media(c)
디바이스 드라이버= 파일 문자 디바이스 # ls –l /dev | more crw-------. 1 root root 5, 1 2014-09-01 14:06 console (system console) crw-r-----. 1 root kmem 1, 1 2014-09-01 14:06 mem (physical memory) crw-r-----. 1 root root 10, 1 2014-09-01 14:06 psaux (PS/2 mouse) 블록 디바이스 # ls –l /dev hda* brw-rw----. 1 root disk 8, 0 2014-09-24 19:29 /dev/sda brw-rw----. 1 root disk 8, 1 2014-09-01 14:07 /dev/sda1 brw-rw----. 1 root disk 8, 2 2014-09-01 14:07 /dev/sda2 (활용) 윈도우즈 파티션 백업 # cat /dev/hda > windowsbackup # grub-install /dev/hda (부트 로더 복구 명령) C:\> fdisk /mbr
(참고) Linux Disk Drive Naming Old IDE = Parallel ATA(Advanced Technology Attachment) Naming /dev/hda: the first drive (master) on the first IDE controller /dev/hdb: the second drive (slave) on the first IDE controller /dev/hdc: the fist (master) drive on the second IDE controller /dev/hdd: the second (slave) drive on the second IDE controller SATA = Serial ATA(Advanced Technology Attachment) controller /dev/sda: the first drive (master) on the first SATA controller /dev/sdb: the second drive (slave) on the first SATA controller /dev/sdc: the fist (master) drive on the second SATA controller /dev/sdd: the second (slave) drive on the second SATA controller
디바이스 드라이버 함수 디바이스 번호 # cat /proc/devices 디바이스 드라이버는 모듈로 개발 why ? Major 8-bit 256개 (0~255) Minor 8-bit 256개 (0~255) 디바이스 드라이버는 모듈로 개발 why ? 디바이스 드라이버 기본 함수 정의 struct file_opeations { … }; 커널 소스에서 파일 기본 함수 정의 소스 코드를 찾아보세요 !
리눅스의 디바이스 드라이버 구성 2018-11-13 Hanbit Media(c)
문자 디바이스 개발 과정 2018-11-13 Hanbit Media(c)
각 함수의 호출 관계 2018-11-13 Hanbit Media(c)
struct file_operations struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t*, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); };
문자 디바이스 드라이버의 구조 2018-11-13 Hanbit Media(c)
virtual_device1 작성 (32-bit) $ cd /root # vi virtual_device1.c # vi virtual_app1.c # vi Makefile # make # insmod virtual_device1.ko # mknod /dev/virtual_device c 250 0 # gcc virtual_app1.c –o virtual_app1 # ./virtual_app1 # dmesg | tail /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = virtual_device1.o TARGETS = virtual_app1 KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: ${TARGETS} $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules CC := /usr/bin/gcc %.c%: ${CC} -o $@ $^ clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o rm -rf .tmp_versions
#include <stdio.h> #include <unistd.h> /* virtual_device1.c */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <asm/uaccess.h> #include <linux/slab.h> static char *buffer = NULL; int virtual_device_open( struct inode *inode, struct file *filp ) { printk( KERN_ALERT "virtual_device open function called\n" ); return 0; } int virtual_device_release( struct inode *inode, struct file *filp ) printk( KERN_ALERT "virtual device release function called\n" ); ssize_t virtual_device_write( struct file *filp, const char *buf, size_t count, loff_t *f_pos ) printk( KERN_ALERT "virtual_device write function called\n" ); memset( buffer, 0, 1024 ); strcpy( buffer, buf ); return count; ssize_t virtual_device_read( struct file *filp, char *buf, size_t count, loff_t *f_pos ) printk( KERN_ALERT "virtual_device read function called\n" ); copy_to_user( buf, buffer, 1024 ); // file operations static struct file_operations vd_fops = { .read = virtual_device_read, /*구조체 초기화*/ .write = virtual_device_write, /*구조체 초기화*/ .open = virtual_device_open, /*구조체 초기화*/ .release = virtual_device_release /*구조체 초기화*/ }; int __init virtual_device_init( void ) if( register_chrdev( 250, "virtual_device", &vd_fops ) < 0 ) printk( KERN_ALERT "driver init failed\n" ); else printk( KERN_ALERT "driver init successful\n"); buffer = (char*) kmalloc( 1024, GFP_KERNEL ); if( buffer != NULL ) void __exit virtual_device_exit( void ) unregister_chrdev( 250, "virtual_device" ); /*unregister_chdev() return void*/ // if( unregister_chrdev( 250, "virtual_device" ) < 0 ) // printk( KERN_ALERT "driver cleanup failed\n" ); // else printk( KERN_ALERT "driver cleanup successful\n" ); kfree( buffer ); sss /* virtual_app1.c */ #include <stdio.h> #include <unistd.h> #include <sys/fcntl.h> int main( int argc, char *argv[] ) { int dev; char buff[1024]; printf( "Device driver test.\n" ); dev = open( "/dev/virtual_device", O_RDWR ); printf( "dev: %d\n", dev ); if( dev < 0 ) printf( "device file open error\n" ); return -1; } write( dev, "1234", 4 ); read( dev, buff, 4 ); printf( "read from device: %s\n", buff ); close( dev ); return 0;
ioctl_device1 작성 (32-bit) $ cd /root # vi ioctl_device1.c # vi ioctl_app1.c # vi Makefile # make # insmod ioctl_device1.ko (# mknod /dev/virtual_device c 250 0) # gcc ioctl_app1.c –o ioctl_app1 # ./ioctl_app1 # dmesg | tail /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = ioctl_device1.o TARGETS = ioctl_app1 KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: ${TARGETS} $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules CC := /usr/bin/gcc %.c%: ${CC} -o $@ $^ clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o rm -rf .tmp_versions
#include <stdio.h> #include <unistd.h> /* ioctl_device1.c */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> int virtual_device_open( struct inode *inode, struct file *filp ) { printk( KERN_ALERT "virtual_device open function called\n" ); return 0; } int virtual_device_release( struct inode *inode, struct file *filp ) printk( KERN_ALERT "virtual device release function called\n" ); int virtual_device_ioctl( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) switch( cmd ) case 1: /* read */ printk( KERN_ALERT "ioctl read...\n" ); break; case 2: /* write */ printk( KERN_ALERT "ioctl write...\n" ); default: printk( KERN_ALERT "ioctl unknown command...\n" ); // file operations static struct file_operations vd_fops = { .owner = THIS_MODULE, /*구조체 초기화*/ .open = virtual_device_open, /*구조체 초기화*/ .release = virtual_device_release, /*구조체 초기화*/ .unlocked_ioctl = virtual_device_ioctl /*커널 2.6.3 이후 unlocked_ioctl로 바뀜*/ }; int __init virtual_device_init( void ) if( register_chrdev( 250, "virtual_device", &vd_fops ) < 0 ) printk( KERN_ALERT "driver init failed\n" ); else printk( KERN_ALERT "driver init successful\n"); void __exit virtual_device_exit( void ) unregister_chrdev( 250, "virtual_device" ); /*unregister_chdev() return void*/ // if( unregister_chrdev( 250, "virtual_device" ) < 0 ) // printk( KERN_ALERT "driver cleanup failed\n" ); // else printk( KERN_ALERT "driver cleanup successful\n" ); module_init( virtual_device_init ); module_exit( virtual_device_exit ); MODULE_LICENSE( "GPL" ); /* ioctl_app1.c */ #include <stdio.h> #include <unistd.h> #include <sys/fcntl.h> #include <sys/ioctl.h> int main( int argc, char *argv[] ) { int dev; printf( "Device driver test.\n" ); dev = open( "/dev/virtual_device", O_RDWR ); if( dev < 0 ) printf( "device file open error\n" ); return -1; } ioctl( dev, 1 ); // read ioctl( dev, 2 ); // write ioctl( dev, 3 ); // unknown command close( dev ); return 0; http://www.opensourceforu.com/2011/08/io-control-in-linux/
ioctl_device2 작성 (32-bit) $ cd /root # vi ioctl_device2.c # vi ioctl_app2.c # vi Makefile # make # insmod ioctl_device1.ko (# mknod /dev/virtual_device c 250 0) # gcc ioctl_app1.c –o ioctl_app1 # ./ioctl_app2 # dmesg | tail /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = ioctl_device1.o TARGETS = ioctl_app1 KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: ${TARGETS} $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules CC := /usr/bin/gcc %.c%: ${CC} -o $@ $^ clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o rm -rf .tmp_versions
#include <stdio.h> #include <stdlib.h> /* ioctl_device2.c */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <asm/uaccess.h> #include <linux/slab.h> #define DEV_NAME "virtual_device" #define MY_IOCTL_NUMBER 100 #define MY_IOCTL_READ _IOR( MY_IOCTL_NUMBER, 0, int ) #define MY_IOCTL_WRITE _IOW( MY_IOCTL_NUMBER, 1, int ) #define MY_IOCTL_STATUS _IO( MY_IOCTL_NUMBER, 2 ) #define MY_IOCTL_READ_WRITE _IOWR( MY_IOCTL_NUMBER, 3, int ) #define MY_IOCTL_NR 4 static int data = 0; int virtual_device_open( struct inode *inode, struct file *filp ) { printk( KERN_ALERT "virtual_device open function called\n" ); return 0; } int virtual_device_release( struct inode *inode, struct file *filp ) printk( KERN_ALERT "virtual device release function called\n" ); int virtual_device_ioctl( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) int err = 0, size; if( _IOC_TYPE( cmd ) != MY_IOCTL_NUMBER ) return -EINVAL; if( _IOC_NR( cmd ) >= MY_IOCTL_NR ) return -EINVAL; if( _IOC_DIR( cmd ) & _IOC_READ ) { err = access_ok( VERIFY_READ, (void *)arg, sizeof( unsigned long ) ); if( err < 0 ) return -EINVAL; else if( _IOC_DIR( cmd ) & _IOC_WRITE ) { err = access_ok( VERIFY_WRITE, (void *)arg, sizeof( unsigned long ) ); size = _IOC_SIZE( cmd ); switch( cmd ) case MY_IOCTL_READ: printk( KERN_ALERT "[IOCTL Message READ] read called...\n" ); copy_to_user( (void*)arg, (const void*)&data, (unsigned long)size ); printk( KERN_ALERT "[IOCTL Message READ] in kernel space: %d\n", data ); break; case MY_IOCTL_WRITE: printk( KERN_ALERT "[IOCTL Message WRITE] write called...\n" ); copy_from_user( (void*)&data, (const void*)arg, (unsigned long)size ); printk( KERN_ALERT "[IOCTL Message WRITE] write: %d\n", data ); case MY_IOCTL_STATUS: printk( KERN_ALERT "[IOCTL Message STATUS] Status called....\n" ); case MY_IOCTL_READ_WRITE: printk( KERN_ALERT "[IOCTL Message READ_WRITE] READ_WRITE called...\n" ); printk( KERN_ALERT "[IOCTL Message READ_WRITE] data: %d\n", data ); data += 900; copy_to_user( (void*)arg, (const void*)&data, (unsigned long)size ); default: printk( KERN_ALERT "[IOCTL Message] Unknown command...\n" ); // file operations static struct file_operations vd_fops = { .owner = THIS_MODULE, .open = virtual_device_open, .release = virtual_device_release, .unlocked_ioctl = virtual_device_ioctl }; int __init virtual_device_init( void ) if( register_chrdev( 250, DEV_NAME, &vd_fops ) < 0 ) printk( KERN_ALERT "driver init failed\n" ); else printk( KERN_ALERT "driver init successful\n"); void __exit virtual_device_exit( void ) unregister_chrdev( 250, DEV_NAME ); // if( unregister_chrdev( 250, DEV_NAME ) < 0 ) // printk( KERN_ALERT "driver cleanup failed\n" ); // else printk( KERN_ALERT "driver cleanup successful\n" ); module_init( virtual_device_init ); module_exit( virtual_device_exit ); MODULE_LICENSE( "GPL" ); /* ioctl_app1.c */ #include <stdio.h> #include <stdlib.h> //#include <unistd.h> #include <sys/fcntl.h> #include <sys/ioctl.h> #define DEV_NAME "/dev/virtual_device" #define MY_IOCTL_NUMBER 100 #define MY_IOCTL_READ _IOR( MY_IOCTL_NUMBER, 0, int ) #define MY_IOCTL_WRITE _IOW( MY_IOCTL_NUMBER, 1, int ) #define MY_IOCTL_STATUS _IO( MY_IOCTL_NUMBER, 2 ) #define MY_IOCTL_READ_WRITE _IOWR( MY_IOCTL_NUMBER, 3, int ) #define MY_IOCTL_NR 4 int main( int argc, char **argv ) { int dev; int data = 100; dev = open( DEV_NAME, O_RDWR ); if( dev < 0 ) exit( EXIT_FAILURE ); printf( "\n----------------------------------------------\n" ); printf( "[App Message READ] Before IOCTL READ, data = %d\n", data ); ioctl( dev, MY_IOCTL_READ, &data ); printf( "[App Message READ] After IOCTL READ, data = %d\n", data ); printf( "[App Message WRITE] Before IOCTL WRITE, data = %d\n", data ); data = 200; ioctl( dev, MY_IOCTL_WRITE, &data ); printf( "[App Message WRITE] After IOCTL WRITE, data = %d\n", data ); printf( "[App Message STATUS] IOCTL STATUS\n" ); ioctl( dev, MY_IOCTL_STATUS ); data = 400; printf( "[App Message READ_WRITE] Before IOCTL READ_WRITE, data = %d\n", data); ioctl( dev, MY_IOCTL_READ_WRITE, &data ); printf( "[App Message READ_WRITE] After IOCTL READ_WRITE, data = %d\n", data ); close( dev ); return 0; }
cdev 구조체를 이용한 device 작성 (32-bit) $ cd /root (장치번호 지정) # vi cdev.c # vi Makefile # make # rm /dev/virtual_device # mknod /dev/virtual_device c 252 10 # insmod cdev.ko # dmeg | tail # cat /dev/virtual_device # cat > /dev/virtual_device # dmesg | tail (장치번호 동적 할당) # insmod cdev2.ko # dmeg | tail (자동할당된 장치번호 확인) # mknod /dev/virtual_device c 장치번호 10 /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = cdev.o cdev2.o TARGETS = ioctl_app2 KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: ${TARGETS} $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules CC := /usr/bin/gcc %.c%: ${CC} -o $@ $^ clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o rm -rf .tmp_versions
/* cdev.c */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> dev_t virtual_device_number; struct cdev *virtual_device; int virtual_device_open( struct inode *inode, struct file *filp ) { return 0; } int virtual_device_release( struct inode *inode, struct file *filp ) ssize_t virtual_device_write( struct file *filp, const char *buf, size_t count, loff_t *f_pos ) printk( KERN_ALERT "virtual_device write function called\n" ); return count; ssize_t virtual_device_read( struct file *filp, char *buf, size_t count, loff_t *f_pos ) printk( KERN_ALERT "virtual_device read function called\n" ); // file operations static struct file_operations vd_fops = { .read = virtual_device_read, .write = virtual_device_write, .open = virtual_device_open, .release = virtual_device_release }; int __init dev_init( void ) virtual_device_number = MKDEV( 252, 10 ); register_chrdev_region( virtual_device_number, 1, "virtual_device" ); virtual_device = cdev_alloc(); cdev_init( virtual_device, &vd_fops ); cdev_add( virtual_device, virtual_device_number, 1 ); printk( "[Module Message] Major: %d\n", MAJOR( virtual_device_number ) ); printk( "[Module Message] Minor: %d\n", MINOR( virtual_device_number ) ); void __exit dev_exit( void ) // void unregister_chrdev_region(dev_t from, unsigned count) cdev_del( virtual_device ); unregister_chrdev_region( virtual_device_number, 1 ); module_init( dev_init ); module_exit( dev_exit ); MODULE_LICENSE( "GPL" ); MODULE_AUTHOR( "coffee man" ); MODULE_DESCRIPTION( "cdev example" ); /*cdev2.c */ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/cdev.h> dev_t virtual_device_number; struct cdev *virtual_device; int virtual_device_open( struct inode *inode, struct file *filp ) { return 0; } int virtual_device_release( struct inode *inode, struct file *filp ) ssize_t virtual_device_write( struct file *filp, const char *buf, size_t count, loff_t *f_pos ) printk( KERN_ALERT "virtual_device write function called\n" ); return count; ssize_t virtual_device_read( struct file *filp, char *buf, size_t count, loff_t *f_pos ) printk( KERN_ALERT "virtual_device read function called\n" ); // file operations static struct file_operations vd_fops = { .read = virtual_device_read, .write = virtual_device_write, .open = virtual_device_open, .release = virtual_device_release }; int __init dev_init( void ) virtual_device = cdev_alloc(); alloc_chrdev_region( &virtual_device_number, 10, 1, "virtual_device" ); cdev_init( virtual_device, &vd_fops ); cdev_add( virtual_device, virtual_device_number, 1 ); printk( "[Module Message] Major: %d\n", MAJOR( virtual_device_number ) ); printk( "[Module Message] Minor: %d\n", MINOR( virtual_device_number ) ); void __exit dev_exit( void ) cdev_del( virtual_device ); unregister_chrdev_region( virtual_device_number, 1 ); module_init( dev_init ); module_exit( dev_exit ); MODULE_LICENSE( "GPL" ); MODULE_AUTHOR( "coffee man" ); MODULE_DESCRIPTION( "cdev example" );
md5 device 작성 debugging (32-bit) $ cd /root (장치번호 지정) # make menuconfig (Cryptographic Manager를 모듈로 설정) # make bzImage # make modules # make modules_install # cp arch/x86/boot/bzImage /boot/vmlinuz-3.2.37 # reboot # vi md5_dev.c # vi Makefile# apt-get install kerneli # make # insmod md5_dev.ko # dmeg | tail # mknod /dev/mod5 c 동적장치번호 0 # gcc md5_app.c –o md5_app # ./md5_app hello # dmesg | tail # modinfo md5_dev.ko /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = md5_dev.o TARGETS = ioctl_app2 KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: ${TARGETS} $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules CC := /usr/bin/gcc %.c%: ${CC} -o $@ $^ clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o rm -rf .tmp_versionss
/* md5_dev.c 디버깅 해야 함 */ #include <linux/fs.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mm.h> #include <linux/crypto.h> #include <asm/uaccess.h> #include <asm/scatterlist.h> #define offset_in_page(p) ((unsigned long)(p) & ~PAGE_MASK) #define TVMEMSIZE 4096 /* to hash */ #define MAX_TAP 8 #define MAX_DIGEST_SIZE 64 #define DEV_NAME "md5" #define MAX_LEN 1024 struct hash_vec { char plaintext[128]; unsigned char psize; char digest[MAX_DIGEST_SIZE]; unsigned char np; unsigned char tap[MAX_TAP]; char key[128]; /* only used with keyed hash algorithms */ unsigned char ksize; }; struct hash_vec md5_template; static char *hash_md5( struct hash_vec *template ); static void hexdump(unsigned char *buf, unsigned int len); static char* hexdump_as_str(unsigned char *buf, unsigned int len); int virtual_device_open( struct inode *, struct file * ); int virtual_device_release( struct inode *, struct file * ); ssize_t virtual_device_write( struct file *, const char *, size_t, loff_t * ); ssize_t virtual_device_read( struct file *, char *, size_t, loff_t * ); static atomic_t virtual_device_usage = ATOMIC_INIT( 1 ); static int virtual_device_major = 0; static char *buffer = NULL; // file operations static struct file_operations vd_fops = { .read = virtual_device_read, .write = virtual_device_write, .open = virtual_device_open, .release = virtual_device_release int virtual_device_open( struct inode *inode, struct file *flip ) { // 카운터 값은 1 printk( KERN_ALERT "[Message] before open: %d\n", virtual_device_usage.counter ); printk( KERN_ALERT "[Message] open function called\n" ); if( !atomic_dec_and_test( &virtual_device_usage ) ) // 카운터를 감소시키면 안되는 경우 실행되는 영역(이미 사용중인 경우) // 감소시킨 카운터를 복원 atomic_inc( &virtual_device_usage ); return -EBUSY; } // 카운터 값은 0 printk( KERN_ALERT "[Message] after open: %d\n", virtual_device_usage.counter); return 0; int virtual_device_release( struct inode *inode, struct file *flip ) printk( KERN_ALERT "[Message] relese function called\n" ); printk( KERN_ALERT "[Message] before release: %d\n", virtual_device_usage.counter ); printk( KERN_ALERT "[Message] after release: %d\n", virtual_device_usage.counter ); int virtual_device_release( struct inode *inode, struct file *flip ) { printk( KERN_ALERT "[Message] relese function called\n" ); // 카운터 값은 0 printk( KERN_ALERT "[Message] before release: %d\n", virtual_device_usage.coun ter ); atomic_inc( &virtual_device_usage ); // 카운터 값은 1 printk( KERN_ALERT "[Message] after release: %d\n", virtual_device_usage.count er ); return 0; } ssize_t virtual_device_write( struct file *flip, const char *buf, size_t count, loff_t *f_pos ) printk( KERN_ALERT "[Message] write function called\n" ); // 처리할 수 있는 길이를 초과하면 if( strlen( buf ) > MAX_LEN ) // 오류를 출력하고 종료 printk( KERN_ALERT "[Message] the size of input cannot be over %d bytes\n", MAX_LEN ); return -EINVAL; else strcpy( md5_template.plaintext, buf ); md5_template.psize = strlen( buf ); return count; ssize_t virtual_device_read( struct file *flip, char *buf, size_t count, loff_t *f_pos ) char *p = (char *)kmalloc( MAX_LEN, GFP_KERNEL ); printk( KERN_ALERT "[Message] read function called\n" ); p = hash_md5( &md5_template ); copy_to_user( buf, p, MAX_LEN ); static void hexdump(unsigned char *buf, unsigned int len) printk( KERN_ALERT "[Message] " ); while (len--) printk("%02x", *buf++); printk("\n"); static char* hexdump_as_str(unsigned char *buf, unsigned int len) char *str = (char*)kmalloc( len, GFP_KERNEL ); strcat( str++, buf++ ); return str; static char *tvmem; static char* hash_md5( struct hash_vec *template ) char *p; unsigned int i; struct scatterlist sg[1]; char* result; struct crypto_tfm *tfm; struct hash_vec *hash_tv; unsigned int tsize; result = (char*)kmalloc( 64, GFP_KERNEL ); tsize = sizeof (struct hash_vec); if (tsize > TVMEMSIZE) { printk("[Message] template (%u) too big for tvmem (%u)\n", tsize, TVMEMSIZE); return NULL; // copy data memcpy( tvmem, template, tsize ); hash_tv = (void *)tvmem; // MD5 알고리즘을 tfm에 할당합니다. tfm = crypto_alloc_tfm( "md5", 0 ); // MD5 알고리즘 할당이 실패한 경우 if (tfm == NULL) { printk("[Message] failed to load transform for %s\n", "md5"); return NULL; } // init result memset( result, 0 , 64 ); p = hash_tv[0].plaintext; sg[0].page = virt_to_page( p ); sg[0].offset = offset_in_page( p ); sg[0].length = hash_tv[0].psize; crypto_digest_init( tfm ); // 암호화에 사용할 키가 설정되어 있으면 if( tfm->crt_u.digest.dit_setkey ) { // 키를 tfm에 할당합니다. crypto_digest_setkey( tfm, hash_tv[0].key, hash_tv[0].ksize ); // 암호화할 데이터를 tfm에 전달합니다. crypto_digest_update( tfm, sg, 1 ); crypto_digest_final( tfm, result ); printk( "[Message] ------- test md5 -------\n" ); // 구해진 해시의 길이를 구합니다. i = crypto_tfm_alg_digestsize( tfm ); // 해시를 16진수로 출력합니다. hexdump( result, i ); printk( "[Message] Length: %d\n", i ); // tfm에 할당된 메모리를 해제합니다. crypto_free_tfm (tfm); return result; static int __init md5_init(void) virtual_device_major = register_chrdev( 0, DEV_NAME, &vd_fops ); if( virtual_device_major < 0 ) printk( KERN_ALERT "[Message] failed to register virtual device\n" ); return -EINVAL; printk( KERN_ALERT "[Message] major num: %d\n", virtual_device_major ); buffer = (char*)kmalloc( 64, GFP_KERNEL ); if( buffer == NULL ) printk( KERN_ALERT "[Message] kmalloc failed\n" ); return -ENOMEM; else memset( buffer, 0, 64 ); tvmem = (char*)kmalloc(TVMEMSIZE, GFP_KERNEL); if (tvmem == NULL) return 0; static void __exit md5_exit(void) { unregister_chrdev( virtual_device_major, DEV_NAME ); kfree( tvmem ); module_init( md5_init ); module_exit( md5_exit ); MODULE_LICENSE( "Dual BSD/GPL" ); MODULE_AUTHOR( "You and I" ); MODULE_DESCRIPTION( "MD5 hash device" );
Chapter 6 proc 파일시스템
procfs에서 제공하는 프로세스별 정보-2 2018-11-13 Hanbit Media(c)
procfs 주요 항목-1 (비고 명령 확인) 2018-11-13 Hanbit Media(c)
procfs 주요 항목-2 (비고 명령 확인) 2018-11-13 Hanbit Media(c)
1. simple_proc.c (p393) struct proc_dir_entry # make /usr/src/linux-3.2.37/include/linux/proc_fs.h 51행 # make # insmod simple_proc.ko # lsmod # echo “hello, profs” > /proc/simple # cat /proc/simple /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = simple_proc.o TARGETS = KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: ${TARGETS} $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules CC := /usr/bin/gcc %.c%: ${CC} -o $@ $^ clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o rm -rf .tmp_versions
2. adder_proc.c # make # insmod adder_proc.ko # lsmod /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = adder_proc.o TARGETS = KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: ${TARGETS} $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules CC := /usr/bin/gcc %.c%: ${CC} -o $@ $^ clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o rm -rf .tmp_versions # make # insmod adder_proc.ko # lsmod # echo 3000 > /proc/adder # cat /proc/adder
3. fun_proc.c Hacking technique! debugging ! current # adduser mona 현재 실행되고 있는 태스크를 가리키는 전역 변수 /usr/src/kernels/linux-2.6.38.6/arch/x86/include/asm/current.h 17행 /usr/src/mylinux/include/linux/sched.h 1221행~1579행 struct task_struct 자료구조를 포인팅 688행 struct user_struct # adduser mona # make # insmod simple_proc.ko # lsmod # echo “hello, profs” > /proc/simple # cat /proc/simple /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = fun_proc.o TARGETS = KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: ${TARGETS} $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules CC := /usr/bin/gcc %.c%: ${CC} -o $@ $^ clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o rm -rf .tmp_versions
4. delay1.c 3.4 G (=3,400,000,000) Hz Time delay 초당 34억 번의 clock tick Time delay $ sleep() : sec $ mdelay() : milli sec $ udelay() : micro sec $ ndelay() : nano sec jiffy (= short period of time) 내장된 타이머의 주기적인 interrupt interval (= clock tick, Hz) /usr/src/linix-3.2.37/kernel/timer.c # uname –a (?) # make # insmod delay1.ko … 10*HZ delay … # dmesg | tail /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = delay1.o TARGETS = KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: ${TARGETS} $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules CC := /usr/bin/gcc %.c%: ${CC} -o $@ $^ clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o rm -rf .tmp_versions
5. delay2.c schedule() 이용 # vi delay2.c #include <linux/sched.h> /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = delay2.o TARGETS = KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: ${TARGETS} $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules CC := /usr/bin/gcc %.c%: ${CC} -o $@ $^ clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o rm -rf .tmp_versions schedule() 이용 커널 안 delay 동안 busy-waiting 하는 대신 다음 CPU 시간을 할당해 줄 프로세스를 찾는 schedule() 후 복귀 # vi delay2.c #include <linux/sched.h> # make # insmod delay1.ko … 10*HZ delay … # dmesg | tail
6. delay3.c cond_schedule() 이용 set_current_state(TASK_INTERRUPTIBLE) 커널 안 delay 동안 busy-waiting 하는 대신 다음 필요한 경우에만 CPU 시간을 할당해 줄 프로세스를 찾는 cond_resched() 후 복귀 set_current_state(TASK_INTERRUPTIBLE) 프로세스 상태 설정 sched_timeout( delay ) delay 동안 작업이 완료되지 못하면 제어권을 넘김 # vi delay3.c #include <linux/sched.h> # make # insmod delay1.ko … 10*HZ delay … # dmesg | tail /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = delay3.o TARGETS = KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: ${TARGETS} $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules CC := /usr/bin/gcc %.c%: ${CC} -o $@ $^ clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o rm -rf .tmp_versions
7. rdtscll.c TSC (TimeStamp Counter) register 1Hz (= cycles per second) 당 1씩 증가 64비트 32비트 = 232 = 약 42억 4GHz (=1,000,000,000Hz : 10억) CPU rdtsc(low32, high32) = read tsc rdtscl(low32) = read tsc at low rdtscll(u64) = read tsc in long long /arch/x86/include/asm/tsc.h extern void tsc_init(void); # vi rdtscll.c void tsc_init( void ) # make # insmod rdtscll.ko # gcc rdtscll_app2.c –o rdtscll_app2 # ./rdtscll_app2 /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = rdtscll.o TARGETS = rdtscll_app2 KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: ${TARGETS} $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules CC := /usr/bin/gcc %.c%: ${CC} -o $@ $^ clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o rm -rf .tmp_versions
/. rdtscll. c. / #include <linux/kernel /* rdtscll.c */ #include <linux/kernel.h> #include <linux/module.h> #include <asm/msr.h> static unsigned long long tsc = 0; //int tsc_init( void ) void tsc_init( void ) { rdtscll( tsc ); printk( KERN_ALERT "[Message] TSC: %llu\n", tsc ); return 0; } void tsc_exit( void ) module_init( tsc_init ); module_exit( tsc_exit ); MODULE_LICENSE( "Dual BSD/GPL" );
8. timer.c debugging /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = timer.o TARGETS = rdtscll_app2 KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: ${TARGETS} $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules CC := /usr/bin/gcc %.c%: ${CC} -o $@ $^ clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o rm -rf .tmp_versions BogoMIPS (Bogus Millions of Instructions Per Second) # dmesg | grep BogoMIPS # cat /proc/cpuinfo # vi timer.c 36행 current_cpu_data boot_cpu_data # make # insmod timer.ko # cat /proc/timer HPET (High Precision Event Timer) RTC (Real Time Clock) PIT (Programmable Interval Timer) APIC (Advanced Programmable Interrupt Controller) ACPI (Advanced Configuration and Power Interface) /usr/src/linux-3.2.37/arch/x86/kernel/kvmclock.c /usr/src/linux-3.2.37/arch/x86/kernel/tsc.c
Chapter 7 블록 디바이스 프로그래밍
정리 하드디스크 블록 디바이스 구조 I/O scheduler I/O 관련 기본 자료구조 전송 시간 = 검색 시간 + 회전 지연 시간 + 데이터 전송 시간 p458 검색시간 계산식? 블록 디바이스 구조 p461~462: 그림 7-4와 struct buffer_head 관계 p467~468: 그림 7-7와 struct bio 관계 I/O scheduler write bomb $ cp /dev/zero /tmp/test ^C $ ls –l /tmp/test $ more /tmp/test 스케줄링 알고리즘 Linus Elevator Deadline Elevator Anticipatory Elevator CFQ(Complete Faireness Queuing): 완전공평큐 NOOP scheduler $ cat /sys/block/sda/queue/scheduler noop deadline [cfg] I/O 관련 기본 자료구조 버퍼 처리 자료구조 struct buffer_head 블록 I/O 자료구조 struct bio 요청(queue_head) 큐 관리 자료구조 struct request_queue 예측 스케줄러 구조체 struct elevator_type
rd.c debugging /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = rd.o TARGETS = KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: ${TARGETS} $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules CC := /usr/bin/gcc %.c%: ${CC} -o $@ $^ clean: rm -rf *.ko rm -rf *.mod.* rm -rf .*.cmd rm -rf *.o rm -rf .tmp_versions $ vi rd.c typedef struct request_queue request_queue_t; 62행 bio_endio ( bio, bio->bi_size ); 66행 bio_io_err (bio); # make # mknod /dev/vrd b 250 0 # insmod rd.ko # mkfs –t ext2 /dev/vrd # mkdir /mnt/vrd # mount –t ext2 /dev/vrd /mnt/vrd # ls –l /mnt/vrd … # modprobe –r rd
Chapter 8 메모리 관리
1. Memory Model In Concept In Concrete Segments Segment: Windows Paging: Linux Segment + Paging Intel Linux (implied segments) : 내부적으로 세그먼트 기법을 사용하지만 프로그램은 선형공간에 있는 것처럼 동작 In Concrete Real Mode Protected Mode Segments CS: Code Segment DS: Data Segment SS: Stack Segment ES: Extra Segment 기본: OS/응용 구분 없음 보호: OS/응용 구분, 프로세스 보호 Linux 멀티: CS, DS + 각 프로세스별 세그먼테이션 Linux Segmentation (커널 영역 보호 모드) #define __KERNEL_CS (GDT_ENTRY_KERNEL_CS*8) #define __KERNEL_DS (GDT_ENTRY_KERNEL_DS*8) #define __USER_DS (GDT_ENTRY_DEFAULT_USER_DS*8+3) #define __USER_CS (GDT_ENTRY_DEFAULT_USER_CS*8+3) 스레드 생성
2. Process Page Table Mapping-1 Page Frame Number
Process Page Table Mapping-2
페이징 관련 커널 함수 /usr/src/linux-3.2.37/arch/x86/include/asm/pgtable.h /usr/src/linux-3.2.37/arch/x86/include/asm/pgtable-2level.h /usr/src/linux-3.2.37/arch/x86/include/asm/pgtable-3level.h pgd_offset(mm, address) pmd_offset(mm, address) pte_offset(mm, address) pgd_index() pmd_index() virt_to_page(kaddr) page_to_pfn( struct page *page ) pfn_to_page( pfn ) pfn_valid( pfn ) page_address( struct page *page ) phys_to_virt() virty_to_phys() pgd_alloc() pgd_free() pgd_ctor() pgd_dtor() pgd_present() pgd_bad() pgd_none() 2018-11-13 Hanbit Media(c)
struct page { … } & flags # vi /usr/src/linux-3.2.37/include/linux/mm_types.h struct page { unsigned long flags; /* Atomic flags, some possibly * updated asynchronously */ atomic_t _count; /* Usage count, see below. */ union { atomic_t _mapcount; /* Count of ptes mapped in mms, * to show when page is mapped * & limit reverse map searches. */ struct { /* SLUB */ u16 inuse; u16 objects; }; struct { unsigned long private; /* Mapping-private opaque data: * usually used for buffer_heads * if PagePrivate set; used for * swp_entry_t if PageSwapCache; * indicates order in the buddy * system if PG_buddy is set. struct address_space *mapping; /* If low bit clear, points to * inode address_space, or NULL. * If page mapped as anonymous * memory, low bit is set, and * it points to anon_vma object: * see PAGE_MAPPING_ANON below. #if USE_SPLIT_PTLOCKS spinlock_t ptl; #endif struct kmem_cache *slab; /* SLUB: Pointer to slab */ struct page *first_page; /* Compound tail pages */ pgoff_t index; /* Our offset within mapping. */ void *freelist; /* SLUB: freelist req. slab lock */ struct list_head lru; /* Pageout list, eg. active_list * protected by zone->lru_lock ! /* * On machines where all RAM is mapped into kernel address space, * we can simply calculate the virtual address. On machines with * highmem some memory is mapped into kernel virtual memory * dynamically, so we need a place to store that address. * Note that this field could be 16 bits on x86 ... ;) * * Architectures with slow multiplication can define * WANT_PAGE_VIRTUAL in asm/page.h #if defined(WANT_PAGE_VIRTUAL) void *virtual; /* Kernel virtual address (NULL if not kmapped, ie. highmem) */ #endif /* WANT_PAGE_VIRTUAL */ #ifdef CONFIG_WANT_PAGE_DEBUG_FLAGS unsigned long debug_flags; /* Use atomic bitops on this */ #ifdef CONFIG_KMEMCHECK * kmemcheck wants to track the status of each byte in a page; this * is a pointer to such a status block. NULL if not tracked. void *shadow; # vi /usr/src/linux-3.2.37/include/linux/page_flags.h enum pageflags { PG_locked, /* Page is locked. Don't touch. */ PG_error, PG_referenced, PG_uptodate, PG_dirty, PG_lru, PG_active, PG_slab, PG_owner_priv_1, /* Owner use. If pagecache, fs may use*/ PG_arch_1, PG_reserved, PG_private, /* If pagecache, has fs-private data */ PG_private_2, /* If pagecache, has fs aux data */ PG_writeback, /* Page is under writeback */ #ifdef CONFIG_PAGEFLAGS_EXTENDED PG_head, /* A head page */ PG_tail, /* A tail page */ #else PG_compound, /* A compound page */ #endif PG_swapcache, /* Swap page: swp_entry_t in private */ PG_mappedtodisk, /* Has blocks allocated on-disk */ PG_reclaim, /* To be reclaimed asap */ PG_swapbacked, /* Page is backed by RAM/swap */ PG_unevictable, /* Page is "unevictable" */ #ifdef CONFIG_MMU PG_mlocked, /* Page is vma mlocked */ #ifdef CONFIG_ARCH_USES_PG_UNCACHED PG_uncached, /* Page has been mapped as uncached */ #ifdef CONFIG_MEMORY_FAILURE PG_hwpoison, /* hardware poisoned page. Don't touch */ #ifdef CONFIG_TRANSPARENT_HUGEPAGE PG_compound_lock, __NR_PAGEFLAGS, /* Filesystems */ PG_checked = PG_owner_priv_1, /* Two page bits are conscripted by FS-Cache to maintain local caching * state. These bits are set on pages belonging to the netfs's inodes * when those inodes are being locally cached. */ PG_fscache = PG_private_2, /* page backed by cache */ /* XEN */ PG_pinned = PG_owner_priv_1, PG_savepinned = PG_dirty, /* SLOB */ PG_slob_free = PG_private, /* SLUB */ PG_slub_frozen = PG_active, };
struct mm_struct { … } # vi /usr/src/linux-3.2.37/include/linux/mm_types.h struct mm_struct { struct vm_area_struct * mmap; /* list of VMAs */ struct rb_root mm_rb; struct vm_area_struct * mmap_cache; /* last find_vma result */ #ifdef CONFIG_MMU unsigned long (*get_unmapped_area) (struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags); void (*unmap_area) (struct mm_struct *mm, unsigned long addr); #endif unsigned long mmap_base; /* base of mmap area */ unsigned long task_size; /* size of task vm space */ unsigned long cached_hole_size; /* if non-zero, the largest hole below free_area_cache */ unsigned long free_area_cache; /* first hole of size cached_hole_size or larger */ pgd_t * pgd; atomic_t mm_users; /* How many users with user space? */ atomic_t mm_count; /* How many references to "struct mm_struct" (users count as 1) */ int map_count; /* number of VMAs */ spinlock_t page_table_lock; /* Protects page tables and some counters */ struct rw_semaphore mmap_sem; struct list_head mmlist; /* List of maybe swapped mm's. he se are globally strung * together off init_mm.mmlist, and are protected * by mmlist_lock */ unsigned long hiwater_rss; /* High-watermark of RSS usage */ unsigned long hiwater_vm; /* High-water virtual memory usage */ unsigned long total_vm, locked_vm, shared_vm, exec_vm; unsigned long stack_vm, reserved_vm, def_flags, nr_ptes; unsigned long start_code, end_code, start_data, end_data; unsigned long start_brk, brk, start_stack; unsigned long arg_start, arg_end, env_start, env_end; unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */ /* * Special counters, in some configurations protected by the * page_table_lock, in other configurations by being atomic. */ struct mm_rss_stat rss_stat; struct linux_binfmt *binfmt; cpumask_t cpu_vm_mask; /* Architecture-specific MM context */ mm_context_t context; /* Swap token stuff */ * Last value of global fault stamp as seen by this process. * In other words, this value gives an indication of how long * it has been since this task got the token. * Look at mm/thrash.c unsigned int faultstamp; unsigned int token_priority; unsigned int last_interval; unsigned long flags; /* Must use atomic bitops to access the bits */ struct core_state *core_state; /* coredumping support */ #ifdef CONFIG_AIO spinlock_t ioctx_lock; struct hlist_head ioctx_list; #endif #ifdef CONFIG_MM_OWNER * "owner" points to a task that is regarded as the canonical * user/owner of this mm. All of the following must be true in * order for it to be changed: * * current == mm->owner * current->mm != mm * new_owner->mm == mm * new_owner->alloc_lock is held struct task_struct __rcu *owner; #ifdef CONFIG_PROC_FS /* store ref to file /proc/<pid>/exe symlink points to */ struct file *exe_file; unsigned long num_exe_file_vmas; #ifdef CONFIG_MMU_NOTIFIER struct mmu_notifier_mm *mmu_notifier_mm; #ifdef CONFIG_TRANSPARENT_HUGEPAGE pgtable_t pmd_huge_pte; /* protected by page_table_lock */ /* How many tasks sharing this mm are OOM_DISABLE */ atomic_t oom_disable_count; };
3. 2-Phase (Level) Paging 0xC1234567 1100 0001 00 1000 1101 00 0101 0110 0111 unsinged int table[1024][1024]; unsigned int directory[1024], table[1024]; 20 bit table인 경우: 2^20 = 1,048,576 = 1M 1M * 2^12(4096) = 4G PTE = 4 bytes, 1M * 4 = 4M each process Page Directory = 1024개 * 4 bytes = 4k = 1 page Page Table = 1024개 * 4 bytes = 4k = 1 page 2018-11-13 Hanbit Media(c)
4. 3-Phase (Level) Paging /usr/src/linux-3.2.37/arch/x86/include/asm/pgtable.h, pgtable-2levle.h, pgtable-3level.h CR3 (Control Register3): page directory base register (PDBR) cf. include/asm-i386/page.h, pgalloc.h, pgtable.h, pgtable-2level.h 2018-11-13 Hanbit Media(c)
5. Memory Management of Process vm_flags 값: VM_READ VM_WRITE VM_EXEC VM_SHARED VM_GROWSDOWN VM_GROWUP VM_LOCKED 2018-11-13 Hanbit Media(c)
프로세스 주소 공간 배치
(참고) Linux 메모리 관리 자료구조 task_struct 구조체내의 struct mm_struct *mm /usr/src/kernels/linux-2.6.35.6/include/linux/sched.h (1193행 CentOS Linux 2.6.32.71) /usr/src/kernels/linux-2.6.35.6/include/linux/mm_types.h (222행, 131행) vm_area_struct 안의 struct mm_struct * vm_mm은 메모리 디스크립터 포인터 운영체제
BSS(Block Started by Symbol) (참고) Linux 가상 메모리 구조 $ pmap 1 $ cat /proc/1/maps 또는 $ ps $ pmap PID $ cat /proc/PID/maps 하나의 태스크마다 4GB 주소 공간을 할당 각 태스크에게 3GB 할당 나머지 1GB는 모든 태스크들의 공통 영역으로서 커널 공간으로 할당 BSS(Block Started by Symbol) 운영체제
maps (p546~550 coding) # ls –l /proc/self # vi layout.c # cat /proc/1/maps # pmap –x 1 # vi hello.c #include <stdio.h> int main( void ) char data[100] = { 0, }; printf( “Hello, World\n” ); return 0; } # gcc hello.c –o hello # ls –l hello # strip hello # size hello # ls –l /proc/self # vi layout.c #include <stdio.h> #include <stdlib.h> int main( void ) char cmd[32]; sprintf( cmd, “cat /proc/self/maps” ); system ( cmd ); return 0; } # gcc layout.c –o layout #./layout # ulimit –s 100 # ./layout
Memory of Process
struct vm_area_struct { … } & flags # vi /usr/src/linux-3.2.37/include/linux/mm_types.h struct vm_area_struct { struct mm_struct * vm_mm; /* The address space we belong to. */ unsigned long vm_start; /* Our start address within vm_mm. */ unsigned long vm_end; /* The first byte after our end address within vm_mm. */ /* linked list of VM areas per task, sorted by address */ struct vm_area_struct *vm_next, *vm_prev; pgprot_t vm_page_prot; /* Access permissions of this VMA. */ unsigned long vm_flags; /* Flags, see mm.h. */ struct rb_node vm_rb; /* * For areas with an address space and backing store, * linkage into the address_space->i_mmap prio tree, or * linkage to the list of like vmas hanging off its node, or * linkage of vma in the address_space->i_mmap_nonlinear list. */ union { struct { struct list_head list; void *parent; /* aligns with prio_tree_node parent */ struct vm_area_struct *head; } vm_set; struct raw_prio_tree_node prio_tree_node; } shared; /* * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma * list, after a COW of one of the file pages. A MAP_SHARED vma * can only be in the i_mmap tree. An anonymous MAP_PRIVATE, stack * or brk vma (with NULL file) can only be in an anon_vma list. */ struct list_head anon_vma_chain; /* Serialized by mmap_sem & page_table_lock */ struct anon_vma *anon_vma; /* Serialized by page_table_lock */ /* Function pointers to deal with this struct. */ const struct vm_operations_struct *vm_ops; /* Information about our backing store: */ unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */ struct file * vm_file; /* File we map to (can be NULL). */ void * vm_private_data; /* was vm_pte (shared mem) */ unsigned long vm_truncate_count;/* truncate_count or restart_addr */ #ifndef CONFIG_MMU struct vm_region *vm_region; /* NOMMU mapping region */ #endif #ifdef CONFIG_NUMA struct mempolicy *vm_policy; /* NUMA policy for the VMA */ };
6. 메모리 존(Memory Zone) 왜 메모리 존이 있는가? i386 하드웨어 제약 극복 관련 변수: zone_table, zone_names 소스: mm/page_alloc.c 정의: include/linux/mmzone.h HighMem 크기 확인 # make menuconfig Processor type and features ---> High Memory Support (64GB) ---> PAE (Physical Address Extension)
zone 구조체
7. 메모리 할당 함수 메모리 할당 함수의 종류 페이지 단위 할당 함수: 2order 개수 만큼 바이트 단위 할당 함수
vmalloc() 할당 함수
메모리 해제 함수
kmalloc() 5장 p306 kmalloc() 커널의 메모리 할당 Dynamic Memory Allocation kmalloc (kernel memory allocation): 연속된 물리 메모리 할당 vmalloc: 연속된 가상 메모리 할당, 불연속 물리 메모리 Dynamic Memory Allocation Interrupt API: include/asm-generic/system.h GFP_KERNEL = __GFP_WAIT | __GFP_IO | __GFP_FS (p307~p309참조) # make # insmod kmalloc.ko # dmesg | tail
vmalloc() # vi /usr/src/linux-3.2.37/linu/vmalloc.h # vi vmalloc.c 8줄 //int vmalloc_init( void ) void vmalloc_init( void ) 17줄 //return 0; # make # insmod kmalloc.ko # dmesg | tail
8. 버디 시스템(Buddy System) 외부 단편화를 최소화하기 위한 방법 alloc_pages(), get_free_pages()
버디 시스템의 크기와 비트 수의 관계 2k의 k는 0의 개수와 같음
9. 슬랩 할당자(Slab Allocator) 내부 단편화를 최소화하기 위한 방법 kmalloc() # cat /proc/slabinfo
슬랩 할당자 구조 struct kmem_cache { typedef struct slob_block slob_t
객체 크기와 슬랩 할당의 관계 2018-11-13 Hanbit Media(c)
슬롭 할당자(SLOB Allocator) 슬랩 할당자 메모리 공간 # cat /proc/meminfo | grep –i slab 38432K 바이트 (과거 기술) 슬롭 할당자 메모리 공간 # vi /usr/src/linux-3.2.37/mm/slob.c The heap is grown on demand 0 바이트 제한된 메모리를 가지는 임베디드 환경을 위하여 포함됨 CONFIG_SLOB 설정하여 컴파일
슬랩할당자 cache.c void *kmem_cache_alloc(strut kmem_cache *, gfp_t); GFP_KERNEL = __GFP_WAIT | __GFP_IO | __GFP_FS (p307~p309참조) http://lxr.free-electrons.com/source/include/linux/slab.h?v=3.2#L271 # vi cache.c 6줄 //static kmem_cache_t my_cachep; static struct kmem_cache my_cachep; # linux/slab_def.h 23줄 //NULL 40줄 // kmen_cache_destory(…) # make # insmod cache.ko # dmesg | tail # cat /proc/slabinfo | grep my_cache
메모리 매핑 호출 그래프
10. 메모리 매핑 mmap.c # vi /usr/include/sys/mman.h Memory Mapped IO 키보드 입력 ⇔ 비디오 메모리 공간 B8000 – C0000 (CGA) A0000 – B10000 # vi /usr/include/sys/mman.h mmap(): start=0 자동지정, offset=0 처음부터 사용 munmap() msync() mmap로 read()와 write() 대체 # man –s 2 open # gcc mmap.c –o mmap # ./mmap # ls –l test.dat
메모리 매핑 드라이버 mmap_dev.c # vi mmap_dev.c # make 1줄 #include <linux/fs.h> #define NOPAGE_SIGBUS (NULL) 55줄 //.nopage 138줄 //int mmap_init ( void ) void mmap_init ( void ) 144줄 //return 0; # make # mknod /dev/mmap c 252 0 # insmod mmap_dev.ko # gcc mmap2.c –o mmap2 # ./mmap2
11. 연결 리스트 자료구조 # gcc –std=c99 –Wall offset1.c –o offset1 #./offset1 # vi test/linked.c // doubley-link list 5줄 //#include <asm/system.h> # gcc linked.c –o linked # ./linked # dmesg | tail # vi link.c # make
12. 프로세스 메모리 뷰어 # pmap –x PID 유사한 동작 (소스 코드 참고) fs/proc/task_mmu.c # vi show_mm.c 1줄 #include <linux/slab.h> 2줄 #include <linux/fs.h> 26줄 //task = find_task_by_pid( pid ); task = find_task_by_vpid( pid ); 76줄 //fullname = d_path( file-> f_denry, fullname = d_path( file-> f_denry->d_fsdata, 78줄 //file->vfsmnt 121줄 //task = find_task_by_pid( pid ); 147줄 //get_mm_count( mm, rss ); get_mm_rss( mm ) );
13. LRU 정책 효율적인 캐싱 정책 Least Recently Used 페이지를 제거 LRU/k: LRU 큐가 k개, k번째 접근한 페이지를 LRU 리스트에 추가 Lists Active list Inactive list Status bits Active bit Referenced bit /usr/src/linux-3.2.37/include/linx/page-flags.h
LRU/k 정책 2018-11-13 Hanbit Media(c)
inactive list 호출 그래프 2018-11-13 Hanbit Media(c)
active list 호출 그래프 2018-11-13 Hanbit Media(c)
zone과 page 구조체의 관계 2018-11-13 Hanbit Media(c)
Chapter 9 프로세스 관리
1. 프로세스 자료구조 모두 C program 인 이유? get_task_state() { in /usr/include/linux-3.2.37/linux/sched.h 줄 1221~ 줄 1579 task_state_array { in fs/proc/array.c 설정 방법 task->state |=TASK_INTERRUPTIBLE set_task_state (tsk, TASK_INTERRUPTIBLE ); set_current_state ( TASK_INTERRUPTIBLE ); flags in /usr/include/linux-3.2.37/linux/sched.h exec_domain in /usr/include/linux-3.2.37/linux/personality.h struct linux_binfmt { in /usr/include/linux-3.2.37/linux/binfmts.h /proc/sys/fs/binfmt_misc (예) 3 가지 register 하고 applet 으로 테스트 # pstree real_parent? # tty struct tty_ldisc_ops in /usr/include/linux-3.2.37/linux/tty_ldisc.h fs_struct { in /usr/include/linux-3.2.37/linux/fs_struct.h 프로세스당 1개? files_struct { in /usr/include/linux-3.2.37/linux/fdtable.h 프로세스당 1개?
2. 프로세스 생성 # ./system # ./execl in /usr/include/unistd.h # ./execlp # ./execve # ./fork # ./fork & ps j # ./wait 상위 8비트와 하위 8비트 출력은 과제로 제출 multiple mount # touch tmp # mount --bind /tmp /toot/tmp # mkdir tmp3 # mount –move /root/tmp /root/tmp3 # ls –l /root/tmp3
2. 프로세스 종료 in atomic() exit_notify()
(참고) 프로세스 종료 (Process Termination) exit 시스템 종료 계산 결과는 부모에게 Return 메모리(물리적/가상적), 오픈한 화일, I/O버퍼를 OS에게 돌려줌 abort 시스템 호출 부모만 호출(그렇지 않으면 서로 죽이는 일 생김) 실행 종료 이유 자식이 할당된 자원을 초과 사용할 때 자식의 task가 더 이상 필요 없을 때 부모가 종료될 때 Unix 계단식(cascading) 종료 : 부모 종료 OS가 모든 자식 종료 (예) mystatus.c 참조 exit system call로 프로세스 종료 wait system call : return값 = 종료하는 자식의 pid wait(&status) /* int status */ 정상종료 경우 : 하위 8bits는 0, 상위 8bits 는 exit status (자식 프로세스가 exit 명령으로 전달한 상태정보 값) signal로 종료된 경우 : 하위 8bits는 signal 번호, 상위 8bits는 0 (상위 8 비트 추출) status >> 8; status &= 0xFF;
(참고) exit 예제 mystatus.c & myexit.c $ cat mystatus.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> main () { int pid, status, childPid; printf ("I'm the parent process and my PID is %d\n", getpid ()); pid = fork (); /* Duplicate */ if (pid != 0) /* Branch based on return value from fork () */ printf ("I'm the parent process with PID %d and PPID %d\n", getpid (), getppid ()); childPid = wait (&status); /* Wait for a child to terminate. */ printf ("A child with PID %d, terminated with exit code low: %d, high: %d\n", childPid, (status & 0xFF), status >> 8); /* Linux */ } else printf ("I'm the child process with PID %d and PPID %d\n", getpid (), getppid ()); execlp ("ls", "ls", "-li", (char *)0); /* execlp ("myexit", "myexit", (char *)0); */ exit (42); /* Exit with a silly number */ printf ("PID %d terminates\n", getpid () ); $ cat myexit.c #include <stdio.h> #include <stdlib.h> main () { printf ("I'm going to exit with return code 33\n"); exit (33); } 운영체제
4. 프로세스 스케줄링 스케줄링 알고리즘 FIFO SJF Priority RR Realtime Multilevel Queue 다단계큐 Multilevel Feedback Queue 다단계 피드백큐
리눅스 스케줄링 prio.c prio2.c sched_getparam.c get_sched.c sched.c patch original.c edited.c p478 # cat /sys/block/sda/queue/scheduler # echo cfq /sys/block/sda/queue/scheduler
Chapter 10 파일시스템
가상 파일시스템 f_path.dentry: path.h list.h 19줄 LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name=LIST_JEAD_INIT(name) # cat /proc/slabinfo | grep dentry
SFS 파일시스템 # vi fs.c
Chapter 11 네트워크
Linux 네트워크 모델 linux/tcp.h 294줄 struct tcp_sock { net/ipv4/af_inet.c check ops include/linux/net.h 138 struct socket { include/net/sock.h 239, 261, 300 listen ( s, backlog ), struct backlog net_protocol inet_protos IANA http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml netdevice.h LLC(logical link control) skbuff.h include/linux/netdevice.h 963줄 netfilter_ipv4.h 47줄 linux/in.h 25줄
nfilter.c 16줄 (struct iphdr*)(*pskb)->network_header 36줄 .hooknum = 3, # make # insmod nfilter.ko # ./sclient # dmesg | tail