Kernel Programming Kernel 3장 ~ 11장
주별 학습 주제 주차 날짜 학습 주제 강의진행방식 진행자 특기사항 1 3.5 목 수업 소개 성미영 2 3.12 목 1장. 리눅스 커널 프로그래밍 환경 구축 Lab Seminar 3 3.19 목 2장. 커널 컴파일 및 개발 환경 구축 4 3.26 목 3장. 리눅스 시스템 호출 5 4.2 목 4장. 모듈 프로그래밍 김준하 6 4.9 목 3장.(보충) 리눅스 시스템 호출 7 4.16 목 5장. 문자 디바이스 프로그래밍 ACHMAA 8 4.23 목 6장. proc 파일 시스템 장욱진 9 4.30 목 7장. 블록 디바이스 프로그래밍 지승은 10 5.8 금 8장. 메모리 관리 남여민 11 5.14 목 9장. 프로세스 관리 장재현 12 5.21목 10장. 파일 시스템 이재호 13 5.27목 11장. 네트워크 최아람 14 6.4 목 커널 프로그래밍 프로젝트 발표 15 6.11 목 종강
참고 : http://emzei.tistory.com/154 Chapter 3 리눅스 시스템 호출 newsyscall newsysadd getcpuinfo getstat 참고 : http://emzei.tistory.com/154
커널 빌드 과정 # make (p112) 2019-05-09 make install
커널 컴파일 (참고) https://www.debian.org/releases/lenny/sparc/ch08s06.html.ko /usr/src/linux-3.19 위치 # make mrproper # make menuconfig exit save 오류 나면 libncurses* 설치 # apt-get install ncurses* # make bc not found 오류 나면 bc 설치 # apt-get install bc # make modules_install (커널 이미지 확인) # ls –l arch/x86/boot (커널 모듈 확인) # ls –l /lib/modules # make install /* 커널 이미지 설치 */ # vi /boot/grub/grub.cfg /* title 편집 */ # reboot # uname -a
(참고) 부트 로더 보수 내 커널 버전 로그인 위한 grub 구성 파일 생성 (SCSI disk) # update-grub /dev/sda 또는 # grub-install /dev/sda (IDE disk) # update-grub /dev/hda 또는 # 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/
사용자 공간과 커널 공간 커널과 응용프로그램의 분리 2019-05-09 Hanbit Media(c)
특권 레벨(Privilege Level) GDT(Global Descriptor Table)로 메모리 공간을 정의 DPL(Descriptor Privilege Level)에서 특권 레벨을 지정 2019-05-09 Hanbit Media(c)
특권 레벨의 이동 시스템 호출은 커널 영역과 사용자 영역의 이동(특권 레벨의 이동)을 의미 특권 레벨의 이동을 위해서는 특별한 명령이 필요(CALL, RET) 2019-05-09 Hanbit Media(c)
CPL(Current Privilege Level) CPU에서 현재 실행중인 특권 레벨을 지정 인터럽트 또는 시스템 호출 처리시 특권 레벨의 변화를 추적하기 위해 사용 2019-05-09 Hanbit Media(c)
2. 인터럽트 처리 인터럽트 서비스 루틴(ISR; Interrupt Service Routine) 2019-05-09 Hanbit Media(c)
세그먼트 디스크립터 0x0000 0000 1024 2019-05-09 Hanbit Media(c)
CPU가 인터럽트 80에 대한 ISR을 찾아가는 과정 ISR 위치 = 코드 세그먼트 시작 위치(Base Address) + 상대위치 2019-05-09 Hanbit Media(c)
IDT와 리눅스 시스템 호출 IDT(Interrupt Descriptor Table) 인터럽트가 발생했을 때 수행할 ISR(Interrupt Service Routine)들의 테이블 시스템 부팅시에 BIOS에 의해 기본 설정된 테이블이 있음 운영체제 부팅 과정에서 운영체제 고유의 IDT 테이블로 변경함 2019-05-09 Hanbit Media(c)
리눅스의 ISR 2019-05-09 Hanbit Media(c)
시스템 호출 2019-05-09 Hanbit Media(c)
fork() 시스템 호출의 흐름 2019-05-09 Hanbit Media(c)
시스템 호출 추가 과정 2019-05-09 Hanbit Media(c)
나만의 시스템 호출 newsyscall (359) (32-bit) $ cd /usr/src/linux-3.19.1 # vi /usr/include/asm/unistd_32.h #define __NR_newsyscall 359 # vi /usr/src/linux-3.19.1/arch/x86/syscalls/syscall_32.tbl 359 i386 newsyscall sys_newsyscall # vi /usr/src/linux-3.19.1/include/linux/syscalls.h asmlinkage long sys_newsyscall( void ); # vi /usr/src/linux-3.19.1/kernel/newsyscall.c /* 커널 코드 /usr/src/mylinux/kernel/newsyscall.c 코딩*/ # vi /usr/src/linux-3.19.1/kernel/Makefile 수정 obj -y = 라인 마지막에 newsyscall.o 추가 # cd /usr/src/linux-3.19.1 # make bzImage # cp arch/x86/boot/bzImage /boot/vmlinuz-3.19.1 # vi /boot/grub/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( void ) { 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); argument type void 코딩 매우 중요!
나만의 시스템 호출 newsysadd (360) (32-bit) $ cd /usr/src/linux-3.19.1 # vi /usr/include/asm/unistd_32.h #define __NR_newsysadd 360 # vi /usr/src/linux-3.19.1/arch/x86/syscalls/syscall_32.tbl 360 i386 newsysadd sys_newsysadd # vi /usr/src/linux-3.19.1/include/linux/syscalls.h asmlinkage long sys_newsysadd(int a, int b, int *to_user); # vi /usr/src/linux-3.19.1/kernel/newsysadd.c /* 커널 코드 /usr/src/mylinux/kernel/newsysadd.c 코딩*/ # vi /usr/src/linux-3.19.1/kernel/Makefile 수정 obj -y = 라인 마지막에 newsysadd.o 추가 # cd /usr/src/linux-3.19.1 # make bzImage # cp arch/x86/boot/bzImage /boot/vmlinuz-3.19.1 # vi /boot/grub/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 (361) /* /usr/src/linux-3.19.1/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 rfu; char pad0; char pad1; #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+NBUGINTS]; 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; /* 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; u32 microcode; }; (32-bit) $ cd /usr/src/ulinux-3.19.1 # vi /usr/include/asm/unistd_32.h #define __NR_getcpuinfo 361 # vi /usr/src/linux-3.19.1/arch/x86/syscalls/syscall_32.tbl 361 i386 getcpuinfo sys_getcpuinfo # vi /usr/src/linux-3.19.1/include/linux/syscalls.h asmlinkage void sys_getcpuinfo(struct cpu_info *info); # vi /usr/src/linux-3.19.1/kernel/getcpuinfo.c /* 커널 코드 /usr/src/mylinux/kernel/getcpuinfo.c 코딩*/ # vi /usr/src/linux-3.19.1/kernel/Makefile 수정 obj -y = 라인 마지막에 getcpuinfo.o 추가 # cd /usr/src/linux-3.19.1 # make bzImage # cp arch/x86/boot/bzImage /boot/vmlinuz-3.19.1 # vi /boot/grub/grub.cfg 부트로더 검토 # reboot $ gcc test3.c –o test3 $ ./test3 $ dmesg | tail $ cat /proc/cpuinfo 커널에 항상 struct cpuinfo_x86 데이터를 유지하고 있음 /usr/src/linux-3.19/arch/x86/include/asm/processor.h (참고) $ cat /proc/cpuinfo /usr/src/linux-3.19/arch/x86/kernel/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 (362) /* 헤더 파일 mystat.h */ struct mystat { int pid; (32-bit) $ cd /usr/src/ulinux-3.19.1 # vi /usr/include/asm/unistd_32.h #define __NR_getstat 362 # vi /usr/src/linux-3.19.1/arch/x86/syscalls/syscall_32.tbl 362 i386 getstat sys_getstat # vi /usr/src/linux-3.19.1/include/linux/syscalls.h asmlinkage long sys_getstat(int id, struct mystat *user_buf); # vi /usr/src/linux-3.19.1/kernel/getstat.c /* 커널 코드 /usr/src/mylinux/kernel/getstat.c 코딩*/ # vi /usr/src/linux-3.19.1/kernel/Makefile 수정 obj -y = 라인 마지막에 getstat.o 추가 # cd /usr/src/linux-3.19.1 # make bzImage # cp arch/x86/boot/bzImage /boot/vmlinuz-3.19.1 # vi /boot/grub/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_getstat, 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 = %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() 시스템 호출의 흐름 2019-05-09 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)) 2019-05-09 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 vi 에서 소스코드 복사 후 Tab 처리 :%s/^I//g /* ^I는 Tab 키 입력 */ $(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
/. [교재오류본] wrapping. c. / #include <linux/init /* [교재오류본] 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" );
/. [김준하 수정본] wrapping. c 코딩. / #include <linux/init /* [김준하 수정본] 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); static void disable_page_protection(void); static void enable_page_protection(void); static void disable_page_protection(void){ unsigned long value; asm volatile("mov %%cr0, %0":"=r"(value)); if(value & 0x00010000){ value &= ~0x00010000; asm volatile("mov %0, %%cr0"::"r"(value)); } static void enable_page_protection(void){ if(!(value & 0x00010000)){ value |= 0x00010000; asmlinkage int sys_our_open( char *fname, int flags, int mode){ printk(KERN_ALERT "init\n"); printk(KERN_ALERT "%s file is opened by %d\n", fname, org_sys_getuid()); printk(KERN_ALERT "exit\n"); return(org_sys_open(fname, flags, mode)); static void **find_system_call_table(void){ unsigned long ptr; unsigned long init_val = 0xc1000000; //modified extern int loops_per_jiffy; unsigned long *p; //ptr = (unsigned long) &loops_per_jiffy; //modified //printk(KERN_ALERT "init_val : %p \n", ptr); for(ptr = init_val ; ptr< (unsigned long)&loops_per_jiffy ; ptr += sizeof(void *)){ //modified p=(unsigned long * )ptr; //printk(KERN_ALERT "for init\n"); if(p[6] == (unsigned long)sys_close){ //printk(KERN_ALERT "if init\n"); //printk(KERN_ALERT "p[6] : 0x%p\n",p[6]); return (void**)p; return NULL; int __init sct_init(void){ sys_call_table = find_system_call_table(); //printk(KERN_ALERT "sys_call_table : 0x%p\n", (void*)sys_call_table); if(sys_call_table != NULL){ printk(KERN_ALERT "I think sys_call_table is at 0x%p\n", sys_call_table); org_sys_open = sys_call_table[__NR_open]; //printk(KERN_ALERT "sys_call_table[__NR_open] = %p\n", sys_call_table[__NR_open]); disable_page_protection(); sys_call_table[__NR_open] = sys_our_open; org_sys_getuid = sys_call_table[__NR_getuid]; enable_page_protection(); }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 문자 디바이스 프로그래밍
(Hardware Abstraction Layer) 디바이스 드라이버 하드웨어에 대한 추상화를 제공 리눅스에서는 가상 파일 시스템(VFS)을 사용하여 하드웨어 추상화를 지원 HAL (Hardware Abstraction Layer) VFS (Virtual File System 2019-05-09 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) 블록 디바이스 # df –h –T # ls –l /dev sda* 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/sda > backup # cat backup > /dev/sda # grub-install /dev/sda (부트 로더 복구 명령) C:\> fdisk /mbr (Windows 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
주요 디바이스의 주번호 2019-05-09 Hanbit Media(c)
디바이스 드라이버 함수 디바이스 번호 # cat /proc/devices Major 8-bit 256개 (0~255) typedef unsigned short kdev_t; Minor 8-bit 256개 (0~255) /dev/sdaXX 259 blkext ? 확인할 것 # fdisk -l # cat /usr/src/linux-3.19.1/include/linux/uapi/include/major.h 디바이스 드라이버는 모듈로 개발 why ? 디바이스 드라이버 기본 함수 정의 fs.h struct file_opeations { … }; 커널 소스에서 파일 기본 함수 정의 소스 코드를 찾아보세요 !
리눅스의 디바이스 드라이버 구성 2019-05-09 Hanbit Media(c)
문자 디바이스 개발 과정 2019-05-09 Hanbit Media(c)
각 함수의 호출 관계 2019-05-09 Hanbit Media(c)
struct file_operations (include/linux/fs.h) 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); };
문자 디바이스 드라이버의 구조 2019-05-09 Hanbit Media(c)
virtual_device1 작성 (32-bit) $ cd /root # vi virtual_device1.c # vi virtual_app1.c # vi Makefile # make # insmod virtual_device1.ko # ls –l /dev/virtual* # mknod /dev/virtual_device c 250 0 # (gcc virtual_device_app1.c –o virtual_device_app1) # ./virtual_app1 # dmesg | tail # cat > /dev/virtual_device # cat /dev/virtual_device /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = virtual_device1.o TARGETS = virtual_device_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
/* virtual_device_app1.c */ #include <stdio.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_device_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;
커널의 메모리 할당 kmalloc() # vi /usr/src/linux-3.19.1/include/linux/slab.h 412행 kmalloc # vi /usr/src/linux-3.19.1/include/linux/gfp.h 109행 #define GFP_KERNEL (__GFP_WAIT | __GFP_IO | __GFP_FS)
구조체 함수 포인터 선언 방법 구조체 함수 포인터 선언 방법의 차이는 커널 버전의 차이가 아닌 C90/99/GCC 확장의 차이 구조체 선언이 바뀌면 코드도 변경 ANSI C89 (C언어 방식) GCC Extension ANSI C99 (v2.6 이후) static struct file_operations call_fops = { NULL, NULL, xxx_read, xxx_write, NULL, NULL, NULL, NULL, xxx_open, NULL, xxx_release, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static struct file_operations call_fops = { read : xxx_read, write: xxx_write, open: xxx_open, release: xxx_release }; static struct file_operations call_fops = { .read = xxx_read, .write = xxx_write, .open = xxx_open, .release = xxx_release };
IOCTL이란? the Single UNIX specification only as an extension for dealing with STREAMS devices, [Rago, S.A. 1993. UNIX System V Network Programming. Addison-Wesley] 현재는 다양한 디바이스 및 regular files에도 사용하고 있다 read/write 이외의 device specific operations control 디바이스 제어나 상태 설정 읽기/쓰기 처리도 가능 응용 프로그램 명령에 따라 디바이스 드라이버 매개변수 처리를 다르게 할 수 있다
IOCTL 매개변수 전달과정 ioctl() 인자는 커널 내부 처리를 거쳐서 디바이스의 ioctl로 전달된다. ioctl() ➜ sys_ioctl() ➜ dev_ioctl() int dev_ioctl( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg ) { … return ret; } ret = ioctl( int fd, int request, char *argp );
IOCTL 함수의 구조 2019-05-09 Hanbit Media(c)
IOCTL의 cmd 구조 int dev_ioctl( struct inode *inode, struct file *flip, unsigned int cmd, unsigned long arg ) 2019-05-09 Hanbit Media(c)
IOCTL 관련 매크로 1 /usr/src/linux-3.19.1/include/uapi/asm-generic/ioctl.h OS2 강의교재 IOCTL 관련 매크로 1 /usr/src/linux-3.19.1/include/uapi/asm-generic/ioctl.h cmd 명령을 만드는 매크로 _IO: 부가적인 데이터가 없는 명령을 위한 매크로 _IOR : 데이터 읽기(R) 명령을 위한 매크로 _IOW : 데이터 쓰기(W) _IOWR : 데이터 읽기/쓰기 매크로 형태 _IO(매직번호, 구분번호) _IOR(매직번호, 구분번호, 변수형) _IOW(매직번호, 구분번호, 변수형) _IORW(매직번호, 구분번호, 변수형) #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 )
IOCTL 관련 매크로 2 cmd 명령을 해석하는 매크로 매크로 형태 OS2 강의교재 _IOC_NR : 구분 번호 값을 위한 매크로 _IOC_TYPE: 매직번호 값을 위한 매크로 _IOC_SIZE: 데이터의 크기 값을 위한 매크로 _IOC_DIR : 읽기/쓰기 속성 필드의 값을 위한 매크로 매크로 형태 _IOC_NR( 명령 ) _IOC_TYPE( 명령 ) _IOC_SIZE( 명령 ) _IOC_DIR( 명령 ) _IOC_DIR 매크로 반환값 _IOC_NONE _IOC_READ _IOC_WRITE _IOC_READ | _IOC_WRITE if( _IOC_TYPE( cmd ) != MY_IOCTL_NUMBER ) return -EINVAL; if( _IOC_NR( cmd ) >= MY_IOCTL_NR ) return -EINVAL; size = _IOC_SIZE( cmd );
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 또는 compat_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, dev, 1 ); // read ioctl( dev, dev, 2 ); // write ioctl( dev, dev, 3 ); // unknown command close( dev ); return 0; http://www.opensourceforu.com/2011/08/io-control-in-linux/
/. ioctl_app2. c. / #include <stdio. h> #include <stdlib /* ioctl_app2.c */ #include <stdio.h> #include <stdlib.h> //#include <unistd.h> #include <sys/fcntl.h> #include <sys/ioctl.h> /* ioctl macro */ #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; } /* 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 또는 compat_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_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_app2.c –o ioctl_app2) # ./ioctl_app2 # dmesg | tail /* Makefile 코딩*/ KERNELDIR = /lib/modules/$(shell uname -r)/build obj-m = ioctl_device2.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
/* 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_app2.c */ #include <stdio.h> #include <stdlib.h> //#include <unistd.h> #include <sys/fcntl.h> #include <sys/ioctl.h> /* ioctl macro */ #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 # insmod cdev.ko # dmeg | tail (장치번호 확인) # rm /dev/virtual_device # mknod /dev/virtual_device c 252 11 # cat /dev/virtual_device [Ctrl]-C # dmesg | tail # cat > /dev/virtual_device hello[Ctrl]-D (장치번호 동적 할당) # insmod cdev2.ko # dmeg | tail (자동할당된 장치번호 확인) # mknod /dev/virtual_device c 장치번호 부장치번호 /* 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, 11 ); /* 장치번호 수동 할당 */ 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, 12, 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 MD5 (Message Digest V) CryptoAPI 사용하려면 커널 설정 필요 (32-bit) $ cd /root (장치번호 지정) # make menuconfig (Cryptographic algorithm 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 ??? # 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 2019-05-09 Hanbit Media(c)
procfs 주요 항목-1 (비고 명령 확인) 2019-05-09 Hanbit Media(c)
procfs 주요 항목-2 (비고 명령 확인) 2019-05-09 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
timer.c #include <asm/msr.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/proc_fs.h> #include <linux/time.h> #include <linux/jiffies.h> static struct proc_dir_entry *timer_proc = NULL; static int my_read( char *page, char **start, off_t off, int count, int *eof, vo id *data ) { int len = 0; unsigned long my_jiffy = jiffies; //unsigned long my_jiffy = jiffies64; //unsigned long my_jiffy = get_jiffies_64(); unsigned long high_tsc = 0; unsigned long low_tsc = 0; unsigned long cycles = 0; u64 tsc = 0; struct timespec my_timespec; struct timeval my_timeval; len += sprintf( page + len, "[Message] HZ: %d\n", HZ ); len += sprintf( page + len, "[Message] jiffies: %lu\n", my_jiffy ); "[Message] jiffies after 1s: %lu\n", my_jiffy + HZ ); // len += sprintf( page + len, // "[Message] loops_per_jiffy: %lu\n", current_cpu_data.loops_per_jiffy ); "[Message] uptime in min: %lu\n", my_jiffy / HZ / 60 ); "[Message] uptime in sec: %lu\n", my_jiffy / HZ ); // "[Message] xtime : %20lu.%09lu\n", // xtime.tv_sec, xtime.tv_nsec ); my_timespec = current_kernel_time(); my_jiffy = jiffies; // u64 nsec = (u64)jiffies * TICK_NSEC; len+=sprintf( page+len, "Message] timespec: %40llu\n", ((u64)jiffies) * TICK_NSEC ); len += sprintf( page + len, "[Message] timespec: %20lu.%09lu\n", my_timespec.tv_sec, my_timespec.tv_nsec ); do_gettimeofday( &my_timeval ); "[Message] timeval: %10lu.%06lu\n", my_timeval.tv_sec, my_timeval.tv_usec ); cycles = get_cycles(); "[Message] cycles: %lu\n", cycles ); "[Message] cpu_khz: %u\n", cpu_khz ); "[Message] nanoseconds: %lu\n", cycles * (1000 / (cpu_khz / 1000 )) ); // rdtsc( low_tsc, high_tsc ); "[Message] TSC Register High: %lu, Low: %lu\n", high_tsc, low_tsc ); rdtscll( tsc ); "[Message] TSC Register : %llu\n", tsc ); *eof = 1; return len; } int __init timer_init( void ) { timer_proc = create_proc_entry( "timer", 0, NULL ); if( timer_proc == NULL ) return -ENOMEM; timer_proc->read_proc = my_read; // timer_proc->owner = THIS_MODULE; return 0; void __exit timer_exit( void ) remove_proc_entry( "timer", NULL ); module_init( timer_init ); module_exit( timer_exit ); MODULE_LICENSE( "Dual BSD/GPL" );
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() 2019-05-09 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 2019-05-09 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 2019-05-09 Hanbit Media(c)
5. Memory Management of Process vm_flags 값: VM_READ VM_WRITE VM_EXEC VM_SHARED VM_GROWSDOWN VM_GROWUP VM_LOCKED 2019-05-09 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
객체 크기와 슬랩 할당의 관계 2019-05-09 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 정책 2019-05-09 Hanbit Media(c)
inactive list 호출 그래프 2019-05-09 Hanbit Media(c)
active list 호출 그래프 2019-05-09 Hanbit Media(c)
zone과 page 구조체의 관계 2019-05-09 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