[bug] Linux program cannot receive interrupt while running.
wfly1998 opened this issue · 4 comments
Environment: QEMU
Command: cd zCore && make run linux=1
File: zCore/src/main.rs
#[cfg(feature = "linux")]
fn main(ramfs_data: &'static mut [u8], _cmdline: &str) {
use alloc::sync::Arc;
use alloc::vec;
use linux_object::fs::MemBuf;
let args = vec!["/bin/loop".into()]; // <-- I'VE EDITED HERE
let envs = vec!["PATH=/usr/sbin:/usr/bin:/sbin:/bin:/usr/x86_64-alpine-linux-musl/bin".into()];
let device = Arc::new(MemBuf::new(ramfs_data));
let rootfs = rcore_fs_sfs::SimpleFileSystem::open(device).unwrap();
let _proc = linux_loader::run(args, envs, rootfs);
run();
}
fn run() -> ! {
loop {
executor::run_until_idle(); // <-- PROBLEM OCCURS HERE
x86_64::instructions::interrupts::enable_interrupts_and_hlt(); // <-- WHEN PROGRAM BLOCKS, IT WILL NEVER RUN
x86_64::instructions::interrupts::disable();
info!("I'm running."); // <-- I'VE EDITED HERE
}
}
Noticed that I replace args from busybox
to loop
,the code of loop
is following:
#include <stdio.h>
int main()
{
while (1);
return 0;
}
When a zircon program is running, the info "I'm running" displays.
But when a linux program is running, before it ends, the info will never display, which also means that interrupt is not enabled, the program will never receive interrupt when running.
And when I turn log level to trace, busybox
can receive timer interrupt but loop
cannot.
The executor::run_until_idle()
function will keep running tasks in the ready queue, until the queue becomes empty.
During this time the interrupt keeps disabled, to avoid handling difficult situations.
So when running your loop
program, the task will always be in the ready queue and the function will never return.
A possible solution is to enable interrupt every time before running a task in the executor.
I even edited executor::run_until_idle()
to following:
/// Run futures until there is no runnable task.
pub fn run_until_idle() {
while let Some(task) = { || GLOBAL_EXECUTOR.lock().pop_runnable_task() }() {
x86_64::instructions::interrupts::enable(); // <-- ENABLE INTERRUPT
task.mark_sleep();
x86_64::instructions::interrupts::enable(); // <-- ENABLE INTERRUPT
// make a waker for our task
let waker = waker_ref(&task);
x86_64::instructions::interrupts::enable(); // <-- ENABLE INTERRUPT
// poll our future and give it a waker
let mut context = Context::from_waker(&*waker);
x86_64::instructions::interrupts::enable(); // <-- ENABLE INTERRUPT
let ret = task.future.lock().as_mut().poll(&mut context);
x86_64::instructions::interrupts::enable(); // <-- ENABLE INTERRUPT
if let Poll::Pending = ret {
GLOBAL_EXECUTOR.lock().push_task(task);
x86_64::instructions::interrupts::enable(); // <-- ENABLE INTERRUPT
}
x86_64::instructions::interrupts::disable();
}
}
Interrupt still doesn't work...
It's my fault, I've thought that trap_handler
is the entry of all interrupt, but it's only the entry of kernel interrupt...
Entry of user interrupt is context_run
in kernel-hal-bare/src/lib.rs
.