VPOS_kernel_main() 에서 부터, shell thread 가 수행되기전까지의 설명을 담고 있다.
VPOS_kernel_main
수행에 필요한 queue 와 하드웨어 초기화를 수행한다. 마지막으로 가장 낮은 우선순위를 가지는 shell thread 를 수행하고, 스케줄러를 시작한다.
void VPOS_kernel_main( void ) { pthread_t p_thread; vk_init_kdata_struct(); // 앞으로의 수행에 필요한 각 queue 생성 및 초기화 vk_machine_init(); // 하드웨어 설정 초기화 printk("%s\n%s\n%s\n%s\n%s\n%s\n", top_line, version, develop, release, homepage, bottom_line); pthread_create(&p_thread, NULL, VPOS_SHELL, (void *)NULL); // 기본적인 shell thread 생성 VPOS_start(); // 스케줄러 동작 }
vk_init_kdata_struct
각 queue 를 생성하고 초기화 시킨다. vpos 는 총 0 ~ 31 단계의 우선순위를 가질 수 있다. 그래서 각각 31 개의 queue 를 생성한다.
void vk_init_kdata_struct(void) { vk_init_ready_queue(); // ready queue (0 ~ 31개) vk_init_wait_queue(); // wait queue (0 ~ 31개) vk_init_tcb_log_queue(); // tcb log queue (0 ~ 31개) vk_init_dd_table(); // device driver, 총 29 개의 vk_dd_table 생성 및 총 64 개의 vk_idt_table 생성 vk_init_thread_join_table(); // 10 개의 thread_join_table 생성 vk_current_thread= &init_thread; // init thread 를 current thread 로 지정 }
vk_machine_init
각각의 하드웨어 초기화를 한다.
void vk_machine_init(void) { vh_serial_init(); // 시리얼 통신을 위한 UART 관련 레지스터 세팅(s3c2410 user manual 참조) vh_timer_init(); // 타이머 등록 및 인터럽트 언마스크 vh_rtc_init(); vh_lcd_init(); }
vh_timer_init
위에서 vh_timer_init() 의 타이머를 등록시키는 루틴을 살펴보기로 한다.
void vh_timer_init(void) { int timer_load_val; int TIMER = 14; vu_register_dev(0, "Timer", &timer_device_driver_fops, 14); // file_operations(open, release, read, write, ioctl, interrrupt) 가진 구조체 vh_interrupt_unmask(TIMER); vh_rTCFG0 = 0x0f00; vh_rTCFG1 = 0x0000; timer_load_val = 50000000/(2*16*100); vh_rTCNTB = timer_load_val; vh_rTCON = (vh_rTCON & ~0x0700000) | 0x600000; vh_rTCON = (vh_rTCON & ~0x0700000) | 0x500000; } ... ... ... int vu_register_dev(unsigned int dev_number, char *dev_name, struct file_operations *fops, int irq_num) { unsigned int i; i = dev_number; // i = 0 if(vk_dd_table[i].register_dev == 1) { // 이미 dev_number 를 사용하고 있다면 printk("Alreday %s device driver is registered\n", dev_name); return -1; } vk_dd_table[i].register_dev = 1; vk_dd_table[i].name = dev_name; // Timer vk_dd_table[i].interrupt_number= irq_num; // 14 vk_dd_table[i].fop_list = fops; // file_operations(open, release, read, write, ioctl, interrrupt) if(irq_num >= 0) vk_idt_table[irq_num]=dev_number; // vk_idt_table[14] = 0 return 0; }
위의 코드를 수행 후의 상태는 다음과 같다.
구조체 | 변수값 |
vk_dd_table[0] | register_dev = 1 |
name = Timer | |
interrupt_number = 14 | |
fop_list = file_operations |
구조체 | 변수값 |
vk_idt_table[14] | 0 |
여기서 fop_list 에 등록된 file_operations 은 다음과 같다. 아래에 보면, TIMER 인터럽트가 발생하면, vh_timer_interrupt_handler() 를 호출한다.
struct file_operations timer_device_driver_fops = { NULL, // open NULL, // release NULL, // read NULL, // write NULL, // ioctl vh_timer_interrupt_handler // interrupt 발생 시 수행 함수 };
주기적으로 TIMER 인터럽트가 발생할 때마다 수행되는 함수는 아래와 같다.
int vh_timer_interrupt_handler(void) { vh_rSRCPND = vh_BIT_TIMER; // 레지스터 세팅 vh_rINTPND = vh_BIT_TIMER; ++(vk_current_thread->cpu_tick); // vk_current_thread->cpu_tick = 0 -> 1 if(vk_sched_lock==0) vk_scheduler(); // vk_sched_lock = 0, vk_scheduler() 함수 호출 return 0; }
등록을 한 후에는 인터럽트를 언마스크 한다.
vh_interrupt_unmask(TIMER); // TIMER = 14 ... ... ... void vh_interrupt_unmask(int unmask_irq_number) { vh_rINTMSK &= ~(0x1<<unmask_irq_number); // vh_rINTMSK = 0x4a000008, ~(0x1<<unmask_irq_number) = 0xFFFFBFF }
s3c2410 user manual 을 보면(p358), 0x4a000008 에 0 은 마스크를 해제, 1 은 마스크를 적용하도록 설정할 수 있다. 여기서는 TIMER 의 인터럽트를 언마스크 해야 하기 때문에, 0x14 의 역수로 설정한다.
이 결과, 스케줄링은 타이머 인터럽트에 의해서 주기적으로 발생되며, 또는 사용자에 의한 함수 호출로 인해 발생할 수 있다는 것을 알았다.
vh_rtc_init
루틴은 다음과 같다. 여기서는 시스템의 년, 월, 일을 설정한다.
void vh_rtc_init(void) { int Timetick = 8; vu_register_dev(8, "Timetick", &timeTick_device_driver_fops, 8); // Timetick 등록 vh_interrupt_unmask(Timetick); // 인터럽트 마스크 해제 //sem_init(&time_sem, 0, 1); vh_rRTCCON = 0x00; vh_rTICNT = (1<<7) + 127; vh_timeInit(); }
위 루틴의 수행 후의 상태는 다음과 같다.
구조체 | 변수값 |
vk_dd_table[8] | register_dev = 1 |
name = Timetick | |
interrupt_number = 8 | |
fop_list = file_operations |
구조체 | 변수값 |
vk_idt_table[14] | 8 |