From: acevest Date: Sat, 11 May 2024 01:40:36 +0000 (+0800) Subject: 解决task_t.ticks可能被时钟中断减为负数的问题 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/doc/mdoc.7.txt?a=commitdiff_plain;h=92624ca65653e45e28b27ed05a506d7c24063094;p=kernel.git 解决task_t.ticks可能被时钟中断减为负数的问题 --- diff --git a/boot/boot.c b/boot/boot.c index f7ad9f0..7498796 100644 --- a/boot/boot.c +++ b/boot/boot.c @@ -53,7 +53,7 @@ void setup_gdt(); void setup_idt(); void setup_gates(); void set_tss(); -void setup_i8253(uint16_t); +void setup_i8254(uint16_t); void setup_boot_irqs(); void check_kernel(unsigned long addr, unsigned long magic) { @@ -78,7 +78,7 @@ void check_kernel(unsigned long addr, unsigned long magic) { setup_boot_irqs(); - setup_i8253(100); + setup_i8254(100); boot_delay(DEFAULT_BOOT_DELAY_TICKS); diff --git a/include/task.h b/include/task.h index d47538e..30f96de 100644 --- a/include/task.h +++ b/include/task.h @@ -48,7 +48,7 @@ typedef union task_union { uint32_t esp; uint32_t eip; - uint32_t ticks; + int ticks; uint32_t turn; // 时间片用完次数 uint32_t priority; uint64_t jiffies; diff --git a/kernel/clock.c b/kernel/clock.c index 6c7ac63..e78622f 100644 --- a/kernel/clock.c +++ b/kernel/clock.c @@ -36,15 +36,24 @@ void clk_handler(unsigned int irq, pt_regs_t *regs, void *dev_id) { current->jiffies = jiffies; // 中断目前虽然不能嵌套,但依然可以打断前一个中断的下半部分处理 - // 若前一个时钟中断将这个值减到0 - // 同时其下半部分处理时间过长,直到这个时钟中断还没处理完 - // 那么这个时钟中断是完全可以打断它,且在这里把这个ticks从0减到负数 - // 而这个是uint32_t型,因此会溢出成0xFFFFFFFF - if (current->ticks > 0) { - current->ticks--; + // 若前一个时钟中断将这个值减到0,会将该进程设置为need_resched,同时分配新的时间片值,以在下半部处理完后再重新调度就绪进程运行 + // 而如果其下半部分需要处理的事情很多,处理时间过长,两个时钟中断之间的时间还不足以处理完 + // 那么下一个时钟中断是完全可以打断还没处理完的下半部逻辑 + // 打断后该时钟中断不应该继续减少该进程的时间片,因为这会造成该进程在后续的调底中少了实际的运行时间 + if (1 == current->need_resched) { + // 这种情况必然已经发生了该时钟中断打断了下半部处理程序 + return; } - assert(current->ticks <= TASK_MAX_PRIORITY); // 防止ticks被减到0后再减溢出 + current->ticks--; + + if (0 == current->ticks) { + current->need_resched = 1; + current->ticks = current->priority; + current->turn++; + } + + assert(current->ticks >= 0); // 防止ticks被减到0后再减溢出 add_irq_bh_handler(clk_bh_handler, NULL); } @@ -73,23 +82,56 @@ void clk_bh_handler(void *arg) { debug_print_all_tasks(); } -void setup_i8253(uint16_t hz) { - // 最低频率为18.2Hz(1193180/65536,最大计数值是65536是因为往计数器里写0就从65536开始计数) +uint16_t read_i8254_counter(uint8_t counter_no) { + const uint8_t i8254_cmd_port = 0x43; + const uint8_t i8254_data_port = 0x40 + counter_no; + + assert(counter_no < 3); + + uint8_t cmd = 0x00; + cmd |= 0x03 << 6; // read back command + cmd |= 0x01 << 4; // don't latch status + cmd |= 0x01 << (counter_no + 1); // read back timer channel n + + uint16_t value = 0; + + outb_p(cmd, i8254_cmd_port); + + value |= inb(i8254_data_port) << 0; + value |= inb(i8254_data_port) << 8; + + return value; +} + +void setup_i8254(uint16_t hz) { + // PC/AT 8254的连接方法 + // 第0个计数器连连到了8259A的IRQ0 + // 第1个计数器连连到了DRAM刷新电路,通常情况下,该通道的频率设置为15.2kHz + // 第2个计数器连连到了音频驱动单元,可以通过频率控制发声 + + // 8254的最低频率为18.2Hz(1193180/65536,最大计数值是65536是因为往计数器里写0就从65536开始计数) assert(hz >= 19); const uint8_t counter_no = 0; // 第0个计数器 const uint8_t read_write_latch = 3; // 0 锁存数据供CPU读;1只读写低字节;2只读写高字节;3先读写低字节,后读写高字节 const uint8_t mode = 2; // + const uint8_t BCD = 0; // 0 二进制;1 BCD码 - const uint8_t cmd = (counter_no << 6) | (read_write_latch << 4) | (mode << 1); // 第0位为0表示二进制,为1表示BCD + const uint8_t cmd = + ((counter_no & 0x03) << 6) | ((read_write_latch & 0x03) << 4) | ((mode & 0x07) << 1) | ((BCD & 0x01) << 0); - const uint8_t i8253_cmd_port = 0x43; - const uint8_t i8253_data_port = 0x40 + counter_no; + const uint8_t i8254_cmd_port = 0x43; + const uint8_t i8254_data_port = 0x40 + counter_no; const uint32_t clock_rate = 1193180; uint16_t latch = (clock_rate + hz / 2) / hz; - outb_p(cmd, i8253_cmd_port); - outb_p(latch & 0xFF, i8253_data_port); - outb(latch >> 8, i8253_data_port); + // 必须先写控制命令,再写入初始计数值 + outb_p(cmd, i8254_cmd_port); + outb_p((latch >> 0) & 0xFF, i8254_data_port); + outb_p((latch >> 8) & 0xFF, i8254_data_port); + + // for (uint8_t i = 0; i < 3; i++) { + // printk("i8254 counter%u value %u\n", i, read_i8254_counter(i)); + // } } diff --git a/kernel/irq.c b/kernel/irq.c index b6f6a5b..b25f382 100644 --- a/kernel/irq.c +++ b/kernel/irq.c @@ -136,7 +136,7 @@ __attribute__((regparm(1))) void irq_handler(pt_regs_t *regs) { reenter--; // 考察如果不需要调度程序,直接退出 - if (current->ticks != 0) { + if (current->need_resched == 0) { return; } diff --git a/kernel/sched.c b/kernel/sched.c index b2af86e..86d9105 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -167,12 +167,12 @@ const char *task_state(unsigned int state) { void debug_print_all_tasks() { task_t *p = 0; list_head_t *pos = 0, *t = 0; - printl(MPL_TASK_TITLE, " NAME STATE TK/PI REASON SCHED KEEP"); + printl(MPL_TASK_TITLE, " NAME STATE TK/PI REASON SCHED KEEP TURN"); list_for_each_safe(pos, t, &all_tasks) { p = list_entry(pos, task_t, list); - printl(MPL_TASK_0 + p->pid, "%08x%s%-6s:%u %s %02u/%02u %-10s %-10u %-10u", p, + printl(MPL_TASK_0 + p->pid, "%08x%s%-6s:%u %s %02u/%02u %-10s %-10u %-10u %-10u", p, p->state == TASK_RUNNING ? ">" : " ", p->name, p->pid, task_state(p->state), p->ticks, p->priority, - p->reason, p->sched_cnt, p->sched_keep_cnt); + p->reason, p->sched_cnt, p->sched_keep_cnt, p->turn); } } @@ -182,17 +182,12 @@ void schedule() { task_t *p = 0; list_head_t *pos = 0, *t = 0; - assert(current->ticks <= TASK_MAX_PRIORITY); + assert(current->ticks >= 0); assert(current->priority <= TASK_MAX_PRIORITY); unsigned long iflags; irq_save(iflags); - if (0 == current->ticks) { - current->turn++; - current->ticks = current->priority; - } - if (current->state == TASK_RUNNING) { current->state = TASK_READY; } @@ -233,6 +228,8 @@ void schedule() { task_t *prev = current; task_t *next = sel != 0 ? sel : root; + prev->need_resched = 0; + next->state = TASK_RUNNING; next->reason = ""; diff --git a/kernel/setup.c b/kernel/setup.c index 12e2314..a44c169 100644 --- a/kernel/setup.c +++ b/kernel/setup.c @@ -24,7 +24,7 @@ extern void init_buffer(); extern void setup_gdt(); extern void setup_idt(); extern void setup_gate(); -void setup_i8253(uint16_t hz); +void setup_i8254(uint16_t hz); extern void detect_cpu(); extern void setup_sysc(); extern void setup_pci(); @@ -87,7 +87,7 @@ void setup_kernel() { boot_delay(DEFAULT_BOOT_DELAY_TICKS); - setup_i8253(100); + setup_i8254(100); setup_irqs(); void ide_init(); diff --git a/kernel/syscall.c b/kernel/syscall.c index 9979ce1..c1f5c01 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -43,20 +43,24 @@ int sysc_none() { extern uint64_t jiffies; -// 特别说明:如果想把这个函数的参数ticks改为uint64_t +// 特别说明:如果想把这个函数的参数ticks改为int64_t // 那么就需要在编写用户级的系统调用库函数的时候注意 // 不仅需要填写 ebx,还要填写 ecx // 不然就可能出现诡异的一直WAIT,不会调度到该任务的问题 -int sysc_wait(uint32_t ticks) { - unsigned long flags; - irq_save(flags); - current->state = TASK_WAIT; - current->reason = "sysc_wait"; - current->delay_jiffies = jiffies + ticks; - list_add(¤t->pend, &delay_tasks); - irq_restore(flags); - +int sysc_wait(int ticks) { + if (ticks < 0) { + return -EINVAL; + } else { + unsigned long flags; + irq_save(flags); + current->state = TASK_WAIT; + current->reason = "sysc_wait"; + current->delay_jiffies = jiffies + ticks; + list_add(¤t->pend, &delay_tasks); + irq_restore(flags); + } schedule(); + return 0; } int sysc_test() {} diff --git a/kernel/task_init.c b/kernel/task_init.c index 30753e7..7064470 100644 --- a/kernel/task_init.c +++ b/kernel/task_init.c @@ -9,7 +9,7 @@ #include #include #include -int sysc_wait(uint32_t ticks); +int sysc_wait(int ticks); void init_task_entry() { current->priority = 10; diff --git a/kernel/task_root.c b/kernel/task_root.c index 9ea4224..528f3a9 100644 --- a/kernel/task_root.c +++ b/kernel/task_root.c @@ -22,7 +22,7 @@ #include int do_fork(pt_regs_t *regs, unsigned long flags); -int sysc_wait(uint32_t ticks); +int sysc_wait(int ticks); #define get_eflags(x) __asm__ __volatile__("pushfl; popl %0;" : "=g"(x)::"memory")