이벤트
- 인터럽트와 태스크간 연결 매체
- 버튼 클릭 -> 전기 신호 흐름 -> 컨트롤러가 인식 -> 소프트웨어 인터럽트 처리(IRQ, FIQ 발생) -> IRQ FIQ 핸들러가 인터럽트 확인 -> 해당 인터럽트 핸들러가 처리
- GUI의 경우 : 마우스 클릭(인터럽트 발생) -> 닷넷 or x윈도우가 이벤트로 만들어 핸들러 함수에 전달
* RTOS의 경우 커널이 인터럽트를 이벤트로 바꿔 전달 이벤트 핸들러가 처리
이벤트 플래그
- 임의로 정한 갑하는 값
- 아래 이벤트 플래그를 16진수로 표현 시 0x8000 0002 -> 31번, 2번 이벤트 발생
이벤트 처리 구현
kernel/event.h
이벤트 플래그 선언. 유아트 입력, 커맨드 입력, 출력, 잠금해제 이벤트 플래그 존재하고
나머진 미정
#ifndef KERNEL_EVENT_H_
#define KERNEL_EVENT_H_
#include <stdbool.h>
typedef enum KernelEventFlag_t
{
KernelEventFlag_UartIn = 0x00000001,
KernelEventFlag_CmdIn = 0x00000002,
KernelEventFlag_CmdOut = 0x00000004,
KernelEventFlag_Unlock = 0x00000008,
KernelEventFlag_Reserved04 = 0x00000010,
KernelEventFlag_Reserved05 = 0x00000020,
KernelEventFlag_Reserved06 = 0x00000040,
KernelEventFlag_Reserved07 = 0x00000080,
KernelEventFlag_Reserved08 = 0x00000100,
KernelEventFlag_Reserved09 = 0x00000200,
KernelEventFlag_Reserved10 = 0x00000400,
KernelEventFlag_Reserved11 = 0x00000800,
KernelEventFlag_Reserved12 = 0x00001000,
KernelEventFlag_Reserved13 = 0x00002000,
KernelEventFlag_Reserved14 = 0x00004000,
KernelEventFlag_Reserved15 = 0x00008000,
KernelEventFlag_Reserved16 = 0x00010000,
KernelEventFlag_Reserved17 = 0x00020000,
KernelEventFlag_Reserved18 = 0x00040000,
KernelEventFlag_Reserved19 = 0x00080000,
KernelEventFlag_Reserved20 = 0x00100000,
KernelEventFlag_Reserved21 = 0x00200000,
KernelEventFlag_Reserved22 = 0x00400000,
KernelEventFlag_Reserved23 = 0x00800000,
KernelEventFlag_Reserved24 = 0x01000000,
KernelEventFlag_Reserved25 = 0x02000000,
KernelEventFlag_Reserved26 = 0x04000000,
KernelEventFlag_Reserved27 = 0x08000000,
KernelEventFlag_Reserved28 = 0x10000000,
KernelEventFlag_Reserved29 = 0x20000000,
KernelEventFlag_Reserved30 = 0x40000000,
KernelEventFlag_Reserved31 = 0x80000000,
KernelEventFlag_Empty = 0x00000000,
} KernelEventFlag_t;
void Kernel_event_flag_init(void);
void Kernel_event_flag_set(KernelEventFlag_t event);
void Kernel_event_flag_clear(KernelEventFlag_t event);
bool Kernel_event_flag_check(KernelEventFlag_t event);
#endif /* KERNEL_EVENT_H_ */
kernel/event.c
sEventflag 이벤트 플래그 32개 기록해서 보관 + 태스크에 전달하기 위한 변수
flag_clear 특정 플래그 반전 함수
flag_check 해당 이벤트가 1인지 체크. 1이면 호출한 함수에게 대기중임을 알려주고 플래그 클리어
#include <stdint.h>
#include <stdbool.h>
#include "stdio.h"
#include "event.h"
static uint32_t sEventFlag;
void Kernel_event_flag_init(void)
{
sEventFlag = 0;
}
void Kernel_event_flag_set(KernelEventFlag_t event)
{
sEventFlag |= (uint32_t)event;
}
void Kernel_event_flag_clear(KernelEventFlag_t event)
{
sEventFlag &= ~((uint32_t) event);
}
bool Kernel_event_flag_check(KernelEventFlag_t event)
{
if (sEventFlag & (uint32_t) event)
{
Kernel_event_flag_clear(event);
return true;
}
return false;
}
커널 API로 이벤트 처리하기위해 kernel/Kernel.c, .h에 함수 추가
#ifndef KERNEL_KERNEL_H_
#define KERNEL_KERNEL_H
#include "task.h"
#include "event.h"
void Kernel_start(void);
void Kernel_yield(void);
void Kernel_send_events(uint32_t event_list);
KernelEventFlag_t Kernel_wait_events(uint32_t waiting_list);
#endif
kernel/Kernel.c
Kernel_send_events
이벤트를 전달하기 위한 함수로 uint32_t로 이벤트들의 목록을 받아
* 1100 0000 0000 0000, 0010 0000 0000 0000과 같이 이벤트가 하나 혹은 여러개가 발생한 경우를 매개변수로 받는듯
비트 연산자로 해당 플래그가 올라왔는지 확인
플래그가 올라온 이벤트가 있으면 uint32_t 이벤트 목록을 KernelEventFlag_t인 sending_event에다 담아서(SET_BIT)
Kernel_event_flag_set으로 sEventFlag에 등록
* sEventFlag에 이벤트리스트를 등록함으로서 sending을 하나 어떻게 받아서 처리하는지는 don't care
Kernel_wait_events
위와 비슷하게 대기할 이벤트 목록을 받아서
올라온 이벤트 플래그가 있으면 내리고, 내린 대기 이벤트 플래그 반환
waiting list에 올라온 플래그가 없으면 엠프티 반환
uint32_t 로 전달하는건 event1 | event2 .. 와같여 어러 이벤트를 or 연산해서 한번에 보내기 위함.
#include <stdint.h>
#include <stdbool.h>
#include "memio.h"
#include "Kernel.h"
#include "event.h"
void Kernel_start(void)
{
Kernel_task_start();
}
void Kernel_yield(void)
{
Kernel_task_scheduler();
}
void Kernel_send_events(uint32_t event_list)
{
for (uint32_t i = 0; i < 32 ;i++)
{
if ((event_list >> i) & 1)
{
KernelEventFlag_t sending_event = KernelEventFlag_Empty;
sending_event = (KernelEventFlag_t)SET_BIT(sending_event, i);
Kernel_event_flag_set(sending_event);
}
}
}
KernelEventFlag_t Kernel_wait_events(uint32_t waiting_list)
{
for (uint32_t i = 0; i < 32; i++)
{
if ((waiting_list >> i) & 1)
{
KernelEventFlag_t waiting_event = KernelEventFlag_Empty;
waiting_event = (KernelEventFlag_t)SET_BIT(waiting_event, i);
if (Kernel_event_flag_check(waiting_event))
{
return waiting_event;
}
}
}
return KernelEventFlag_Empty;
}
인터럽트로 이벤트 발생시키기
QEMU에는 인터럽트가 별로 없으며, 타이머는 자주 발생해 Uart 만 사용
Uart 입력 인터럽트 발생 시 유아트 입력 이벤트 발생 시키기
interrupt_handler에서 Kernel_send_events에다가 enum인 UartIn을 전달하여 인터럽트 처리 후 커널에 이벤트 전달
#include <stdint.h>
#include "Uart.h"
#include "HalUart.h"
#include "HalInterrupt.h"
#include "Kernel.h"
extern volatile PL011_t* Uart;
static void interrupt_handler(void);
void Hal_uart_init(void)
{
//enable UART
Uart->uartcr.bits.UARTEN=0;
Uart->uartcr.bits.TXE=1;
Uart->uartcr.bits.RXE=1;
Uart->uartcr.bits.UARTEN=1;
Uart->uartimsc.bits.RXIM=1;
Hal_interrupt_enable(UART_INTERRUPT0);
Hal_interrupt_register_handler(interrupt_handler, UART_INTERRUPT0);
}
void Hal_uart_put_char(uint8_t ch)
{
while(Uart->uartfr.bits.TXFF);
Uart->uartdr.all = (ch & 0xFF);
}
uint8_t Hal_uart_get_char(void)
{
uint32_t data;
while(Uart->uartfr.bits.RXFE);
data = Uart->uartdr.all;
//check for and error flag
if (data & 0xFFFFFF00)
{
//clear the error
Uart->uartrsr.all = 0xFF;
return 0;
}
return (uint8_t)(data & 0xff);
}
static void interrupt_handler(void)
{
uint8_t ch = Hal_uart_get_char();
Hal_uart_put_char(ch);
Kernel_send_events(KernelEventFlag_UartIn);
}
boot/Main.c에
이벤트 플래그 초기화와
User Task0에 유아트 입력 이벤트 처리 코드 구현
Kernel_wait_event(KernelEventFlag_UartIn) 으로
태스크 0 동작때마다 유아트 입력이 들어왔는지 확인. 확인 후
sEventFlag에서 유아트인 플래그는 off되고 handle_event에 일단 유아트 이벤트 플래그 등록
스위칭을 통해 처리할 이벤트 핸들 이벤트가 유아트인 이면 이벤트 처리됨 문자열 출력
#include <stdint.h>
#include <stdbool.h>
#include "HalInterrupt.h"
#include "HalUart.h"
#include "HalTimer.h"
#include "task.h"
#include "Kernel.h"
#include "stdio.h"
#include "stdlib.h"
static void Hw_init(void);
static void Kernel_init(void);
static void Printf_test(void);
static void Timer_test(void);
void User_task0(void);
void User_task1(void);
void User_task2(void);
void main(void)
{
Hw_init();
uint32_t i = 100;
while(i--)
{
Hal_uart_put_char('N');
}
Hal_uart_put_char('\n');
putstr("Hello World!\n");
Printf_test();
Timer_test();
Kernel_init();
while(true);
}
static void Hw_init(void)
{
Hal_interrupt_init();
Hal_uart_init();
Hal_timer_init();
}
static void Printf_test(void)
{
char* str = "printf pointer test";
char* nullptr = 0;
uint32_t i = 5;
uint32_t* sysctrl0 = (uint32_t*)0x10001000;
debug_printf("%s\b", "Hello printf");
debug_printf("output string pointer: %s\n", str);
debug_printf("%s is null pointer, %u number\n", nullptr, 10);
debug_printf("%u=5\n", i);
debug_printf("dec=%u hex=%x\n", 0xff, 0xff);
debug_printf("print zero %u\n", 0);
debug_printf("SYSCTRL0 %x\n", *sysctrl0);
}
static void Timer_test(void)
{
for(uint32_t i = 0; i < 5 ; i++)
{
debug_printf("current count : %u\n", Hal_timer_get_1ms_counter());
delay(1000);
}
}
static void Kernel_init(void)
{
uint32_t taskId;
Kernel_task_init();
Kernel_event_flag_init();
taskId = Kernel_task_create(User_task0);
if (NOT_ENOUGH_TASK_NUM == taskId)
{
putstr("Task0 creation fail\n");
}
taskId = Kernel_task_create(User_task1);
if (NOT_ENOUGH_TASK_NUM == taskId)
{
putstr("Task1 creation fail\n");
}
taskId = Kernel_task_create(User_task2);
if (NOT_ENOUGH_TASK_NUM == taskId)
{
putstr("Task2 creation fail\n");
}
Kernel_start();
}
void User_task0(void)
{
uint32_t local = 0;
debug_printf("User Task #0 SP = 0x%x\n", &local);
while(true)
{
KernelEventFlag_t handle_event = Kernel_wait_events(KernelEventFlag_UartIn);
switch(handle_event)
{
case KernelEventFlag_UartIn:
debug_printf("\nEvent handled\n");
break;
}
Kernel_yield();
}
}
void User_task1(void)
{
uint32_t local = 0;
while(true)
{
debug_printf("User Task #1 SP = 0x%x\n", &local);
Kernel_yield();
}
}
void User_task2(void)
{
uint32_t local = 0;
while(true)
{
debug_printf("User Task #2 SP = 0x%x\n", &local);
Kernel_yield();
}
}
딜레이를 안줬더니 너무 빨리 지나가서
위 코드의 각 태스크에 delay(100) 추가해서 돌리니
잘 나온다. User task #0은 루프 밖이라 한번 출력하고 마니 안나오는게 당연하고
'컴퓨터과학 > os' 카테고리의 다른 글
navilos - 18. 메시징 (0) | 2022.08.25 |
---|---|
navilos - 17. 이벤트 2 : 사용자 정의 이벤트, 여러 이벤트 동시 처리 (0) | 2022.08.25 |
navilos - 15. 컨텍스트 스위칭 2 : yield로 비선점형 스케줄링 구현 (0) | 2022.08.24 |
navilos - 14. 컨텍스트 스위칭 1 : 컨텍스트 저장, 복구 구현 하기 (0) | 2022.08.24 |
navilos - 13. 라운드로빈 스캐줄러 (0) | 2022.08.22 |