5 장프로세스 프로세스와프로세스디스크립터의이해 task_struct 구조체 프로세스생성과소멸 프로세스상태와전이 스케줄링 시그널 한빛미디어 ( 주 )
Section 01 프로세스와프로세스디스크립터의이해 Section 01 프로세스란? 실행중인프로그램 프로세스 = 태스크 (task) 자신에게포함된기계어명령을종료할때까지실행하는동적인존재 살아있는동안많은자원을사용 자원 : CPU, 메모리, 파일그리고시스템내의물리적인장치등 커널과프로세스 커널 : 프로세스관리자 + 자원관리자 커널쓰레드 (kernel thread) 커널모드에서실행되며다른커널쓰레드들과함께자원을공유 독립적인 PID를부여받고스케줄링도독립적으로이루어지는하나의객체» 커널쓰레드도하나의태스크로봐도무방함 2
Section 01 프로세스와프로세스디스크립터의이해 Section 01 프로세스디스크립터 커널은각프로세스가무엇을하고있는지명확히알아야한다. 프로세스의 ID 및우선순위는어떤지, 프로세스가실행상태인지, 어떤사건을기다리며블록된상태인지, 프로세스에어떤주소공간이할당되어있는지, 어떤파일을다룰수있는지,.. process 와관련된모든정보를담는역할 = PCB(Process Control Block) Linux 에서는 task_struct 라는자료구조를사용 current (/usr/src/kernels/mylinux/arch/x86/include/asm/current.h : line 37) 라는전역변수가 task_struct (/usr/src/mylinux/include/linux/sched.h : line 1115) 자료구조를포인팅 3
Section 02 task_struct 구조체 Section 02 구조체를통한정보관리 프로세스가생성되면 task_struct 구조체를통해프로세스의모든정보를저장하고관리 모든태스크들에게하나씩할당 include/linux/sched.h : line 1115 태스크 ID, 상태정보, 가족관계, 명령, 데이터, 시그널, 우선순위, CPU 사용량및파일디스크립터등생성된태스크의모든정보를가짐 alloc_task_struct 매크로를통해커널영역의메모리에서 8KB를할당받아프로세스디스크립터와커널스택의자료를저장 current : 현재실행되고있는태스크를가리키는변수» include/arch/x86/include/asm/current.h : line 37 [ 그림 5-1] 커널스택과프로세스디스크립터 4
Section 02 task_struct 구조체 Section 02 task_struct 구조체를구성하고있는멤버들의정보유형별분류 [ 그림 5-2] task_struct 구조체 5
Section 02 task_struct 구조체 Section 02 태스크식별정보에관련된변수들 pid _ t pid; pid_t pgrp; pid_t session; pid_t tgid; int leader; // PID // 그룹 ID, 일반적으로부모로부터상속 // 세션 (session) ID // 스레드그룹 ID // 세션 leader 태스크그룹과세션리더의예 사용자가콘솔로로그인하여셸을띄웠다고가정 셸도하나의태스크이므로자신의 PID를가지며, 하나의태스크그룹을형성 #ls more 명령을실행하면각명령에대해각각 PID 값을부여받고, 이두명령은하나의태스크그룹을형성 두개의태스크그룹은하나의세션을이루며이때셸은세션리더가됨 6
Section 02 task_struct 구조체 Section 02 태스크들에대한사용자의접근제어에관련된변수 태스크가생성되면사용자의 ID 와사용자가속한그룹의 ID 가등록됨 uid_t uid,euid,suid,fsuid; // 사용자접근제어에이용 gid _ t gid,egid,sgid,fsgid; g, g, g // 사용자그룹에대한접근제어에이용 int ngroups; // 태스크가속한그룹의수 gid_t groups[ngroups]; // 태스크가속한그룹 euid 프로그램이실행될때즉, 태스크가갖는 uid 를의미 예 ) 패스워드변경 수퍼유저의권한으로실행을해야하는경우처럼비록일반사용자가실행하였지만수퍼유저의권한을가짐 7
Section 02 task_struct 구조체 Section 02 태스크상태정보에관련된변수 state 변수 #define TASK_RUNNING 0 #define TASK_INTERRUPTIBLE 1 #define TASK_UNINTERRUPTIBLE 2 #define TASK_ZOMBIE 4 #define TASK_STOPPED 8 TASK_RUNNING 실행중이거나준비상태 TASK_UNINTERRUPT 하드웨어적인조건을기다리는상태, 시그널을받아도무시 예 ) process 가장치파일을열때해당장치드라이버가자신이다룰 하드웨어장치가있는지조사할때, memory swapping TASK_INTERRUPT 하드웨어나시스템자원을사용할수있을때까지기다리고있는대기상태» 예 ) wait for a semaphore TASK_STOPPED 수행중단상태 ( 시그널을받거나트레이싱등 ) TASK_ZOMBIE process 실행은종료했지만아직 process 의정보를반환하지않은상태 8
Section 02 task_struct 구조체 Section 02 procdesc_ptr ->state = TASK_RUNNING; 디스크상태와전이 dispatch running exit fork ready timeout sleep, lock zombie wait wakeup, unlock wait 9
Section 02 task_struct 구조체 Section 02 flags 변수 프로세스의상태플래그 #define PF_ALIGNWARN 0x00000001 /* Print alignment warning msgs */ #define PF_STARTING 0x00000002 /* being created */ #define PF_EXITING 0x00000004 /* getting shut down */ #define PF_ FORKNOEXEC 0x00000040 /* forked but didn't exec */ #define PF_SUPERPRIV 0x00000100 /* used super-user privileges */ #define PF_DUMPCORE 0x00000200 /* dumped core */ #define PF_SIGNALED 0x00000400 /* killed by a signal */ #define PF_ MEMALLOC 0x00000800 /* Allocating memory */ #define PF_MEMDIE 0x00001000 /* Killed for out-of-memory */ #define PF_FREE_PAGES 0x00002000 /* per process page freeing */ #define PF_NOIO 0x00004000 /* avoid generating further I/O */ #define PF_FSTRANS 0x00008000 /* inside a filesystem transaction */ #define PF_USEDFPU 0x00100000 /* task used FPU this quantum (SMP)*/ unsigned long flags; 10
Section 02 task_struct 구조체 Section 02 ptrace 변수 디버깅목적으로사용되는플래그 #define PT_PTRACED #define PT_TRACESYS #define PT_DTRACE #define PT_TRACESYSGOODTRACESYSGOOD #define PT_PTRACE_CAP 0x00000001 0x00000002 0x00000004 0x00000008 0x00000010 스케줄링정보에관련된변수들 volatile long need_resched; // 스케줄링이일어나도록유도하는변수 long counter; // 틱 (tick) 단위로 CPU 시간을할당받기위해사용 long nice; // 우선순위 unsigned long policy; // 스케줄링정책 unsigned long rt_priority; // 실시간태스크우선순위 unsigned long cpus_runnable, cpus_allowed; int processor; // SMP 환경에서스케줄링목적으로사용 11
Section 02 task_struct 구조체 Section 02 태스크관계와관련된변수들 struct task_ struct *p_ opptr, *p_pp pptr, *p_ cptr, *p_ysptr, *p_ osptr; p_opptr과 p_pptr : 부모태스크» p_oppr : Original Parent p_cptr : 자식, p_ysptr : 아우, p_osptr : 형 struct task_struct *next_task, *prev_task; 커널에존재하는모든태스크들은원형이중연결리스트로연결» SET_LINKS : 리스트에추가하는매크로» REMOVE_LINK : 리스트서삭제하는매크로 12
Section 02 task_struct 구조체 Section 02 struct task_struct *pidhash_next; struct task_struct **pidhash_pprev; 태스크를빨리검색하기위해 pidhash 테이블의연결리스트유지 해시함수는 PID 와테이블인덱스사이에일대일대응을보장하지않음 해시테이블 include/linux/sched.h» #define PIDHASH_SZ SZ (4096 >> 2)» extern struct task_struct *pidhash[pidhash_sz];» #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) 시그널정보와관련된변수들 int sigpending; // 블록되지않은대기중인시그널이있는지를표시 struct signal_struct *sig; // 시그널핸들러정보저장 sigset_t blocked; // 블록된시그널표시 struct sigpending pending; // 대기중인시그널리스트 13
Section 02 task_struct 구조체 Section 02 메모리정보와관련된변수들 struct mm_struct *mm; // 태스크가소유한메모리공간에대한정보 struct mm_struct *active_mm; // 태스크실행시사용하는메모리공간에대한정보 일반태스크는동일, 커널스레드는사용자주소공간의메모리영역을사용하지않으므로 mm은 NULL 임 메모리정보와관련된변수들 struct fs_struct *fs; struct files_struct *files; // 파일시스템의정보와관련된변수 // 태스크가오픈한파일들에접근할때사용 14
Section 02 task_struct 구조체 Section 02 문맥교환정보와관련된변수 struct thread_ struct thread; // 문맥을저장하기위한변수 문맥 태스크가생성될때커널이태스크정보를관리하기위해할당하는자료구조 태스크의자원과수행환경집합 시간정보와관련된변수들 struct timer_list real_timer; struct tms times; unsigned long start_time; // 타이머핸들러에대한정보 // 사용자레벨과커널레벨에서의태스크실행시간 // 태스크가생성된시간 15
Section 02 task_struct 구조체 Section 02 자원사용과관련된변수들 struct rlimit rlim[rlim _ NLIMITS]; // 자원의사용현황 char comm[16]; // 태스크의이름 struct rlimit { unsigned long rlim_cur; // 현재설정된허용자원의수 unsigned long rlim_max; // 최대로허용할수있는자원의수 }; /* include/linux/resource.h */ #define RLIMIT_CPU 0 // 프로세스의최대 CPU 사용시간 #define RLIMIT_FSIZE 1 // 최대파일의크기 #define RLIMIT_DATA 2 // 최대데이터세그먼트크기 #define RLIMIT_STACK 3 // 최대스택세그먼트크기 #define RLIMIT_CORE 4 // 코어파일의최대크기 #define RLIMIT_RSS 5 // 최대페이지프레임수 16
Section 02 task_struct 구조체 Section 02 자원사용과관련된변수들 #define RLIMIT_NPROC 6 // 사용자당생성가능한최대프로세스수 #define RLIMIT_NOFILE 7 // 최대로오픈가능한파일의수 #define RLIMIT_MEMLOCK 8 // 최대잠글수있는메모리 #define RLIMIT_AS 9 // 최대주소공간 #define RLIMIT_LOCKS 10 // 최대락 (lock) 의설정수 #define RLIM_NLIMITS 11 // 정의하고있는자원제한수 17
[ 실습 5-1] 태스크 PID 와이름을출력하는시스템콜구현 문제 커널에존재하는모든태스크들의 pid 와이름을출력하는시스템콜을작성해보자. 문제를해결하기위한조건 (1) 커널에존재하는모든태스크들은원형이중연결리스트로연결되어있으며, 시작은 init_task 변수가가리 키고있다. 리스트에서다음노드를가리키는포인터변수는 next_task 다. (2) 프로그래밍의편의를위해모듈로작성해도무방하다. (3) 시스템콜번호는사용되지않고있는번호하나를할당하라. (4) 시스템콜이름은 sys_displaytask 로정의한다. [ 실행화면예 ] 18
[ 실습 5-1] 태스크 PID 와이름을출력하는시스템콜구현 (1) 시스템콜처리함수작성 01 asmlinkage int sys _ displaytask() 02 { 03 int i; 04 struct task_struct *findtask; 05 06 findtask = &init_task; 07 printk( "%s[%d] ", findtask->comm, findtask->pid ); 08 09 findtask = findtask->next_task; 10 while(findtask->pid!= init_task.pid ) 11 { 12 printk( "-> %s[%d]", findtask->comm, findtask->pid ); 13 findtask = findtask->next_task; 14 } 15 16 printk( "-> %s[%d] \n", findtask->comm, findtask->pid ); 17 return 0; 18 } [ 소스 5-1] 19
[ 실습 5-1] 태스크 PID 와이름을출력하는시스템콜구현 1 1행 : 시스템콜처리함수정의 2 6행 ~7행 : init_task 변수를 findtak에저장하고이름과 pid를출력 init_taskit t 는리스트의헤더 3 9행 ~14행 : 다음리스트부터시작하여다시 init_task로돌아올때까지태스크의이름과 pid를출력 4 16행 : 리스트의처음으로되돌아왔음을확인하기위해태스크이름과 pid를출력 사용자응용프로그램에서출력값들을돌려받고, 응용프로그램에서출력하도록코드수정할것 20
[ 실습 5-1] 태스크 PID 와이름을출력하는시스템콜구현 (2) 사용자응용프로그램작성 01 /* app_displaytask.c */ 02... 03 _syscall0(int, displaytask) 04 05 int main() 06 { 07 int i; 08 i = findtask(id); 09 } [ 소스 5-2] (3) Makefile 작성과컴파일 TARGET = displaytask [ 소스 5-3] INCLUDE = -isystem /usr/src/linux-2.4.32/include CFLAGS = -O2 -D KERNEL -DMODULE $(INCLUDE) //CFLAGS = -O2 $(INCLUDE) CC = gcc ${TARGET}.o: ${TARGET}.c clean: rm -rf ${TARGET}.o 21
[ 실습 5-1] 태스크 PID 와이름을출력하는시스템콜구현 (4) 응용프로그램컴파일 #gcc -o app_displaytask app_displaytask.c (5) 실행및결과확인 #insmod displaytask.o #./app_displaytask 22
[ 실습 5-2] 해시함수를이용한태스크찾기 문제 태스크의 pid를인자로전달하고커널에서해시테이블을이용하여해당태스크를찾아해시테이블의인덱스와해당태스크의이름을출력하는시스템콜을작성해보자. 문제를해결하기위한조건 (1) 해시함수와해시테이블을이해해야한다. 이는본문을참조하라. (2) 프로그래밍의편의를위해모듈로작성해도무방하다. (3) 시스템콜번호는현재사용되지않는번호하나를할당하라. (4) 시스템콜이름은 sys_findtask로정의한다. [ 실행화면예 ] 존재하는태스크에대한결과 존재하지않는태스크에대한결과 23
[ 실습 5-2] 해시함수를이용한태스크찾기 (1) 시스템콜처리함수작성 01 asmlinkage int sys_findtask(int id) 02 { 03 int hash_index=0; 04 struct task_struct *findtask; 05 06 hash_index = pid_hashfn(id); 07 findtask = pidhash[hash_index]; h[h h i d 08 09 if (findtask == NULL ) { 10 printk( "Task not exist \n" ); 11 return (-1); 12 } 13 14 while( findtask->pid!= id ) 15 { 16 findtask = findtask->pidhash >pidhash_next; 17 if (findtask == NULL ) { 18 printk( "Task not exist \n" ); 19 return(-1); 20 } 21 } 22 printk("hash table index=%d, pid=%d, Task Name is %s \n", hash_index, findtask->pid, findtask->comm ); 23 24 return 0; 25 } [ 소스 5-4] 24
[ 실습 5-2] 해시함수를이용한태스크찾기 (1) 시스템콜처리함수작성 ( 계속 ) 1 1 행 :pid 를인자로받는시스템콜처리함수를정의 2 6행 ~7행 : 해시함수를이용하여해시테이블의인덱스를찾고이인덱스에대응하는테이블의내용을 findtask에저장 3 9 행 ~12 행 : findtask 가 NULL 이면연결된태스크가없으므로 -1 을리턴값으로하여복귀 4 14행 ~22행 : 하나의인덱스에여러개의태스크가연결되어있을수있으므로리스트를탐색 만약찾게되면루프를벗어나고마지막까지찾지못하면복귀. 5 23 행 : 해시테이블에존재하는경우해시테이블인덱스, PID, 태스크이름출력 사용자응용프로그램에서출력값들을돌려받고, 출력하도록코드수정할것 25
[ 실습 5-2] 해시함수를이용한태스크찾기 (2) 사용자응용프로그램작성 01 /* app_findtask.c */ 02... 03 _syscall1(int, findtask, int, id) 04 05 int main(int argc, int *argv[]) 06 { 07 int i, id; 08 09 id = atoi(argv[1]); 10 i = findtask(id); 11 } [ 소스 5-5] (3) Makefile 작성과컴파일 (4) 실행및결과확인 #insmod findtask.o #./app_findtask 1 26
Section 03 프로세스생성과소멸 Section 03 생성 fork() sys_fork( ) do_fork( ) arch/x86/kernel/process_32.c 596 행 do_fork( ) (kernel/fork.c 1337 행 ) task_structstruct 를확보하여생성되는태스크에게할당및초기화 PID를할당및 task_struct의각멤버변수들을초기화 새로운자료구조생성 1386행 : copy_process() (935행) 호출 () copy_files( ), copy_fs( ), copy_sighand( ), copy_mm( ) 등호출 실행큐에삽입 do_fork 주요코드 1 task_struct 구조체확보및공간할당 p = alloc_task_struct(); struct(); 2 부모프로세스디스크립터를자식프로세스디스크립터에복사 *p =*current; 27
Section 03 프로세스생성과소멸 Section 03 3 새로운태스크생성을위한자원검사 if (atomic_read(&p->user->processes) >= p->rlim[rlimit_nproc].rlim_cur && p->user!= &root_user &&!capable(cap_sys_admin) &&!capable(cap_sys_resource)) goto bad_fork_free; atomic_inc(&p->user-> count); atomic_ inc(&p->user->processes); p if (nr_threads >= max_threads) goto bad_fork_cleanup_count; 4 실행도메인검사및커널모듈참조검사 get_exec_domain(p->exec_domain); if (p->binfmt && p->binfmt->module) MOD_INC_USE_COUNT(p->binfmt->module); 28
Section 03 프로세스생성과소멸 Section 03 5 flag 필드갱신 copy_flags(clone_flags, p); 6 PID 할당 p->pid = get_pid(clone_flags); 7 프로세스디스크립터갱신 p->run >run_list.next next = NULL; p->run_list.prev = NULL; p->p_cptr = NULL;... p->sigpending = 0; init_sigpending(&p->pending);... init_timer(&p->real_timer); p->real >real_timer.data = (unsigned long) p; p->leader = 0; /* session leadership doesn t inherit */ p->tty_old_pgrp = 0; p->times.tms_utime = p->times.tms_stime = 0; p->times.tms >times.tms_cutime = p->times.tms >times.tms_cstime = 0; 29
Section 03 프로세스생성과소멸 Section 03 8 부모프로세스의자료복사 if (copy_files(clone_flags, p)) goto bad_fork_cleanup; if (copy_fs(clone_flags, p)) goto bad_fork_cleanup_files; if (copy_sighand(clone_flags, p)) goto bad_fork_cleanup_fs; if (copy_mm(clone_flags, p)) goto bad_fork_cleanup_sighand; retval = copy_namespace(clone_flags, p); retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs); 9 counter 값설정 p->counter = (current->counter + 1) >> 1; current->counter >>= 1; if (!current->counter) current->need_resched = 1; 30
Section 03 프로세스생성과소멸 Section 03 10 태스크리스트에추가 SET_LINKS(p); #define SET_LINKS(p) do { \ (p)->next_task = &init_task; \ (p)->prev_task = init_task.prev_task; \ init_task.prev_task->next_task = (p); \ init_task.prev_task = (p); \ (p)->p_ysptr = NULL; \ if (((p)->p_osptr = (p)->p_pptr->p_cptr) > t )!= NULL) \ (p)->p_osptr->p_ysptr = p; \ (p)->p_pptr->p_cptr = p; \ } while (0) 11 counter 값설정 hash_pid(p); 12 태스크의상태변경 wake_up_process(p); 31
Section 03 프로세스생성과소멸 Section 03 소멸 exit( ), kill(), 비정상적인작업을수행에의해소멸 do_exit( ) kernel/exit.c (kernel/exit.c 976 행 ) 프로세스에게할당된각종자료구조들을해제 프로세스를좀비 (zombie) 상태로변경 스케줄러호출 do_exit 주요코드 1 현재태스크를 tsk 변수에저장 NORET_TYPE TYPE void do_exit(long code) { struct task_struct *tsk = current; 2 현재태스크의타이머를중지시킴 del_timer_sync(&tsk->real_timer); 32
Section 03 프로세스생성과소멸 Section 03 3 태스크가사용하던가상메모리해제 exit_mm(tsk); 4 세마포어큐에서제거 sem_exit(); 5 태스크가열었던파일디스크립터제거 exit_files(tsk); 6 파일시스템정보제거 exit_fs(tsk); 7 네임스페이스정보제거 exit_namespace(tsk); 8 시그널의핸들러제거 exit_sighand(tsk); 33
Section 03 프로세스생성과소멸 Section 03 9 태스크가세션리더인경우가상터미널해제 disassociate_ctty(1); 10 태스크가사용하던모듈이있으면참조횟수를감소시킴 if (tsk->binfmt && tsk->binfmt->module) MOD_DEC_USE_COUNT(tsk->binfmt->module); 11 프로세스종료통보 exit_notify(); 12 스케줄러호출 schedule(); 34
Section 04 프로세스상태와전이 Section 04 태스크전이과정 [ 그림 5-5] 태스크상태와전이 [ 그림 5-6] #man ps 내의태스크상태설명부분 35
Section 05 스케줄링 스케줄링 실행큐에들어있는실행가능한태스크중한태스크에게 CPU 할당 schedule() hdl() kernel/sched.c FIFO, 라운드로빈, 우선순위방식사용 task_struct 내의 priority 변수 SCHED_FIFO 실시간태스크를위한정책, 높은우선순위할당, 비선점 SCHED_RR 실시간태스크를위한정책, 높은우선순위할당, 선점, 라운드-로빈방식, rt_priority 변수사용 SCHED_OTHER 비실시간의일반태스크들을위한정책, 참고 : 커널 2.6 에서는 SCHED_ NORMAL로이름이변경 36
Section 05 스케줄링 비실시간태스크의우선순위 -20~19 ( 값이작을수록높은우선순위를가짐, 기본값 0 ) $ ps el more 로확인 (BSD 포맷 :$ps ax more) $ man ps로출력의미확인 task_struct 내의 nice 변수 DEF_NICE 기본우선순위값을설정하는매크로 task_struct 내의 nice 변수 [2.6] #define TASK_NICE(p) PRIO_TO_NICE((p)->static_prio) tti i)(kernel/sched.c hd 89행 ) 우선순위를높이는명령예 : $ nice --20 test (super user 만가능 ) 일반사용자는우선순위낮춰실행시키는것만가능 : $ nice -5 sleep 100 & 우선순위와관련된시스템콜 setpriority(), getpriority(), sched_setparam(), sched_getparam() 스케줄링정책과관련된시스템콜 sched_setscheduler() 와 sched_getscheduler() 37
Section 05 스케줄링 $ man ps PROCESS STATE CODES Here are the different values that the s, stat and state output specifiers (header "STAT" or "S") will display to describe the state of a process. D Uninterruptible sleep (usually IO) R Running or runnable (on run queue) S Interruptible sleep (waiting for an event to complete) T Stopped, either by a job control signal or because it is being traced. W paging (not valid since the 2.6.xx kernel) X dead (should never be seen) Z Defunct ("zombie") process, terminated but not reaped by its parent. For BSD formats and when the stat keyword is used, additional characters may be displayed: < high-priority (not nice to other users) N low-priority y( (nice to other users) L has pages locked into memory (for real-time and custom IO) s is a session leader l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do) + is in the foreground process group 38
Section 05 스케줄링 기본 (default) 타임퀀텀 : 100 millisec DEF_TIMESLICE 매크로 (kernel/sched.c 114행 ) #define DEF_TIMESLICE (100 * HZ / 1000) ( 예 ) Hz : 100, 1틱 = 1/100 = 10ms 최대할당타임퀀텀 MAX_COUNTER 매크로 우선순위에따라타임퀀텀을달리설정 (kernel/sched.c 87 행 ) /* * Convert user-nice values [ -20... 0... 19 ] * to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ], * and back. */ #define NICE_TO_PRIO(nice) (MAX_RT_PRIO + (nice) + 20) #define PRIO_TO_NICE(prio) ((prio) - MAX_RT_PRIO - 20) #define TASK_NICE(p) PRIO_TO_NICE((p)->static_prio) 39
Section 06 시그널 시그널 태스크들간통신을위한 IPC(Inter Process Communication) 기법중하나시그널종류 include/asm-i386/signal.h [2.6] arch/x86/include/asm/signal.hh [ 그림 5-7] #kill -l 수행화면 40
Section 06 시그널 시그널과관련된시스템콜 태스크들간통신을위한 IPC(Inter Process Communication) 기법중하나시그널종류 include/asm-i386/signal.h [2.6] arch/x86/include/asm/signal.hh [ 표 5-1] 시그널과관련된시스템콜 이름 sys_kill() 처리내용 시그널전달 sys_signal() 시그널과관련된동작설명 ( 핸들러설정 ) sys_sigaction() 시그널과관련된동작설명 ( 핸들러설정 ) sys_sigpending() sigpending() sys_sigprocmask() sys_sgetmask() sys_ssetmask() sys_rt_sigaction() sys_rt_sigqueueinfo() sys_rt_sigprocmask() sys_rt_sigpending() sigpending() sys_rt_sigtimewait() pending 된시그널존재여부검사블록된시그널목록수정블록된시그널의마스크값획득블록된시그널의마스크값설정실시간시그널과관련되어취해야할 action 설정실시간시그널전달블록된실시간시그널목록변경 pending 된실시간시그널존재여부검사실시간시그널에 timeout을설정하여대기 41
Section 06 시그널 시그널처리를위한 3 가지기능 시그널전송기능 시그널수신기능 시그널처리기능 시그널전송 일반태스크에서시그널을보내는시스템콜 kill() sys_kill() 커널에서시그널을보내는함수 force_sig() send_sig_info() 시그널을보내는쪽은단지 시그널을통보할뿐임 [ 그림 5-8] 시그널전송시함수호출흐름 42
Section 06 시그널 send_signal() 시그널번호와관련정보및수신태스크 (task_struct의 sigpending 구조체 ) 를인수로받아수신태스크에게시그널이도착했음을알림 1 현재시스템에존재하는시그널의개수가제한된수보다적다면시그널큐에삽입할노드를생성 제한된수 : max_queued_signal = 1024 로정의 2 이노드에시그널번호를포함하여관련정보들을저장한다음시그널큐의꼬리에삽입 이때관련정보에는커널이전송하였는지사용자태스크에서전송하였는지의정보도포함 3 시그널의종류를구분하기위한자료구조 (sigset_t t signal) 에관련비트를표시 43
Section 06 시그널시그널수신 시그널을받을수있는자료구조필요 int sigpending; struct signal_struct *sig; sigset_t blocked; struct sigpending pending; sig : 시그널을받았을때처리할핸들러정보저장 pending : 시그널처리를기다리는시그널저장 시그널전송부분에서노드를만들어이곳의연결리스트에추가 sigpending : 블록되지않은대기중인시그널이있는지표시 blocked : 블록된시그널을표시 44
Section 06 시그널시그널관련자료구조 [ 그림 5-9] 시그널관련자료구조 45
Section 06 시그널 sigpending 구조체 include/linux/signal.h head : 시그널큐의첫번째노드를가리키는변수 tail : 수신된시그널의마지막노드를가리키는변수 signal : pending된시그널을비트마스크하기위한변수 struct sigpending { struct sigqueue *head, **tail; sigset _ t signal; }; sigqueue 구조체 next : 시그널큐의다음노드를가리키기위한변수 시그널큐의마지막노드의 next 포인터는 NULL info : 수신된시그널의정보를담는변수 struct sigqueue { struct sigqueue *next; siginfo_t info; }; 46
Section 06 시그널 sigset_t 구조체 _NSIG : 시스템내의총시그널개수 sigset_t t 에 64bit 의공간을할당하여각시그널에대응하는비트를마스크 이구조체를사용하는 blocked 변수는블록된시그널을해당비트에표시하여블록된시그널이발생할경우해당시그널의블로킹이해제될때까지대기상태로있도록한다. k_sigaction 구조체의 sa_mask 변수도이구조체로되어있는데이는시그널핸들러가수행될때블록시킬시그널을비트마스크하기위한용도다 #define _NSIG 64 #define _NSIG_BPW 32 #define _NSIG_WORDS (_NSIG / _NSIG_BPW) typedef struct { unsigned long sig[_nsig_words]; } sigset_t 47
Section 06 시그널 signal_struct 구조체 include/linux/sched.h 427 행에정의 1273 행 task_struct 에서사용 count t: 이구조체를공유하는태스크의수 siglock : 이구조체에배타적인접근을위해 lock을설정하는변수 action : k_sigaction 구조체 (arch/x86/include/asm/signal.h 144행 ) 로선언되어있으며다시 sa 변수를사용하여 sigaction i 구조체를가짐 64개의배열로구성되고수신한각시그널에대한처리방법을저장 처리방법» 시그널의블록이해제된후처리» 시그널무시» 시그널처리 struct sighand_struct { atomic_t count; struct k_sigaction action[_nsig]; spinlock_t siglock; wait_queue_head_t signalfd_wqh; }; 48
Section 06 시그널 sigaction 구조체 (arch/x86/include/asm/signal.h 137 행 ) sa_handler : 시그널핸들러를가리키는변수 만약핸들러가지정되지않은경우는기본동작을지시 (SIG_DFL) 하거나무시 (SIG_IGN) 하는값을갖는다. sa_flags : 시그널처리방법을지시하는플래그값이다. SA_SIGINFOSIGINFO» 시그널처리기가한개가아닌 3개의인수를취한다. 이경우, sa_handler 대신에 sa_sigaction 이설정되어야한다. (sa_sigaction 필드는리눅스 2.1.86에서추가되었다.) sa_restorer : 사용되지않음 sa_mask : 시그널을마스킹하는데사용 struct sigaction { }; sighandler_t sa_handler; unsigned long sa_flags; sigrestore_t sa_restorer; sigset_t sa_mask; /* mask last for extensibility */ struct k_sigaction { }; struct sigaction sa; 49
Section 06 시그널 시그널처리 커널은시그널이발생하면즉각처리하지않음 커널모드에서인터럽트나예외를처리하고난후사용자모드로돌아갈때처리할시그널이존재하는지확인 ENTRY(ret_from_sys_call) cli # need_resched and signals atomic test... cmpl $0,sigpending(%ebx) jne signal_return... signal_return:... call SYMBOL_NAME(do_signal) jmp restore_all 50
Section 06 시그널 do_signal( ) arch/i386/kernel/signal.c [2.6] arch/x86/kernel/signal.c l 916 행 [ 그림 5-10] 시그널처리흐름 51
Section 06 시그널 SIG_DFL 기본처리 Terminate : 태스크를종료시킴 Dump : 태스크를종료시키고 Core Dump 함 Stop : 태스크의수행을중지시킴 Continue : 태스크수행재개 52
[ 실습 5-3] 시그널전송및처리 문제 사용자프로그램에서시그널핸들러를설정하고시그널을수신하였을때정의된시그널핸들러가수행되도록하자. (1) 시그널수신용응용프로그램작성 01 #include <stdio.h> 02 #include <signal.h> 03 #include <unistd.h> 04 05 void signal_handler(int); 06 07 void main( ) 08 { 09 int c; 10 int pid; 11 pid = getpid(); 12 printf( "pid = %d \n", pid ); 13 14 signal(sigusr1, signal_handler); 15 signal(sigint, signal_handler ); [ 소스 5-6] 16 while ((c=getchar( ))!='\n') ; 17 } 18 void signal_handler(int signo ) 19 { 20 switch(signo) 21 { 22 case SIGUSR1: 23 printf("received SIGUSR1 \n"); 24 break; 25 case SIGINT: 26 printf("received SIGINT \n"); 27 break; 28 } 29 } 53
[ 실습 5-3] 시그널전송및처리 1 12 행 : 시그널을보낼태스크에서이태스크의 PID 를사용하기위해실행시 PID 를출력하도록한다. 2 14 행 ~15 행 :SIGUSR1 과 SIGINT 시그널이수신되면이를처리할핸들러를 signal_handler 함수로지정한다. 예 사용자가 ctrl+c 를누르면 SIGINT 시그널이발생하는데, 이핸들러가지정되어있지않으면기본처리로태스크가종료됨 핸들러의지정에따라콘솔에문자열을출력할뿐태스크가종료되지않음. 3 16행 : 사용자가 Enter 키를입력할때까지무한루프를실행하며시그널대기 4 20행 ~28행 : 사용자가지정한두개의시그널에대해처리하는시그널핸들러. 시그널번호에따라 case 문을수행한다. 54
[ 실습 5-3] 시그널전송및처리 (2) 시스템송신용응용프로그램작성 01 #include <stdio.h> 02 #include <signal.h> 03 #include <unistd.h> 04 int count=0; 05 main(int argc, int *argv[ ]) 06 { 07 int c; 08 int rec_pid; 09 10 rec_pid = atoi(argv[1]); 11 for(c=0; c<10; c++) { 12 kill(rec_pid, SIGUSR1 ); 13 sleep(1); 14 } 15 } [ 소스 5-7] 55
[ 실습 5-3] 시그널전송및처리 (3) 컴파일및실행 두응용프로그램을컴파일하고실행 두개의터미널을띄워각각을실행하여해당문자열이출력되는지확인. 수신용태스크실행화면에서 ctrl+c를눌러시그널핸들러의작동여부확인. [ 수신태스크실행화면 ] [ 송신태스크실행화면 ] 56
[ 실습 5-4] sigaction 시스템콜을이용한시스템핸들러설정 문제 sigaction 시스템콜을사용하여응용프로그램에서시그널핸들러를지정하는방법과지정된시그널을무시하거나원래의기본처리형태를지정하는방법을알아본다. (1) 응용프로그램작성 01 #include <stdio.h> 02 #include <signal.h> 03 #include <unistd.h> 04 int count=0; 05 void signal_handler(int); 06 main( ) 07 { 08 struct sigaction sact; 09 10 printf( "(1) Ignore SIGINT signal \n" ); 11 sact.sa_handler = SIG_IGN; 12 sigaction(sigint, &sact, NULL); 13 while ((c=getchar( ))!='\n'); 14 [ 소스 5-8] 57
[ 실습 5-4] sigaction 시스템콜을이용한시스템핸들러설정 (1) 응용프로그램작성 ( 계속 ) 15 printf( "(2) User defined Handler \n" ); 16 sact.sa_handler = signal_handler; 17 sact.sa_flags = SA_SIGINFO; 18 sigaction(sigint, &sact, NULL); 19 while ((c=getchar())!='\n'); 20 21 printf( "(3) default SIGINT signal processing \n" ); 22 sact.sa_handler = SIG_DFL; 23 sigaction(sigint, &sact, NULL); 24 while ((c=getchar())!='\n'); 25 26 } 27 void signal_handler() 28 { 29 printf("received SIGINT \n"); 30 } [ 소스 5-8] 58
[ 실습 5-4] sigaction 시스템콜을이용한시스템핸들러설정 1 10 행 ~13 행 : 시그널무시를지정하는부분 사용자가 Enter 키를누를때까지 while 루프를돌면서시그널을대기한다. 만약 ctrl+c 를눌러 SIGINT 시그널을받더라도 sa_handler = SIG_IGNIGN 로설정하였으므로무시된다. 2 15행 ~19행 : 사용자가정의한핸들러를수행 Enter 키를눌러다음문으로넘어가기전에는지정된핸들러를통해 SIGINT 시그널을처리한다. 3 21 행 ~ 24 행 : 원래의기본처리방법지정 ctrl+c 가눌려지면기본처리에따라태스크는종료할것이다. (2) 실행및결과확인 59