在线Linux源码分析:
Linux source code (v6.9.3) - Bootlin
运行指令
./build_img.sh -file x86_64_InfiniLM -s 1024
make A=apps/monolithic_userboot LOG=error ARCH=x86_64 ACCEL=n run
注意:若子模块testcases目录下没有x86_64_InfiniLM
目录则需要更新
# 初始化子模块
git submodule update --init --recursive
# 更新子模块到远程仓库的最新commit
git submodule update --remote
# 同步子模块
git submodule sync
git pull
更新需将最新InfiniLM编译可执行文件xtask
放入x86_64_InfiniLM
目录下
cp path_to_InfiniLM/target_release path_to_StarryOS/testcases/x86_64_InfiniLM
之后重新移除添加axstarry
组件以建立文件系统链接
kbuild patch remove axstarry && kbuild patch add axstarry
首次运行后发现未实现系统调用epoll_create1
接口
首先拉取linux_syscall_api
组件(开始以为是arceos_posix_api
组件)
kbuild patch add linux_syscall_api
分析其中代码:
pub fn fs_syscall(syscall_id: fs_syscall_id::FsSyscallId, args: [usize; 6]) -> SyscallResult {
match syscall_id {
...
EPOLL_CREATE => syscall_epoll_create1(args),
...
#[cfg(target_arch = "x86_64")]
EPOLL_CREATE1 => unimplemented!(),
...
}
}
epoll_create
接口是实现了的pub fn syscall_epoll_create1(args: [usize; 6]) -> SyscallResult {
let _flag = args[0];
let file = EpollFile::new();
let process = current_process();
let mut fd_table = process.fd_manager.fd_table.lock();
if let Ok(num) = process.alloc_fd(&mut fd_table) {
fd_table[num] = Some(Arc::new(file));
Ok(num as isize)
} else {
// ErrorNo::EMFILE as isize
Err(SyscallError::EMFILE)
}
}
epoll_create1
源码和man手册epoll_create(2) - Linux manual page
SYSCALL_DEFINE1(epoll_create, int, size)
{
//我们可以看到epoll_create会在内部调用epoll_create1,参数没有什么用,
//所以我们编写代码的时候完全可以直接使用epoll_create1,还省一次函数调用
if (size <= 0)
return -EINVAL;
return sys_epoll_create1(0);
}
SYSCALL_DEFINE1(epoll_create1, int, flags)
{
int error;
struct eventpoll *ep = NULL;
/* Check the EPOLL_* constant for consistency. */
BUILD_BUG_ON(EPOLL_CLOEXEC != O_CLOEXEC);
if (flags & ~EPOLL_CLOEXEC)
return -EINVAL;
/*
* Create the internal data structure ("struct eventpoll").
*/
error = ep_alloc(&ep); //为一个eventpoll指针分配空间,并对成员初始化,下面会细说
if (error < 0)
return error;
/*
* Creates all the items needed to setup an eventpoll file. That is,
* a file structure and a free file descriptor.
*/
error = anon_inode_getfd("[eventpoll]", &eventpoll_fops, ep,
O_RDWR | (flags & O_CLOEXEC)); //这里会创建一个匿名文件,并返回文件描述符,下面会细讲
//还有一点其实值得一提,就是我们在创建epoll时经常会设置flag位为O_CLOEXEC,这样看来是不必要的 因为内核中已经帮我们做了这件事了
if (error < 0)
ep_free(ep);
return error;
}
epoll_create
内部实际调用epoll_create1
,size
参数实际没用epoll_create1
时没必要在flag
中使用O_CLOEXEC
,内核中已经加上了// in crates/linux_syscall_api/src/syscall_fs/mod.rs
pub fn fs_syscall(syscall_id: fs_syscall_id::FsSyscallId, args: [usize; 6]) -> SyscallResult {
match syscall_id {
...
EPOLL_CREATE => syscall_epoll_create(args),
...
#[cfg(target_arch = "x86_64")]
EPOLL_CREATE1 => syscall_epoll_create1(args),
...
}
}
// in crates/linux_syscall_api/src/syscall_fs/imp/epoll.rs
pub fn syscall_epoll_create1(args: [usize; 6]) -> SyscallResult {
let _flag = args[0];
let file = EpollFile::new();
let process = current_process();
let mut fd_table = process.fd_manager.fd_table.lock();
if let Ok(num) = process.alloc_fd(&mut fd_table) {
fd_table[num] = Some(Arc::new(file));
drop(fd_table); // 提前释放,避免fcntl调用中重复上锁
// 设置CLOEXEC
if let Ok(flags) = syscall_fcntl64([num as usize, Fcntl64Cmd::F_GETFD as usize, 0, 0, 0, 0]) {
let _ = syscall_fcntl64([
num as usize,
Fcntl64Cmd::F_SETFD as usize,
_flag | flags as usize | O_CLOEXEC as usize, 0, 0, 0
])?;
}
Ok(num as isize)
} else {
// ErrorNo::EMFILE as isize
Err(SyscallError::EMFILE)
}
}
pub fn syscall_epoll_create(args: [usize; 6]) -> SyscallResult {
syscall_epoll_create1(args)
}
EPOLL_CREATE ⇒ syscall_epoll_create1(args)
节省一次调用编译执行后报错在一个Err
值上执行unwrap
,分析报错InfiniLM代码
// in xtask/src/main.rs:182
trait Task: Sized {
...
fn run() {
...
// 启动 tokio 运行时
let runtime = tokio::runtime::Runtime::new().unwrap();
...
}
...
}
Runtime::new()
因其返回Result
#[cfg(feature = "rt-multi-thread")]
#[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))]
pub fn new() -> std::io::Result<Runtime> {
Builder::new_multi_thread().enable_all().build()
}
build
方法pub fn build(&mut self) -> io::Result<Runtime> {
match &self.kind {
Kind::CurrentThread => self.build_current_thread_runtime(),
#[cfg(feature = "rt-multi-thread")]
Kind::MultiThread => self.build_threaded_runtime(),
#[cfg(all(tokio_unstable, feature = "rt-multi-thread"))]
Kind::MultiThreadAlt => self.build_alt_threaded_runtime(),
}
}
let (driver, driver_handle) = driver::Driver::new(self.get_cfg(1))?;
会产生错误,进入Driver::new
pub(crate) fn new(cfg: Cfg) -> io::Result<(Self, Handle)> {
let (io_stack, io_handle, signal_handle) = create_io_stack(cfg.enable_io, cfg.nevents)?;
...
}
create_io_stack
fn create_io_stack(enabled: bool, nevents: usize) -> io::Result<(IoStack, IoHandle, SignalHandle)> {
...
let ret = if enabled {
let (io_driver, io_handle) = crate::runtime::io::Driver::new(nevents)?;
...
} else {
..
};
...
}
Driver::new
impl Driver {
/// Creates a new event loop, returning any error that happened during the
/// creation.
pub(crate) fn new(nevents: usize) -> io::Result<(Driver, Handle)> {
let poll = mio::Poll::new()?;
#[cfg(not(target_os = "wasi"))]
let waker = mio::Waker::new(poll.registry(), TOKEN_WAKEUP)?;
let registry = poll.registry().try_clone()?;
...
}
...
}
let poll = mio::Poll::new()?;
初始化Poll
,调用epoll_create1
,若失败则调用epoll_create
和fcntl
达到同样效果
let waker = mio::Waker::new(poll.registry(), TOKEN_WAKEUP)?;
初始化Waker
,通过epoll_ctl
注册
let registry = poll.registry().try_clone()?;
调用fcntl
进行拷贝文件描述符操作
通过axlog
验证后发现当前epoll_create1
实现返回错误,因为fcntl
调用中的set_close_on_exec
使用的是默认实现(一直返回false)
// in crates/axfs/src/api/port.rs
/// 设置 close_on_exec 位
/// 设置成功返回false
fn set_close_on_exec(&self, _is_set: bool) -> bool {
false
}
观察fcntl
实现
// in crates/linux_syscall_api/src/syscall_fs/imp/ctl.rs
let file = fd_table[fd].clone().unwrap();
info!("fd: {}, cmd: {}", fd, cmd);
match Fcntl64Cmd::try_from(cmd) {
Ok(Fcntl64Cmd::F_GETFD) => {
if file.set_close_on_exec(...) { Ok() }
else { Err() }
}
}
观察file
类型为Arc<dyn FileIO>
,以及从先前let file = EpollFile::new();
可知检查EpollFile
对trait FileIO
的实现
// in crates/linux_syscall_api/src/syscall_fs/ctype/epoll.rs
fn set_close_on_exec(&self, is_set: bool) -> bool {
if is_set {
// 设置close_on_exec位置
*self.flags.lock() |= OpenFlags::CLOEXEC;
} else {
*self.flags.lock() &= !OpenFlags::CLOEXEC;
}
true
}
impl FileIO for EpollFile
添加如上实现,从而不使用默认实现若运行仍然报错EINVAL: 22
,检查fcntl
的F_DUPFD_CLOEXEC
和F_SETFD
实现
运行报错找不到系统调用332 — statx
fstatat
调用类似statx
,不过statx
会先调用do_statx
对mask
进行处理