리눅스 커널 분석 강사명 : 김정인 email : love1770@ioacademy.co.kr 수업자료 ( 소스 / ppt 자료 ) ftp server : 156.147.178.179 id/passwd : linux / linux 강의 배포자료 : 이동식 디스크 ( 커널수업자료.egg ) 리눅스 커널 분석 1. 완벽한 C 언어 2. 커널 고급 자료구조 ( generic linked list, hash, RB Tree ) 3. 리눅스 시스템 프로그램 ( R. stevens ) 4. 네크워크 프로그램 ( R. stevens ) 5. 가상 파일 시스템 ( file system, driver , inode, file, ... ) 6. 프로세스 ( schedule , context switching , fork, exit, wait.. 7. 메모리 ( paging , buddy system, slab allocator ) ; Multi tasking 3. 시간 만료시 양보됨 (2 2.4GHz 2.4Gbit 0.3Gbyte 0.3*1000*1000*1000 Byte 1000000 명령 PC SP CPU PC SP load ready queue player IRQ=3 ls PC SP schedule(); context_switch(); 0x23 vi PC SP PIC store wait queue 0x27 HZ 10 x80483c4 TEXT DATA STACK int main() { printf("after\n"); } 0x80483c4 4 TEXT DATA STACK TEXT int main() { fork(); printf("after\n"); } DATA STACK 4 TEXT DATA STACK int main() { char *argv[3] = { "ls", "-l", 0 }; execve( "/bin/ls", argv, 0 ); printf("after\n"); return 0; } 31ed5e89 68b0ae05 fff49090 5589e553 8b15cc02 c1f8028d 8d4201a3 e183e4f0 08515668 90909090 83ec0480 0608b8fc 58ff39da cc020608 50545268 20f70408 90909090 3dc80206 fe05082d 731f8db6 ff1485f8 a0ae0508 e887fbff 90909090 08007540 f8fe0508 00000000 fe05088b 0x80483c4 4 TEXT DATA TEXT int main() { char *argv[3] = { "ls", "-l", 0 }; DATA if( fork() == 0 ) execve( "/bin/ls", argv, 0 ); printf("after\n"); return 0; } 31ed5e89 68b0ae05 fff49090 5589e553 8b15cc02 c1f8028d 8d4201a3 STACK e183e4f0 08515668 90909090 83ec0480 0608b8fc 58ff39da cc020608 50545268 20f70408 90909090 3dc80206 fe05082d 731f8db6 ff1485f8 a0ae0508 e887fbff 90909090 08007540 f8fe0508 00000000 fe05088b STACK bash fork(); wait(); bash ls execve(); wait(&status)=>sys_wai a.out fork(); SIGCHLD exit(3); my_sig( a.out exit_code=3 17 TASK_ZOMBIE wait(&status)=>sys_wai exit(7); CORE DUMP 1 1 1 kill(2); 0 1 0 root@ubuntu:~# sleep 1000 ^\Quit root@ubuntu:~# ulimit unlimited root@ubuntu:~# ulimit -c 0 root@ubuntu:~# ulimit -c 1000 root@ubuntu:~# ulimit -c 1000 root@ubuntu:~# sleep 1000 ^\Quit (core dumped) O( n ) list_for_each(tmp, &runqueue_head) { p = list_entry(tmp, struct task_struct, run_l if (can_schedule(p, this_cpu)) { int weight = goodness(p, this_cpu, pr if (weight > c) c = weight, next = p; } } if (p->policy == SCHED_OTHER) { weight = p->counter; if (!weight) goto out; if (p->mm == this_mm || !p->mm) weight += 1; weight += 20 - p->nice; goto out; O(1) struct prio_array { unsigned int nr_active; unsigned long bitmap[BITMAP_SIZE]; struct list_head queue[MAX_PRIO]; }; O(1) , active , expire idx = sched_find_first_bit(array->bitmap); queue = array->queue + idx; next = list_entry(queue->next, task_t, run_list); 31 [0] [1] [2] [3] [4] 1 static inline int sched_find_first_bit(const unsign { if (unlikely(b[0])) return __ffs(b[0]); if (unlikely(b[1])) return __ffs(b[1]) + 32; if (unlikely(b[2])) return __ffs(b[2]) + 64; if (b[3]) return __ffs(b[3]) + 96; return __ffs(b[4]) + 128; } CFS : 완전 공평 스케줄링 schedule(); struct cfs_rq { struct load_weight load; unsigned int nr_running, h_nr_running; u64 exec_clock; u64 min_vruntime; struct rb_root tasks_timeline; struct rb_node *rb_leftmost; }; next = pick_next_task(rq); pick_next_task(struct rq *rq) { const struct sched_class *class; struct task_struct *p; if (likely(rq->nr_running == rq->cfs.h_nr_ru p = fair_sched_class.pick_next_task( if (likely(p)) return p; } } pick_next_task_fair(rq); do { se = pick_next_entity(cfs_rq); set_next_entity(cfs_rq, se); cfs_rq = group_cfs_rq(se); } while (cfs_rq); __pick_first_entity(cfs_rq); struct sched_entity *__pick_first_entity(struct cfs_ { struct rb_node *left = cfs_rq->rb_leftmost; if (!left) return NULL; return rb_entry(left, struct sched_entity, r } struct task_struct { struct sched_entity se; }; struct sched_entity { struct rb_node u64 }; run_node; vruntime; 40 20 10 50 30 60 1. -1 => 105ms , 0 => 100ms , 1 => 95ms , 20 => 5m nice : 0 2개의 process ( IO 전용 프로세스 ) 100ms 한번씩 switching nice : 20 2개의 process ( 계산 전용 프로세스 ) 5 ms 한번씩 switching 2. nice : -1 1개의 process 105 ms ( 51.2% ) nice : 0 1개의 process 100 ms ( 48.7% ) nice : 19 1개의 process 10 ms ( 66.6 % ) nice : 20 1개의 process 5 ms ( 33.3 % ) 3. nice : -19 5개의 ( IO 전용프로세스 ) nice : 20 1개의 ( 계산 전용 프로세스 ) nice weight Wp/Wt time W0/Wp vruntime -10 9548 0.675 6.75 0.107 0.72 -5 3121 0.221 2.21 0.328 0.72 0 1024 0.072 0.72 1 0.72 5 335 0.024 0.24 3.057 0.72 10 110 0.008 0.08 9.309 0.72 합계 14138 1.000 100 ms W0/Wp ((2^32)/1024) >> 32 (2^32)/9548 tmp = SRR(tmp * lw->inv_weight, WMULT_SHIFT); static const int prio_to_weight[40] = { /* -20 */ 88761, 71755, 56483, /* -15 */ 29154, 23254, 18705, /* -10 */ 9548, 7620, 6100, /* -5 */ 3121, 2501, 1991, /* 0 */ 1024, 820, 655, /* 5 */ 335, 272, 215, /* 10 */ 110, 87, 70, /* 15 */ 36, 29, 23, }; 46273, 14949, 4904, 1586, 526, 172, 56, 18, static const u32 prio_to_wmult[40] = { /* -20 */ 48388, 59856, 76040, 92818, /* -15 */ 147320, 184698, 229616, 287308, /* -10 */ 449829, 563644, 704093, 875809, /* -5 */ 1376151, 1717300, 2157191, 2708050, /* 0 */ 4194304, 5237765, 6557202, 8165337, /* 5 */ 12820798, 15790321, 19976592, 24970740, /* 10 */ 39045157, 49367440, 61356676, 76695844, /* 15 */ 119304647, 148102320, 186737708, 238609294, }; static inline struct thread_info *current_thread_inf { register unsigned long sp asm ("sp"); return (struct thread_info *)(sp & ~(THREAD_ } static inline struct task_struct *get_current(void) { return current_thread_info()->task; } #define current (get_current()) struct task_struct *prev = current; task_struct 0x1234000 thread_info CPU 0x1234120 8192 => 10000000000000 8191 => 1111111111111 static inline struct thread_info *current_thread_inf { register unsigned long sp asm ("sp"); return (struct thread_info *)(sp & ~(THREAD_ } /etc/init.d/nfs /tftpboot/zImage-S5PC100 tftp /nfsroot/Ro nfs 69 192.168.10.5 com1 <= putty 192.168.10.3 NT-S5PC100 # print vmware 에서 edit >> virtual network editor # vi app.c #include <stdio.h> int main() { printf("hello target\n"); return 0; } # # # # arm-s5pc1xx-linux-gnueabi-gcc -o app app.c arm-s5pc1xx-linux-gnueabi-objdump -S app file app cp app /nfsroot/RootFS-S5PC100 타겟 보드에서 # cd / # ./app 커널 컴파일 # cd /usr/src/linux # cat .cross_compile arm-s5pc1xx-linux-gnueabi# make ntc100_xwindows_defconfig # make zImage 타겟 보드 재부팅 ( 새로운 커널 이미지 적용 ) system call 추가 # cd /usr/src/linux # cd arch/arm/kernel # vi calls.S /* 335 */ CALL(sys_ni_syscall) /* 335 */ CALL(sys_mycall) # cd /usr/src/linux/kernel # vi mycall.c #include <linux/kernel.h> asmlinkage void sys_mycall(void) { printk("hello kernel\n"); } system call 추가 # vi Makefile obj-y = ... mycall.o # cd /usr/src/linux # make zImage 타겟 보드 부팅 후 확인 system call 호출 app 구현 # vi app.c #include <stdio.h> #include <unistd.h> #include <sys/syscall.h> int main() { // fork(); // swi 90005 syscall(335); } # arm-s5pc1xx-linux-gnueabi-gcc -o app app.c # cp app /nfsroot/RootFS-S5PC100/ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/list.h> asmlinkage void sys_mycall(void) { struct task_struct *p; struct list_head *temp; for( temp = current->tasks.next; temp != &current->tasks; temp=temp->next) { p = list_entry( temp, struct task_struct , task printk("pid=%5d, comm=[%s]\n", p->pid, p->comm } p = list_entry( temp, struct task_struct , tasks ) printk("pid=%5d, comm=[%s]\n", p->pid, p->comm );