550 likes | 771 Views
Interrupt Programming in Linux. 경희대학교 컴퓨터공학과 조 진 성. 주요 내용. 주요 내용 인터럽트 개념 PXA255 CPU 에서의 인터럽트 하드웨어 리눅스에서의 인터럽트 처리 인터럽트 프로그래밍 예. intr request. status reg. CPU. intr ack. mechanism. IR. PC. data/address. data reg. 인터럽트 인터페이스. 임베디드 시스템의 실시간성 요구에 필수적인 요소. 인터럽트.
E N D
Interrupt Programming in Linux 경희대학교 컴퓨터공학과 조 진 성
주요 내용 • 주요 내용 • 인터럽트 개념 • PXA255 CPU에서의 인터럽트 하드웨어 • 리눅스에서의 인터럽트 처리 • 인터럽트 프로그래밍 예
intr request status reg CPU intr ack mechanism IR PC data/address data reg 인터럽트 인터페이스 • 임베디드 시스템의 실시간성 요구에 필수적인 요소
인터럽트 • Suppose a peripheral intermittently receives data, which must be serviced by the processor • The processor can poll the peripheral regularly to see if data has arrived – wasteful • The peripheral can interrupt the processor when it has data • Requires an extra pin or pins: Int • If Int is 1, processor suspends current program, jumps to an Interrupt Service Routine (ISR) • Known as interrupt-driven I/O • Essentially, “polling” of the interrupt pin is built-into the hardware, so no extra time!
인터럽트 • What is the address (interrupt address vector) of the ISR? • Fixed interrupt • Address built into microprocessor, cannot be changed • Either ISR stored at address or a jump to actual ISR stored if not enough bytes available • Vectored interrupt • Peripheral must provide the address • Common when microprocessor has multiple peripherals connected by a system bus • Compromise: interrupt address table
1(a): μP is executing its main program. 1(b): P1 receives input data in a register with address 0x8000. Time 2:P1 asserts Int to request servicing by the microprocessor. 3:After completing instruction at 100, μP sees Int asserted, saves the PC’s value of 100, and sets PC to the ISR fixed location of 16. 4(a): The ISR reads data from 0x8000, modifies the data, and writes the resulting data to 0x8001. 4(b): After being read, P1 de-asserts Int. 5: The ISR returns, thus restoring PC to 100+1=101, where μP resumes executing. 인터럽트 기반I/O(fixed ISR location)
μP Data memory Program memory ISR 16: MOV R0, 0x8000 System bus 17: # modifies R0 18: MOV 0x8001, R0 19: RETI # ISR return ... P1 P2 Int Main program ... PC 0x8000 0x8001 100: instruction 101: instruction 인터럽트 기반I/O(fixed ISR location) 1(a): P is executing its main program 1(b): P1 receives input data in a register with address 0x8000.
μP Data memory Program memory ISR 16: MOV R0, 0x8000 System bus 17: # modifies R0 18: MOV 0x8001, R0 19: RETI # ISR return ... P1 P2 Int Main program 1 ... PC 0x8000 0x8001 100: instruction 101: instruction 인터럽트 기반I/O(fixed ISR location) 2: P1 asserts Int to request servicing by the microprocessor Int
μP Data memory Program memory ISR 16: MOV R0, 0x8000 System bus 17: # modifies R0 18: MOV 0x8001, R0 19: RETI # ISR return ... P1 P2 Int Main program ... PC 0x8000 0x8001 100: instruction 100 100 101: instruction 인터럽트 기반I/O(fixed ISR location) 3: After completing instruction at 100, P sees Int asserted, saves the PC’s value of 100, and sets PC to the ISR fixed location of 16.
μP Data memory Program memory ISR 16: MOV R0, 0x8000 System bus System bus 17: # modifies R0 18: MOV 0x8001, R0 19: RETI # ISR return ... P1 P1 P1 P2 P2 Int Int Main program 0 ... PC 0x8000 0x8001 0x8001 100: instruction 101: instruction 0x8000 인터럽트 기반I/O(fixed ISR location) 4(a): The ISR reads data from 0x8000, modifies the data, and writes the resulting data to 0x8001. 4(b): After being read, P1 deasserts Int. 100
μP Data memory Program memory ISR ISR 16: MOV R0, 0x8000 System bus 16: MOV R0, 0x8000 17: # modifies R0 17: # modifies R0 18: MOV 0x8001, R0 18: MOV 0x8001, R0 19: RETI # ISR return ... 19: RETI # ISR return P1 P2 Int ... Main program ... Main program PC 0x8000 0x8001 ... 100: instruction +1 100: instruction 100 100 100 101: instruction 101: instruction 인터럽트 기반I/O(fixed ISR location) 5: The ISR returns, thus restoring PC to 100+1=101, where P resumes executing.
1(a): μP is executing its main program. 1(b): P1 receives input data in a register with address 0x8000. Time 2:P1 asserts Int to request servicing by the microprocessor. 3:After completing instruction at 100, μP sees Int asserted, saves the PC’s value of 100, and asserts Inta. 4:P1 detects Inta and puts interrupt address vector 16 on the data bus. 5(a):μP jumps to the address on the bus (16). The ISR there reads data from 0x8000, modifies the data, and writes the resulting data to 0x8001. 5(b): After being read, P1 deasserts Int. 6: The ISR returns, thus restoring PC to 100+1=101, where μP resumes executing. 인터럽트 기반I/O(vectored interrupt)
μP Data memory Program memory ISR 16: MOV R0, 0x8000 System bus 17: # modifies R0 18: MOV 0x8001, R0 19: RETI # ISR return ... P1 P2 Inta Main program Int 16 ... PC 100: instruction 0x8000 0x8001 100 101: instruction 인터럽트 기반I/O(vectored interrupt) 1(a): P is executing its main program 1(b): P1 receives input data in a register with address 0x8000.
μP Data memory Program memory ISR 16: MOV R0, 0x8000 System bus 17: # modifies R0 18: MOV 0x8001, R0 19: RETI # ISR return ... Inta P1 P2 Int Main program 16 ... 1 PC 100: instruction 0x8000 0x8001 100 101: instruction 인터럽트 기반I/O(vectored interrupt) 2: P1 asserts Int to request servicing by the microprocessor Int
μP Data memory Program memory ISR 16: MOV R0, 0x8000 System bus 17: # modifies R0 18: MOV 0x8001, R0 19: RETI # ISR return 1 ... Inta Inta P1 P2 Int Main program 16 ... PC 100: instruction 0x8000 0x8001 100 100 101: instruction 인터럽트 기반I/O(vectored interrupt) 3: After completing instruction at 100, μP sees Int asserted, saves the PC’s value of 100, and asserts Inta
μP Data memory Program memory ISR 16: MOV R0, 0x8000 System bus System bus 17: # modifies R0 16 18: MOV 0x8001, R0 19: RETI # ISR return ... Inta P1 P2 Int Main program 16 16 ... PC 100: instruction 0x8000 0x8001 101: instruction 인터럽트 기반I/O(vectored interrupt) 4: P1 detects Inta and puts interrupt address vector 16 on the data bus 100
μP Data memory Program memory ISR ISR 16: MOV R0, 0x8000 System bus System bus 16: MOV R0, 0x8000 17: # modifies R0 17: # modifies R0 18: MOV 0x8001, R0 18: MOV 0x8001, R0 19: RETI # ISR return ... 19: Inta RETI # ISR return P1 P1 P2 P2 ... Int Int Main program 16 ... Main program 0 PC ... 100: instruction 0x8000 0x8000 0x8001 0x8001 100: instruction 100 101: instruction 101: instruction 인터럽트 기반I/O(vectored interrupt) 5(a): PC jumps to the address on the bus (16). The ISR there reads data from 0x8000, modifies the data, and writes the resulting data to 0x8001. 5(b): After being read, P1 deasserts Int.
μP Data memory Program memory ISR ISR 16: MOV R0, 0x8000 System bus 16: MOV R0, 0x8000 17: # modifies R0 17: # modifies R0 18: MOV 0x8001, R0 18: MOV 0x8001, R0 19: RETI # ISR return ... 19: RETI # ISR return P1 P2 Int ... Main program ... Main program PC 0x8000 0x8001 ... 100: instruction +1 100: instruction 100 100 100 101: instruction 101: instruction 인터럽트 기반I/O(vectored interrupt) 6: The ISR returns, thus restoring the PC to 100+1=101, where the μP resumes
Interrupt address table • Compromise between fixed and vectored interrupts • One interrupt pin • Table in memory holding ISR addresses (maybe 256 words) • Peripheral doesn’t provide ISR address, but rather index into table • Fewer bits are sent by the peripheral • Can move ISR location without changing peripheral
Additional interrupt issues • Maskable vs. non-maskable interrupts • Maskable: programmer can set bit that causes processor to ignore interrupt • Important when in the middle of time-critical code • Non-maskable: a separate interrupt pin that can’t be masked • Typically reserved for drastic situations, like power failure requiring immediate backup of data to non-volatile memory • Jump to ISR • Some microprocessors treat jump same as call of any subroutine • Complete state saved (PC, registers) – may take hundreds of cycles • Others only save partial state, like PC only • Thus, ISR must not modify registers, or else must save them first • Assembly-language programmer must be aware of which registers stored
Pin Direction Register(GPDR) 0x40E0_000C/10/14 2 Alternate Function Register(GAFR) 0x40E0_0054/58/5C 0x40E0_0060/64/68 GPDR 1 : 출력 0 : 입력 Pin Set Registers(GPSR) 0x40E0_0060/64/68 0 Pin Clear Registers(GPCR) 1 0x40E0_0060/64/68 2 Alternate Function(Output) 3 3 Base Address0x40E0_0000 Alternate Function(Input) 2 1 0 Edge DetectStatus Register(GEDR) 0x40E0_0048/4C/50 EdgeDetect Rising Edge DetectEnable Register(GRER) 0x40E0_0030/34/38 Falling Edge DetectEnable Register(GFER) Power Manager Sleep Wake-up logic 0x40E0_003C/40/44 Pin-LevelRegister(GPLR) 0x40E0_0000/04/08 PXA255 General Purpose I/O Block Diagram 세부적인 내용은 Embedded SW I 참조
PXA255 Interrupt controller All Other Qualified interrupt Bits Interrupt Controller Level Register(ICLR) 0 : IRQ 1 : FIQ 40D0 0008 XScale CORE 23 23 CCR[DIM]=0 & IDLE mode=‘1’ Interrupt Controller Mask Register (ICMR) 40D0 0004 FIQ Interrupt Source Bit CPSR.6(F) Interrupt Controller Pending Register (ICPR) 40D0 0010 IRQ Interrupt Controller IRQ Pending Register (ICIP) CPSR.7(I) 40D0 0000 Interrupt Controller FIQ Pending Register (ICFP) 40D0 000C • 40D0 0014 : Interrupt controller control register (ICCR) • ICCR.0 : disable idle mask(DIM)
Interrupt Controller(2) • All interrupts routed to FIQ or IRQ • Two level interrupt structure 1. What module caused interrupt • Serial channel, DMA, Power Management, etc 2. Why did an interrupt occur there? • RX, TX, over-run, under-run, Data Done, Battery Fault, etc • Template for servicing interrupts provided with firmware • Peripheral/PCMCIA interrupt mask in each module • GPIO masks determined per pin or group of pins
IM[x] Interrupt Mask ‘x’ (where x= 8 through 14 and 17 through 31). 0 – Pending interrupt is masked from becoming active (interrupts are NOT sent to CPU or Power Manager). 1 – Pending interrupt is allowed to become active (interrupts are sent to CPU and Power Manager). NOTE: In idle mode, the IM bits are ignored if ICCR[DIM] is cleared. Reserved[0-7, 15, 16] Physical Address : 0x40D0/0004 ICMR(Interrupt Controller Mask Register) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ???? 0000 0000 0000 000? ?000 0000 ???? IM31 – IM28 IM23 – IM20 IM19 – IM17 IM11 – IM8 IM27 – IM24 IM14 – IM12 Reserved
IL[x] Interrupt Level ‘x’ (where n = 8 through 14 and 17 through 31). 0 – Interrupt routed to IRQ interrupt input. 1 – Interrupt routed to FIQ interrupt input. Reserved[0-7, 15, 16] Physical Address : 0x40D0/0008 ICLR(Interrupt Controller Level Register) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 #### 0000 0000 0000 000# #000 0000 #### IL31 – IL28 IL23 – IL20 IL19 – IL17 IL11 – IL8 IL27 – IL24 IL14 – IL12 Reserved
DIM[0] Disable Idle mask. 0 – All enabled interrupts bring the processor out of idle mode. 1 – Only enabled and unmasked (as defined in the ICMR) bring the processor out of idle mode. This bit is cleared during all resets. Reserved[31:1] Physical Address : 0x40D0/0014 ICCR(Interrupt Controller Control Register) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ???X ???? ???? ???? ???? ???? ???? ???? Reserved DIM
IP[x] : IRQ Pending x (where x = 8 through 14 and 17 through 31). 0 – IRQ NOT requested by any enabled source. 1 – IRQ requested by an enabled source. Reserved[0-7, 15, 16] Physical Address : 0x40D0/0000 ICIP(Interrupt Controller IRQ Pending Register) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ???? 0000 0000 0000 000? ?000 0000 ???? IP31 – IP28 IP23 – IP20 IP19 – IP17 IP11 – IP8 IP27 – IP24 IP14 – IP12 Reserved
FP[x] : FIQ Pending x (where x = 8 through 14 and 17 through 31). 0 – FIQ NOT requested by any enabled source. 1 – FIQ requested by an enabled source. Reserved[0-7, 15, 16] Physical Address : 0x40D0/000C ICFP(Interrupt Controller FIQ Pending Register) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ???? 0000 0000 0000 000? ?000 0000 ???? FP31 – FP28 FP23 – FP20 FP19 – FP17 FP11 – FP8 FP27 – FP24 Reserved FP14 – FP12
a 32-bit read-only register that shows all active interrupts in the system. These bits are not affected by the state of the mask register (ICMR). Clearing the interrupt status bit at the source, automatically clears the corresponding ICPR flag, provided there are no other interrupt status bits set within the source unit. Table 4-36 참조 Physical Address : 0x40D0/0010 ICPR(Interrupt Controller Pending Register) 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ???? 0000 0000 0000 000? ?000 0000 ???? IS31 – IS28 IS23 – IS20 IS19 – IS17 IS11 – IS8 IS27 – IS24 Reserved IS14 – IS12
Why Bottom Halves ? • Because interrupt handlers …. • run asynchronously and thus interrupt other potentially important code(even other interrupt handlers) • run with current interrupt level disabled at best, and at worst (if SA_INTERRUPT set) with all interrupts disabled • are often timing-critical because they deal with hardware • cannot block • Consequently, managing interrupts is divided into two parts (or halves) • Some useful tips about how to divide work between top and bottom half • if the work is time-sensitive • if the work is related to the hardware itself • if the work needs to ensure that another interrupt (particularly the • same interrupt) does not interrupt it
Bottom Halves in Linux 2.4 • the future when the system is less busy and interrupts are enabled again • Three types of bottom halves in 2.4 • softirq, tasklet, BH • operations on deferrable functions • initialization • activation • masking • execution • (* activation and execution on the same CPU ? )
Bottom Halves in Linux 2.4 • History of bottom half status in Linux • BH removed in 2.5 • Task queues removed in 2.5 • Softirq available since 2.3 • Tasklet available since 2.3 • Work queues available since 2.5
Softirqs • Each registered softirq in one entry of softirq_vec[32] • (32 softirqs possible) • irq_stat /* structure representing a softirq entry */ /* (linux/interrupt.h) */ struct softirq_action { void (*action) (struct softirq_action *); void *data; / }; /* 32-entry array of softirq_action (kernel/softirq.c) */ static struct softirq_action softirq_vec[32]; typedef struct { unsigned int __softirq_pending; unsigned int __local_irq_count; unsigned int __local_bh_count; unsigned int __syscall_count; struct task_struct *__ksoftirqd_task; unsigned int __nmi_count; } irq_cpustat_t; irq_cpustat_t irq_stat[NR_CPUS];
Softirqs • Softirq handler • with entire softirq_action as argument (why ?) • (ex) void net_tx_action(struct softirq_action *h) { } void net_rx_action(struct softirq_action *h) { } • Remember that • A softirq handler run with interrupts enabled and cannot sleep. • softirqs on current CPU are disabled • an interrupt handler can preempt a softirq • Another softirq can run on another CPU. • Four softirqs used in 2.4 (six in 2.6) • HI_SOFTIRQ / TIMER_SOFTIRQ / NET_TX_SOFTIRQ / • NET_RX_SOFTIRQ / SCSI_SOFTIRQ /(…)/ TASKLET_SOFTIRQ
Softirqs • Registering softirq handler at run-time by open_softirq( ) • open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL); • open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL); • Raising softirq by raise_softirq( ) • (usually, an interrupt handler raises its softirq before returning) • raise_softirq(NET_TX_SOFTIRQ); • Checking for pending softirqs (kernel version / supported hardware) • After processing a hardware interrupt • When one of ksoftirqd_CPUn kernel threads is awoken • When a packet is received on a NIC • When local_bh_enable macro re-enables the softirqs
Softirqs if( in_interrupt( ) ) return; local_irq_save( flags ); // disable local INT pending = softirq_pending(cpu); if( pending ){ mask = ~pending; local_bh_disable(cpu); // disable softirq // local_bh_count++ restart: softirq_pending(cpu) = 0; local_irq_enable(); struct softirq_action* h = softirq_vec; do{ if( pending & 1 ) h->action(h); h++; pending >> 1; }while(pending); local_irq_disable(); pending = softirq_pending(cpu); if( pending & mask ){ mask &= ~pending; goto restart; } local_bh_enable(); if( pending ) wakeup_softirqd(cpu); } local_irq_restore( flags ); • Executing softirqs : do_softirq( ) local_irq_count(cpu) = = 0 ? // nested ? local_bh_count(cpu) = = 0 ? // enabled ? serializing execution on a CPU softirq not processed in this invocation ? // goto restart only if different types // goto wakeup_softirqd(cpu) if same softirq re-activated
Kernel Thread : ksoftirqd_CPUn • Softirq kernel thread for each CPU • Q: what if softirqs raised or re-activated at very high frequency ? • either ignore new softirqs that occur while do_softirq( ) is running • long softirq latency (even on idle machine) • or continuous re-checking for pending softirqs • user starving (long response time) • softirq kernel threads • ksoftirqd_CPUn at low priority • give user a priority • but run immediately on idle for (; ;) { set_current_state(TASK_INTERRPTIBLE); schedule( ); // now in TASK_RUNNING state while (softirq_pending(cpu)) { do_doftirq( ); if (current->need_resched) schedule( ); } }
Tasklets • Implemented on top of softirqs: HI_SOFTIRQ, TASKLET_SOFTIRQ • Tasklet structure (linux/interrupt.h) • tasklet_vec[NR_CPUS] and tasklet_hi_vec[NR_CPUS] • to store scheduled tasklets (like raised softirqs) • for regular tasklets and high-priority tasklets struct tasklet_struct { struct tasklet_struct *next; // pointer to next tasklet in the list unsigned long state; // state of the tasklet : 0, SCHED, RUN atomic_t count; // enable (0) /disable (nonzero) tasklet void (*func)(unsigned long); // tasklet handler function unsigned long data; // argument to tasklet function }
Tasklets • Declaring your tasklets • statically (linux/interrupt.h) • dynamically DECLARE_TASKLET(name, func, data) / DECLARE_TASKLET_DISABLED( ) (ex) DECLARE_TASKLET(my_tasklet, my_tasklet_handler, dev); struct tasklet_struct my_tasklet = { NULL, 0, ATOMIC_INIT(0), my_tasklet_handler, dev}; open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL); open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL); void tasklist_init(struct tasklet_struct *t, tasklet_handler, dev);
Tasklets • Writing your tasklet handler • Remember that • Tasklets cannot sleep • if a tasklet share any data with an interrupt handler ? • (tasklets run with all interrupts enabled) • if a tasklet shares data with another tasklet or softirq ? void tasklet_handler(unsigned long data) { ….; }
Tasklets • Activating your tasklet tasklet_schedule( ) / tasklet_hi_schedule( ) tasklet_schedule(&my_tasklet); /* mark my_tasklet as pending */ static void tasklet_schedule(struct tasklet_struct *t) { 1. if TASKLET_STATE_SCHED is set, returns; /* already been scheduled ? */ otherwise set TASKLET_STATE_SCHED flag; 2. local_irq_save( flags); // save the state of interrupt system and disable local interrupts 3.insert the tasklet to the head of tasklet_vec or tasklet_hi_vec; 4. raise TASKLET_SOFTIRQ or HI_SOFTIRQ softirq; 5. local_irq_restore(flags); } * what if the same tasklet is scheduled again ? * what if it is already running on another CPU ?
Tasklets • Handling tasklets via tasklet_action( ) : TASKLET_SOFTIRQ static void tasklet_action(struct softirq_action *a) { 1. disable local interrupts; 2. list = the address of the list pointed to by tasklet_vec[cpu]; 3. put NULL in tasklet_vec[cpu]; // list of scheduled tasklets is emptied // 4. enable local interrupts; 5. for each tasklet decriptor in the list a. if TASKLET_STATE_RUN set, // tasklet of the same type on another CPU reinsert the tasklet descriptor in the list of tasklet_vec[cpu] and activate TASKLET_SOFTIRQ again; otherwise, set TASKLET_STATE_RUN (in SMP) b. if the tasklet is disabled (count <> 0 ?) reinsert and activate TASKLET_SOFTIRQ; // defer tasklet otherwise, clear TASKLET_STATE_SCHED and execute the tasklet function; }
Tasklets • Controlling tasklets • Enable / Disable tasklets • tasklet_enable( ) / tasklet_disable( ) • Remove a tasklet from the pending queue • tasklet_kill( ) tasklet_disable(&my_tasklet); /* we can now do stuff knowing that the tasklet cannot run .. */ tasklet_enable(&my_tasklet);
Tasklets • In summary, • In most cases, tasklets are preferred way to implement a bottom half for a normal hardware device • Tasklets are dynamically created, easy to use, and relatively quick.
BH • BH interface : a high-priority tasklet with no concurrency • at most one BH is running (global_bh_lock spin lock) • only for backward compatibility (Linux 2.2 or older) • Note that • Each BH must be statically defined and there are max 32 BHs. • Modules could not directly use BH interface because BH handlers must be defined at compile-time. • All BH handlers are strictly serialized. • No two BH handlers (even of different types) can run concurrenlty • Easy synchronization, but not good for SMP performance • a driver using BH interface does not scale well to SMP
BH • Task queues • a group of kernel functions as a BH ? (task queues) • Three particular task queues • Immediate queue (run by IMMEDIATE_BH) • Timer queue (run by TQUEUE_BH) • Scheduler queue (run by keventd kernel thread) (* Linux 2.4 *) • Can dynamically create new task queues • Later… • various queues tasklets • scheduler queue + keventd --> “work queue” in 2.6
Disabling Bottom Halves • Data structures accessed by bottom halves must be protected against race conditions because bottom halves can be executed at any time. • A trivial way to disable bottom halves on a CPU is to disable interrupts on that CPU. • (no interrupts --> no softirqs) • How to disable bottom halves without disabling interrupts ? • void local_bh_disable( ) --> __local_bh_count++ (of irq_stat[cpu]) • void local_bh_enable( )
GPIO디바이스 드라이버 • GPIO 입력 • button를 제어하는 gpio 드라이버 프로그램 소스 예제(gpio.c) GPIO에 할당된 IRQ값과 인터럽트 핸들러 블럭킹 I/O를 위한 대기 큐
GPIO디바이스 드라이버(2) 대기큐에 있는 프로세스를 깨움 (버튼이 눌러졌을 때) 대기큐로 진입 – 블록킹 상태 (버튼이 눌러지길 기다림) 블록킹이 해제 후 메시지 출력 16번포트의 GAFR설정 해제 16번 포트를 출력으로 설정과 및 에지 신호 설정
GPIO디바이스 드라이버(3) 인터럽트 핸들러 설치 인터럽트를 활성화 설정된 irq를 설정 해제한다