Transcript 12. 인터럽트 처리
12. 인터럽트 처리 김진홍 [email protected] 2015.11.30. 목차 1. 인터럽트 2. 인터럽트 발생 시간 체크 2/13 1. 인터럽트 인터럽트란 • 인터럽트란 • 프로세스가 수행되는 도중에 다른 서비스 처리 루틴이 끼어드는 것 • 인터럽트 발생 시점에 잠시 정지 -> 정해진 인터럽트 처리 루틴 수행 -> 복귀 1. 프로세서의 현재 상태 저장 1. 인터럽트 2. 인터럽트 발생 시간 체크 2. 처리 3. 원래 처리되고 있던 루틴으로 복귀 • 종류 • 시리얼 인터럽트 • DMA 인터럽트 • 키보드 인터럽트 • 프린터 인터럽트 4/13 IRQ 인터럽트의 처리 과정 • core별 처리 과정 • • i386계열 • 내부 인터럽트 벡터 테이블 안에 IRQ 인터럽트 벡터가 포함되어 있음 • 인터럽트가 발생하면 해당 테이블에 처리 루틴으로 분기됨 ARM 계열 • 1. 인터럽트 2. 인터럽트 발생 시간 체크 IRQ 벡터가 하나이므로 인터럽트 제어장치를 통해 인터럽트 처리를 재분기 함 => 리눅스에서 통합하여 처리함 • 프로세서마다 처리 방식이 다르기 때문에 do_IRQ()를 호출하여 처리 함 • do_IRQ()는 irq_desc라는 전역 변수에 등록된 인터럽트 서비스 함수를 호출 함 • 처리하고자 하는 IRQ 번호에 해당하는 인터럽트 서비스 함수를 irq_desc에 등록 5/13 IRQ 인터럽트의 처리 과정 • IRQ 인터럽트 처리 과정 하드웨어 장치 1. 인터럽트 커널 디바이스 드라이버 struct xxx data; 아키텍처별 IRQ 인식 처리 루틴 1) 등록 do_IRQ(n) 2. 인터럽트 발생 시간 체크 request_irq(irq, int_handler, frag, “xxx”, &data); irq_desc[irqs] 등록된 irq_desc에서 발생된 irq 인터럽트 서비스 함수 호출 irqreturn_t int_handler(int irq, void *dev_id, struct ptr_regs *regs) { 2) 호출 : 인터럽트 처리 : } 3) 제거 free_irq(irq, &data); 6/13 인터럽트 서비스 함수의 형식 • 인터럽트 처리 • 인터럽트가 발생 했을 때 처리할 루틴을 담은 인터럽트 서비스 함수가 필요함 • irqreturn_t int_interrupt(int irq, void *dev_id, struct pt_regs *regs) { 1. 인터럽트 2. 인터럽트 발생 시간 체크 return IRQ_HANDLED; } • irq • • • 인터럽트 번호 *dev_id • 인터럽트 id • 또는 인터럽트 함수가 사용 가능한 메모리 주소 struct pt_regs *regs • 인터럽트가 발생했을 당시의 레지스터 값 7/13 인터럽트 서비스 함수의 형식 • 인터럽트 서비스 함수 내의 메모리 할당 • kmalloc과 kfree 함수를 사용 • vmalloc vfree, ioremap 같은 가상메모리를 이용하는 함수를 사용하면 안됨 • GFP_ATOMIC 인자를 사용해야 함 • • 메모리가 있으면 할당, 없으면 NULL 1. 인터럽트 2. 인터럽트 발생 시간 체크 irqreturn_t int_interrupt(int irq, void *dev_id, struct pt_regs *regs) { char *data; : data = kmalloc(128, GFP_ATOMIC); if(data != NULL) { : kfree(data); } : return IRQ_HANDLED; } 8/13 인터럽트 서비스 함수의 등록과 해제 • 인터럽트 서비스 함수 등록 • 인터럽트를 처리하기 위해 가장 먼저 처리하는 것 • int request_irq 1. 인터럽트 ( unsigned int irq, 2. 인터럽트 발생 시간 체크 irqreturn_t (*handler) (int, void *, struct pt_regs *), unsigned long frags, const char *device, void *dev_id ); 9/13 인터럽트 서비스 함수의 등록과 해제 • 인터럽트 서비스 함수 해제 • 더 이상 필요 없을 때 제거 • void free_irq(unsigned int irq, void *dev_id); • int xxx_release(struct inode *inode, struct file *filp) { 1. 인터럽트 2. 인터럽트 발생 시간 체크 //하드웨어 인터럽트 금지 처리 루틴 :… free_irq(xxx_IRQ, NULL); return 0; } 10/13 인터럽트 함수와 디바이스 드라이버간의 데이터 공유 • 데이터 공유 • 인터럽트 서비스 함수는 문맥과 별개로 임의의 시점에서 수행됨 • 인터럽트 서비스 함수와 디바이스 드라이버 함수간에 데이터 공유 하는 방법이 필요함 • • 전역 변수를 이용 => 동일한 인터럽트 서비스 함수로 여러 디바이스를 제어할 수 없음 • dev_id 매개변수를 사용 => 여러 디바이스를 동일한 입터럽트 서비스 함수로 제어 1. 인터럽트 2. 인터럽트 발생 시간 체크 전역변수를 이용하는 경우 • static unsigned long int_count = 0; irqreturn_t count_interrupt(int irq, void *dev_id, struct pt_regs *regs) • { int int_release(struct inode *inode, struct file *filp) { int_count++; //하드웨어 인터럽트 금지 처리 루틴 return IRQ_HANDLED; : } free_irq(XXX_IRQ, NULL); int int_open(struct inode *inode, struct file *filp) printk(“int_count %d\n”, int_count); { return 0; if(!request_irq(XXX_IRQ, count_interrupt, SA_INTERRUPT, “test”, NULL)) } { // 하드웨어 인터럽트 허가 처리 루틴 : int_count = 0; } return 0; } 11/13 인터럽트 함수와 디바이스 드라이버간의 데이터 공유 • 데이터 공유 • dev_id를 이용하는 경우 • typedef struct { 1. 인터럽트 unsigned long ioaddress; unsigned long count; 2. 인터럽트 발생 시간 체크 } __attribute__ ((packed)) R_INT_INFO; irqreturn_t count_interrupt(int irq, void *dev_id, struct pt_regs *regs) { R_INT_INFO *ptrinfo; ptrinfo = (R_INT_INFO *) dev_id; printk(“INT READ I/O %02X\n”, inb(printinfo->ioaddress)); ptrinfo -> count++; return IRQ_HANDLED; } • int int_open(struct inode * inode, struct file *filp) • int int_release(struct inode *inode, struct file *filp) • … 교재 코드 참고 12/13 인터럽트 서비스 등록과 해제 시점 • 등록과 해제 시점 • 응용 프로그램이 디바이스 파일을 열었을 때 등록 • • open() 함수에서 인터럽트 서비스 함수 등록 응용 프로그램이 디바이스 파일을 닫았을 때 제거 • close() 함수에서 인터럽트 서비스 함수 제거 1. 인터럽트 2. 인터럽트 발생 시간 체크 13/13 인터럽트의 공유 • 개요 • PCI 디바이스 드라이버나 시리얼 디바이스들은 인터럽트 하나를 공유하여 여러 하드웨어에서 동작하는 경우가 있음 • 1. 인터럽트 인터럽트 서비스 함수를 등록할 때 같은 인터럽트 번호에 대해 다른 인터럽트 서비스 함수를 등록 2. 인터럽트 발생 시간 체크 할 수 있도록 지원 함 • request_irq(XXX_IRQ, xxx_int_service_func, SA_INTERRUPT|SA_SHIRQ, PRINT_DEV_NAME, 0x01) • free_irq(XXX_IRQ, 0x01); • • frags 매개변수에는 SA_SHIRQ가 포함되어야 함 • dev_id 매개 변수에는 동일한 인터럽트 서비스 함수를 구별하기 위해 0이 아닌 값을 사용 해야 함 • request_irq와 free_irq를 위해 dev_id 값은 동일해야 함 irqreturn_t xxx_interrupt(int irq, void *dev_id, struct pt_regs *regs) { 인터럽트_관리_구조체 *ptrMng = (인터럽트_관리_구조체 *) dev_id; if(자신의 인터럽트 체크 함수(ptrMng)) { //인터럽트 처리 : return IRQ_HANDLED; } return IRQ_NONE; 14/13 인터럽트의 금지와 해제 • 개요 • 현재 인터럽트 서비스 함수가 동작하도 있어도 또다른 인터럽트 발생 가능 • • 우선순위에 따라 처리 됨 • 우선순위가 높은 인터럽트 처리중일때 낮은 인터럽트가 발생하면 낮은 인터럽트가 대기 함 • 우선순위가 낮은 인터럽트 처리중일때 높은 인터럽트가 발생하면 높은 인터럽트가 처리 됨 1. 인터럽트 2. 인터럽트 발생 시간 체크 하드웨어에 따라 인터럽트 도중에 수행이 중지되면 곤란한 디바이스가 존재함 • 해결책 • 인터럽트 도중 다른 인터럽트가 발생하지 못하게 막는 경우 • • request_irq() 함수의 frags 매개변수에 SA_INTERRUPT 설정 -> 빠른 인터럽트 핸들러 라고 함 데이터 처리를 보호하기 위해 인터럽트를 강제로 막는 경우 • • 특정 인터럽트 번호에 대한 금지와 해제를 수행 함 • void disable_irq(int irq) : 인터럽트 금지 • void enable_irq(int irq) : 인터럽트 허가 [일반적인 사용방법] unsigned long frags; local_save_flags(frags); local_irq_disable(); //인터럽트 호출에서 보호하고자 하는 루틴 local_restore_flags(frags); 프로세서 전체의 인터럽트를 금지하고 해제 • local_irq_disable/enable(void) : 프로세서 인터럽트 처리를 금지/허가 • local_save_flags/restore(unsigned long frags) : 현재의 프로세스의 상태를 저장/복구 15/13 seqlock_t 구조체 • 개요 • 배경 • 디바이스 드라이버가 사용하는 변수와 인터럽트 서비스 함수가 사용하는 변수가 전역변수로 서로 같을 경우 • • 인터럽트 금지루틴이 길거나 잦다면 시스템 효율이 떨어지게 됨 • 상황 • { 하드웨어에서 읽어온 값이 인터럽트 서비스 해결책 • unsinged char ReadData = 0; irqreturn_t xxx_interrupt(int irq, void *dev_id, struct pt_regs *regs) 함수에서 변경될경우 다른 결과가 발생할 수 있음 • 2. 인터럽트 발생 시간 체크 동기화를 위해 인터럽트의 금지와 해제를 이용함 • 1. 인터럽트 ReadData = inb(IN_ADDR); return IRQ_HANDLED; } ssize_t xxx_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) { if(ReadData) seqlock_t 구조체 { 처리루틴 } outb(ReadData | 0x80); return 1; } 16/13 seqlock_t 구조체 • 개요 • seqlock_t 구조체 • 구현 • 장점 • • • • 경량 특징 reader보다 writer가 먼저 • writer는 다른 writer가 seqlock_t the_lock = SEQLOCK_UNLOCKED; unsigned int seq; • • 제어용 변수 선언 데이터 변경 루틴 • 1. 인터럽트 2. 인터럽트 발생 시간 체크 write_seqlock(&the_lock); : 데이터 변경 루틴 없으면 항상 락을 잠글 수 있음 : write_sequnlock(&the_lock); • 데이터 취득 루틴 do{ seq = read_seqbegin(&the_lock); : 데이터 값 처리 루틴 : }while read_seqretry(&the_lock, seq); 17/13 인터럽트 발생 횟수 확인 • 개요 • 보통 디바이스 드라이버가 제대로 동작하는지 확인 하려면 인터럽트 동작을 확인 함 • 인터럽트 호출 횟수는 리눅스 커널에서 알아서 계산함 • proc/interrupts 1. 인터럽트 2. 인터럽트 발생 시간 체크 18/13 Q&A 19/40