IT/디바이스드라이버

12. 인터럽트 처리

피치 크러쉬 2015. 12. 14. 13:04
300x250


12. 인터럽트 처리.pptx


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;
}
irq
인터럽트 번호
*dev_id
인터럽트 id
또는 인터럽트 함수가 사용 가능한 메모리 주소
struct pt_regs *regs
인터럽트가 발생했을 당시의 레지스터 값

인터럽트 서비스 함수 내의 메모리 할당
kmallockfree 함수를 사용
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;
}

인터럽트 서비스 함수의 등록과 해제

인터럽트 서비스 함수 등록
인터럽트를 처리하기 위해 가장 먼저 처리하는 것
int request_irq
(
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;
}

인터럽트 함수와 디바이스 드라이버간의 데이터 공유

데이터 공유
인터럽트 서비스 함수는 문맥과 별개로 임의의 시점에서 수행됨
인터럽트 서비스 함수와 디바이스 드라이버 함수간에 데이터 공유 하는 방법이 필요함
전역 변수를 이용 => 동일한 인터럽트 서비스 함수로 여러 디바이스를 제어할 수 없음
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
; }
int int_release(struct inode *inode, struct file *filp)
{
  //
하드웨어 인터럽트 금지 처리 루틴
  :
  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;
}
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_irqfree_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;
}

인터럽트의 금지와 해제

개요
현재 인터럽트 서비스 함수가 동작하도 있어도 또다른 인터럽트 발생 가능
우선순위에 따라 처리 됨
우선순위가 높은 인터럽트 처리중일때 낮은 인터럽트가 발생하면 낮은 인터럽트가 대기 함
우선순위가 낮은 인터럽트 처리중일때 높은 인터럽트가 발생하면 높은 인터럽트가 처리 됨
하드웨어에 따라 인터럽트 도중에 수행이 중지되면 곤란한 디바이스가 존재함
해결책
인터럽트 도중 다른 인터럽트가 발생하지 못하게 막는 경우
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;
}
개요
seqlock_t 구조체
장점
경량
특징
reader보다 writer가 먼저

writer는 다른 writer
없으면 항상 락을 잠글 수 있음
제어용 변수 선언
seqlock_t the_lock = SEQLOCK_UNLOCKED;
unsigned int seq;
데이터 변경 루틴
write_seqlock(&the_lock);
  :
 
데이터 변경 루틴
  :
write_sequnlock(&the_lock);

데이터 취득 루틴
do{
  seq = read_seqbegin(&the_lock);
  :
 
데이터 값 처리 루틴
  :
}while read_seqretry(&the_lock, seq);

인터럽트 발생 횟수 확인

개요
보통 디바이스 드라이버가 제대로 동작하는지 확인 하려면 인터럽트 동작을 확인 함
인터럽트 호출 횟수는 리눅스 커널에서 알아서 계산함

proc/interrupts


반응형