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