2022-09-20 update
This commit is contained in:
parent
fd99a1abb2
commit
1854626938
|
@ -0,0 +1,23 @@
|
|||
### 简介
|
||||
|
||||
Rust核心的分配与集合库。这个库提供智能指针和集合,用于管理堆分配的值。
|
||||
|
||||
#### Boxed值
|
||||
|
||||
`Box`类型是智能指针类型。
|
||||
|
||||
#### 引用计数指针
|
||||
|
||||
`Rc`类型是非线程安全的引用计数指针类型,用于在线程内共享内存。
|
||||
|
||||
#### 自动引用计数指针
|
||||
|
||||
`Arc`类型是线程安全的引用计数指针类型。
|
||||
|
||||
#### 集合
|
||||
|
||||
定义了最常用的数据结构的实现。
|
||||
|
||||
#### 堆接口
|
||||
|
||||
`alloc`模块定义了默认的全局分配器的底层接口。
|
|
@ -0,0 +1,15 @@
|
|||
线程安全的引用计数指针。
|
||||
|
||||
#### 结构体
|
||||
|
||||
##### Arc
|
||||
|
||||
Arc的意思是自动引用计数(Atomically Reference Counted)。这是一个线程安全的引用计数指针。
|
||||
|
||||
```rust
|
||||
pub fn new(data: T) -> Arc<T> {} // 构造一个新的Arc<T>
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### Weak
|
|
@ -0,0 +1,75 @@
|
|||
#### 概述
|
||||
|
||||
从应用程序的角度看,内核是辅助应用程序执行的一个程序。从硬件的角度看,内核是管理硬件的程序。这两者都是外部视角,本章就以maturin为例,从外部视角来看内核。
|
||||
|
||||
通过观察ramfs里的内容,看到并没有暴露内核里的任何数据,也没有把设备抽象为设备文件,所以maturin对上的接口仅是系统调用。
|
||||
|
||||
通过观察qemu启动时的参数,看到maturin对下的接口是qemu virt物理机。
|
||||
|
||||
#### 系统调用
|
||||
|
||||
##### 执行流程
|
||||
|
||||
1. 用户程序执行`ecall`指令进入用户态。
|
||||
2. 内核态下`stvec`寄存器保存了自陷处理的入口,这是在内核启动时执行`trap::init()`来设置的。
|
||||
3. 从`trap::init()`可见,`__alltraps`是所有自陷处理的入口。
|
||||
4. `__alltraps`先保存了上下文信息,然后调用了`trap_handler()`。
|
||||
5. `trap_handler()`分发自陷给相应的处理函数。这是通过`sstatus`寄存器的SPP位得到进入S模式之前的特权级,进而实现函数的分发。如果进入S模式之前是U模式,则执行`user_trap_handler()`。
|
||||
6. `user_trap_handler()`处理来自用户态的各种自陷(系统调用,异常,时钟中断),以及当前线程的信号。关于自陷的种类,是通过读`scause`寄存器获得的。如果是系统调用,则调用`syscall()`。
|
||||
7. `syscall()`是系统调用的处理者,它通过系统调用号来把系统调用分发给不同的函数处理。
|
||||
8. `syscall()`处理完成返回到`user_trap_handler()`,再返回到`trap_handler()`,再返回到`__alltraps`。
|
||||
9. 在`__alltraps`里恢复上下文,执行`sret`返回用户程序继续执行。
|
||||
|
||||
##### 系统调用的列表
|
||||
|
||||
- 文件系统
|
||||
|
||||
文件描述符操作:DUP, DUP3, FCNTL64
|
||||
|
||||
文件操作:UNLINKAT,LINKAT,OPEN,CLOSE,READ,LSEEK,WRITE,WRITEV,READV,PREAD,SENDFILE64
|
||||
|
||||
获取文件状态:STATFS,FSTATAT,FSTAT,UTIMENSAT
|
||||
|
||||
目录操作:GETCWD,MKDIR,CHDIR,UMOUNT,MOUNT,GETDENTS64
|
||||
|
||||
其它:PIPE, IOCTL
|
||||
|
||||
- 进程
|
||||
|
||||
状态管理:EXIT,EXIT_GROUP,YIELD,KILL,FUTEX,CLONE, EXECVE, WAIT4,, PPOLL
|
||||
|
||||
ID管理:SET_TID_ADDRESS,GETPID,GETPPID,GETUID,GETEUID,GETGID,GETEGID,GETTID
|
||||
|
||||
其它:TIMES, PRLIMIT64
|
||||
|
||||
- 时间
|
||||
|
||||
NANOSLEEP,CLOCK_GET_TIME,GET_TIME_OF_DAY
|
||||
|
||||
- 信号
|
||||
|
||||
SIGACTION,SIGPROCMASK,SIGRETURN , SIGTIMEDWAIT
|
||||
|
||||
- 网络
|
||||
|
||||
SOCKET, SENDTO, RECVFROM
|
||||
|
||||
- 内存
|
||||
|
||||
BRK, MUNMAP, MMAP , MEMBARRIER
|
||||
|
||||
- 其它
|
||||
|
||||
UNAME
|
||||
|
||||
#### virt物理机
|
||||
|
||||
通过`qemu-system-riscv64 -M virt -M dumpdtb=riscv64-virt.dtb -bios default`导出设备树,可以观察virt物理机的内容。也可以在qemu控制台,通过`info ptree`命令观察virt物理机的内容:
|
||||
|
||||
- plic, clint
|
||||
- pci总线
|
||||
- uart串口
|
||||
- rtc时钟
|
||||
- cpu
|
||||
- 内存
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
#### 背景知识
|
||||
|
||||
##### 编译的过程
|
||||
|
||||
1. `make testcases-img`生成文件系统镜像`fat.img`,且在`fat.img`里包含测例。
|
||||
1. `make run`核心操作就是`cargo build`。
|
||||
1. maturin使用了构建脚本`build.rs`,cargo会先编译并执行该脚本,再构建整个项目。
|
||||
1. `build.rs`通过`println!`和cargo通信,告诉它如果`build.rs`或`../fat.img`发生变化则重新编译运行它自己。
|
||||
1. `build.rs`调用`insert_fs_img()`,在`kernel`的源码里加入一个包含一小段汇编代码的文件`fs.S`,把`../fat.img`包含进去。
|
||||
1. `build.rs`在输出目录里创建`linker.ld`文件,把字符串`LINKER`的内容写入这个文件。
|
||||
1. `build.rs`调用`println!`告诉cargo把`-C link-arg=-Tlinker.ld`选项传给编译器,其实就是指定了链接脚本。链接脚本里比较重要的信息有:内核入口点是`_start`,内核装载位置是`0xffffffff80200000`。
|
||||
|
||||
##### 初始化的流程
|
||||
|
||||
1. 入口为`arch/riscv/mod.rs`的`entry()`函数,使用汇编代码设置栈和页表,然后跳转到`start_kernel()`开始Rust代码的执行。
|
||||
2. `start_kernel()`调用相关模块实现内存、自陷、文件系统的初始化。
|
||||
3. `start_kernel()`通过调用`arch::secondary_entry`初始化其它的核。
|
||||
1. 使用汇编代码设置栈和页表,然后跳转到`start_kernel_secondary()`开始Rust代码的执行。
|
||||
2. `start_kernel_secondary()`初始化当前核的页表和自陷,等待主核初始化结束后开始执行`task::run_tasks()`。
|
||||
4. 主核执行`task::run_tasks()`,开始运行用户程序。
|
||||
5. `task::run_tasks()`就是一个大`loop`循环,这个循环的内容是:
|
||||
1. 首先调用`fetch_task_from_scheduler()`从调度器器里取一个任务。
|
||||
1. 执行`task.vm.lock().activate()`切换到用户的页表。最终是写`satp`寄存器实现的,且要注意刷新TLB.
|
||||
2. 通过`__switch`切换到任务的上下文,用户程序开始执行。
|
||||
3. 用户程序执行完毕后,调用`enable_kernel_page_table()`切换回内核页表。
|
||||
4. 获取任务的状态,执行相应的操作。
|
||||
5. 如果没有任务了,则调用`panic!()`退出系统。
|
||||
|
||||
##### 第一个用户进程
|
||||
|
||||
1. `fetch_task_from_scheduler()`调用`load_next_testcase()`创建一个用户程序的PCB,先`push`进调度器`GLOBAL_TASK_SCHEDULER`,再`pop`出来,返回该测例的TCB.
|
||||
2. `load_next_testcase()`把用户程序加载到TCB里。这最终是通过`from_app_name()`实现的。
|
||||
3. `from_app_name()`从用户程序的名称生成TCB。其中,在构造内核栈的时候调用了`app_init_context()`初始化用户程序的上下文。
|
||||
4. `app_init_context()`把`sstatus`寄存器的`SPP`位设置为`SPP::User`。
|
||||
|
||||
#### 概述
|
||||
|
||||
应用程序对用户是有意义的,用户不需要内核,是应用程序需要内核。
|
||||
|
||||
应用程序需要内存来运行自己,于是内核把内存抽象成页,再把页分配给应用程序。
|
||||
|
||||
应用程序需要把运行结果记录下来,于是内核把硬盘抽象成文件系统,这样应用程序就可以把结果保存为文件。
|
||||
|
||||
应用程序需要并行以提高效率,于是内核把CPU抽象成进程。
|
||||
|
||||
内核为了完成上述的三个抽象,还需要一个调度进程来调度所有的应用程序,需要一个自陷管理进程来让应用程序和外设进行交互,需要提供进程间交互的机制(比如信号)。
|
||||
|
||||
#### 内存
|
||||
|
||||
RISC-V在S态提供了把内存抽象成页的功能,和其它架构的实现方式一样,这在本质上就是“多级查表”。第一级表是4k的页,这个页的物理地址放在`satp`寄存器。
|
||||
|
||||
RV32的分页方式是Sv32,RV64的分页方式是Sv39和Sv48。分页方式也记录在`satp`寄存器。
|
||||
|
||||
- Sv32是二级查表,寻址4G的虚拟空间。
|
||||
- Sv39是三级查表,寻址512G的虚拟空间。
|
||||
- Sv48是四级查表,寻址256T的虚拟空间。
|
||||
|
||||
如果修改了页表,需要用`sfence.vma`指令刷新TLB。
|
||||
|
||||
addr子模块实现物理地址与虚拟地址、页与地址之间的转换。
|
||||
|
||||
allocator子模块实现内存分配,主要是堆分配和页桢分配。
|
||||
|
||||
area子模块实现物理段的trait,虚拟段的操作。
|
||||
|
||||
page_table子模块实现页表操作。
|
||||
|
||||
user子模块实现用户空间指针的检查。
|
||||
|
||||
vmm子模块实现虚拟段映射操作。
|
||||
|
||||
#### 进程
|
||||
|
||||
进程是拥有自己的上下文(寄存器值)的一串机器指令。它的存在形式由ELF格式控制,它在内核里表现为TCB(任务控制块)。
|
||||
|
||||
控制进程的状态是通过控制TCB里的状态字段实现的。
|
||||
|
||||
#### 文件系统
|
||||
|
||||
maturin的文件系统在底层是RAM块,上层是通过调用外部库[rust-fatfs](https://github.com/rafalh/rust-fatfs)实现的。
|
||||
|
||||
#### 调度进程
|
||||
|
||||
调度进程维护了一个就绪队列`ready_queue`,`push`方法把进程加到队列末尾,`pop`方法把进程从队列顶头弹出,这样就实现了轮转调度算法。
|
||||
|
||||
#### 自陷管理进程
|
||||
|
||||
自陷分为系统调用,中断和异常。
|
||||
|
||||
`__alltraps`是所有自陷的入口。
|
||||
|
||||
`kernel_trap_handler`处理内核态的自陷。
|
||||
|
||||
`user_trap_handler`处理用户态的自陷。
|
||||
|
||||
#### 信号
|
||||
|
||||
操作TCB里与信号相关的字段。
|
|
@ -0,0 +1,88 @@
|
|||
maturin的文件系统在底层是RAM块,上层是通过调用外部库[rust-fatfs](https://github.com/rafalh/rust-fatfs)实现的。
|
||||
|
||||
|
||||
|
||||
#### file模块
|
||||
|
||||
```rust
|
||||
mod backend;
|
||||
mod device;
|
||||
mod epoll;
|
||||
mod fd_manager;
|
||||
mod fs_stat;
|
||||
mod kstat;
|
||||
mod pipe;
|
||||
mod poll_events;
|
||||
mod stdio;
|
||||
mod vfs;
|
||||
pub mod socket;
|
||||
|
||||
use crate::timer::TimeSpec;
|
||||
use alloc::vec::Vec;
|
||||
use core::any::Any;
|
||||
|
||||
pub use fatfs::SeekFrom;
|
||||
|
||||
/// 文件类抽象
|
||||
pub trait File: Send + Sync + AsAny {
|
||||
...
|
||||
}
|
||||
|
||||
pub use device::{...};
|
||||
pub use backend::{BackEndFile, SyncPolicy};
|
||||
pub use device::{FileDisc, OpenFlags};
|
||||
pub use epoll::{EpollFile, EpollEvent, EpollEventType, EpollCtl};
|
||||
pub use fd_manager::FdManager;
|
||||
pub use fs_stat::FsStat;
|
||||
pub use kstat::normal_file_mode;
|
||||
pub use kstat::{Kstat, StMode};
|
||||
pub use pipe::{Pipe, RingBuffer};
|
||||
pub use poll_events::PollEvents;
|
||||
pub use socket::Socket;
|
||||
pub use vfs::{...};
|
||||
```
|
||||
|
||||
##### device
|
||||
|
||||
```rust
|
||||
lazy_static::lazy_static! {
|
||||
//static ref MEMORY_FS: Arc<Mutex<FileSystem<FsIO, FsTP, FsOCC>>> = Arc::new(Mutex::new(new_memory_mapped_fs()));
|
||||
static ref MEMORY_FS: FATFileSystem = new_memory_mapped_fs();
|
||||
}
|
||||
|
||||
/// 通过info级的日志打印出根目录下的所有文件
|
||||
pub fn list_files_at_root() {...}
|
||||
|
||||
/// 初始化硬盘的内容
|
||||
pub fn fs_init() {
|
||||
// 1. 生成目录/dev, /lib, /tmp, /dev/shm
|
||||
// 2. 把根目录下的库文件链接到/lib目录下
|
||||
// 3. 生成目录/sbin, 并把根目录下的测试程序链接到/sbin目录下
|
||||
// 4. 创建/proc目录及其文件
|
||||
// 5. 创建/dev/disc目录及其文件
|
||||
}
|
||||
|
||||
/// 若干函数
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### epoll
|
||||
|
||||
##### socket
|
||||
|
||||
##### vfs
|
||||
|
||||
##### backend
|
||||
|
||||
##### fd_manager
|
||||
|
||||
##### fs_stat
|
||||
|
||||
##### kstat
|
||||
|
||||
##### pipe
|
||||
|
||||
##### poll_events
|
||||
|
||||
##### stdio
|
|
@ -165,3 +165,91 @@ ioctl(file_descriptor, request, argp);
|
|||
当键盘输入最终发生时,会产生两个中断,一个是键被按下时产生,一个是键被释放时产生。需要注意的是键盘生成的不是ASCII码,而是**扫描码**(scan code)。同一个键扫描码的低7位是相同的,最高位按下是0,释放是1。
|
||||
|
||||
键盘中断是IRQ 1。此中断线无法在系统总线上访问,也不能被其它I/O适配器共享。当`_hwint01`调用`intr_handle`,要通知的TTY会被很快找到。低层的中断处理例程通过直接调用系统任务里的`generic_handler`发出通知消息。`tty_task`接收到`HARD_INT`消息后就分发给`kbd_interrupt`,`kbd_interrupt`再调用`scan_keyboard`。`scan_keyboard`进行了3个内核调用让系统调用读写若干 I/O端口,最终得到扫描码,并把扫描码放到循环缓冲区中。然后设置`tty_events`标志,表示此缓冲区包含字符不再为空。
|
||||
|
||||
每当`tty_task`的主循环开始另一轮循环时,它检索每个终端设备的`tty_events`标志,对于设置了此标志的设备则调用`handle_events`。`handle_events`调用设备相关的函数来进行输入输出,因为`tty_events`标志可以标示各种活动。对于键盘输入来说`handle_events`调用的是`kb_read`,`kb_read`把扫描码转换为ASCII码。`kb_read`再调用`in_process`来处理ASCII码,一般来说就是把字符加到控制台的输入队列`tty_table`。`in_process`还需要把ASCII码回显到显示器上。
|
||||
|
||||
当接收到足够的字符,终端驱动程序执行另一个内核调用让系统任务把数据复制到shell所要求的地址上。复制数据不是通过消息传递来实现的。为满足用户需求,可能要进行多次这样的操作。当操作最终完成,驱动程序会向文件系统发消息表示自己的工作完成了,然后文件系统会向shell发消息来解除阻塞。
|
||||
|
||||
判断是否接收到足够字符的标准取决于终端的模式。规范模式下的标准是,接收到了换行码(linefeed),行尾码(end-of-line)或文件尾码(end-of-file),且一个行的大小不能超过输入队列的大小。非规范模式下的标准是,文件系统接收到操作完成的消息,在此之前读操作可以请求大量的字符,`in_process`可能需要传输多次。
|
||||
|
||||
注意,系统任务是直接把字符从TTY的地址空间复制到shell的地址空间的。这并不经过文件系统。对于块I/O开说,数据确实是经过文件系统的,文件系统使用缓冲区缓存来保存常用的块。
|
||||
|
||||
然而对于键盘I/O,缓存是没用的。文件系统请求磁盘只需要几百毫秒,所以等的起。键盘I/O则可能要花几小时,不可能让文件系统阻塞来等它完成的。
|
||||
|
||||
##### 其它输入
|
||||
|
||||
串行端口上传输的是字符,而不是扫描码。老的UART没有缓冲,所以每次按键都需要一个中断。新的UART有缓冲(16~128字节),可以配置为缓冲区有多个字符时产生一个中断。以太网传输速度比串行线更快,但以太网适配器可以缓存整个包,每个包只需要一个中断。
|
||||
|
||||
##### 终端输入总结
|
||||
|
||||
终端驱动程序被读请求首次激活时发生的事件:
|
||||
|
||||
- 消息抵达终端驱动程序以请求键盘字符,主例程`tty_task`调用`do_read`来处理此请求。`do_read`把调用参数保存到`tty_table`的键盘入口里,以防止没有缓存足够的字符来满足此请求。
|
||||
- `do_read`先调用`in_transfer`以接收已经在等待的输入,再调用`handle_events`。`handle_events`先调用`kb_read`把键盘输入立即复制给用户,再调用`in_transfer`以从输入流中把字符提取出来。如果`in_transfer`或`handle_events`完成了读操作,则向文件系统发消息,这样文件系统就可以解除对调用者的阻塞。如果没有完成读操作(因为字符数不够),则`do_read`会告诉文件系统挂起调用者(阻塞的读操作)或取消读操作(非阻塞的读操作)。
|
||||
|
||||
终端驱动程序被键盘输入再次激活时发生的事件:
|
||||
|
||||
- 当键入一个字符,中断“处理例程“`kbd_interrupt`调用`scan_keyboard`,`scan_keyboard`通过调用系统任务来完成I/O。(`kbd_interrupt`不是真正的处理例程,是系统任务里的`generic_handler`向`tty_task`发消息才把它激活的。)`kbd_interrupt`把扫描码放到键盘缓冲区`ibuf`,并设置一个标志以表明在控制台设备上有事件发生了。当`kbd_interrupt`把控制转移给`tty_task`,一个`continue`语句让主循环开始下一轮循环。`tty_task`会检查所有终端设备的事件标志(event flags),对于被标志的设备则调用`handle_events`。如果被标志的是键盘,`handle_events`则调用`kb_read`和`in_transfer`,就像读请求首次激活时那样处理。此事件可能会发生多次,直到接收到足够的字符满足`do_read`的要求。
|
||||
- `kb_read`调用的函数包括:`map_key`把扫描码转换成ASCII码。`make_break`追踪修饰键(modifier keys)的状态,比如SHIFT键。`in_process`处理复杂情况,比如退格键、其它特殊字符、不同输入模式下的可用选项。`in_process`也会调用`tty_echo`,所以输入的字符也会显示在显示器上。
|
||||
|
||||
##### 从应用程序到控制台输出的流程
|
||||
|
||||
进程一般是调用`printf`来打印一些东西。`printf`调用`write`向文件系统发消息,这个消息里包含了一个指针指向要打印的字符。文件系统向终端驱动程序发消息,终端驱动程序把那些字符复制到视频RAM里。
|
||||
|
||||
当终端驱动程序收到写屏幕的消息,则调用`do_write()`把参数保存进`tty_table`里控制台的结构体`tty`里。`do_write()`接下来再调用`handle_events()`,此函数为参数里指定的设备再调用输入和输出例程。对于控制台输出,这意味着等待着的键盘输入都先被处理过了。如果还有等待着的输入,则会把要回显的字符添加到等待输出的字符中。`handle_events()`接下来再调用`cons_write()`,`cons_write()`就是内存映射显示器的输出例程。`cons_write()`使用`phys_copy`从用户进程复制字符块到当前缓冲区。当前缓冲区满后(64字节),所有字节被转移到另一个缓冲区`ramqueue`。`ramqueue`是由16位字组成的数组。16位字里的另一半是屏幕属性字节,它控制着前景色、后景色和其它属性。一般字符直接传进`ramqueue`里即可,但特殊字符需要特别处理。当字符位置超过屏幕宽度,或`ramqueue`满时也需要特殊处理。`out_char`就是这个进行特殊处理的函数。例如,在屏幕最后一行时收到换行符`out_char()`会调用`scroll_screen()`,如果是转义序列则调用`parse_escape()`来处理字符。`out_char()`调用`flush()`把`ramqueue`的内容复制到视频显示内存中,这是通过汇编例程`mem_vid_copy`实现的。最后一个字符传进`ramqueue`后也会调用`flush()`,确保所有的输出都会显示出来。`flush()`最后会让6845视频控制器芯片显示正确的光标位置。
|
||||
|
||||
##### 视频RAM的管理
|
||||
|
||||
| 字段 | 含义 |
|
||||
| -------- | ------------------------------------------------ |
|
||||
| c_start | 当前控制台显存的起始位位置 |
|
||||
| c_limit | 当前控制台显存的大小 |
|
||||
| c_column | 光标所在列的列号 |
|
||||
| c_row | 光标所在行的行号 |
|
||||
| c_cur | 光标在显存中的位置 |
|
||||
| c_org | 6845基址寄存器指向的内存位置,即屏幕显示的起始点 |
|
||||
|
||||
`console`结构体的`c_start`和`c_limit`字段代表了控制台的视频RAM。当前的光标位则在`c_column`和`c_row`字段。坐标(0,0)在屏幕的左上角,这个位置就是硬件填充屏幕的起点。视频扫描从`c_org`的地址开始,延续80x25字节(即4000字节)。这就意味着,6845芯片从`c_org`拉取字符,并从屏幕的左上角开始显示它们。先是第一行从(0,0)到(79,0),然后是第二行从(0,1)到(79,1),最后一直到第二十五行(0,24)到(79,24)。
|
||||
|
||||
当计算机启动,屏幕被清空,输出被写入到视频RAM的`c_start`位,此时`c_org`和`c_start`是相等的。当行满或检测到换行符,输出被写入到`cstart + 80`。当25行都满,则会要求屏幕滚动。一些程序(比如编辑程序)还有向下滚动的要求,如果此时光标在顶行且要求继续向上移动。
|
||||
|
||||
滚动屏幕有两种方式。一种是**软件滚动**,总是把要在(0,0)坐标上显示的字符放在显存的起始位置,`c_start`代表第0个字的位置。`c_org`保存和`c_start`相同的值,这样视频控制器芯片就会首先从此位置开始显示了。当屏幕需要滚动时,视频RAM上相对位置80的内容要复制到相对位置0,81则复制到相对位置1,以此类推。扫描队列没有改变,还是把屏幕上(0,0)位置的数据放在显存的第0个位置,视屏图像整体向上移动一行。开销是CPU移动了1920(80*24)个字。一种是**硬件滚动**,不是数据在显存里移动,而是视频控制芯片从不同的位置开始显示。比如,从数据的第80个字符开始显示。把80加进`c_org`里,然后把这个值写入视频控制器芯片对应的寄存器里。这要求要么控制器足够聪明能够利用有限的视频RAM进行正确的显示,要么视频RAM足够大可以保存多屏的内容。
|
||||
|
||||
较老的显卡内存小,只能让显示的内容回绕。新显卡内存大,控制器不能回绕。比如保存204行需要32768字节的显存(204x80x2),则可滚动179次(204-25)。但触底后还是需要把最后24行复制到显存的顶部。
|
||||
|
||||
当开启了虚拟控制台,这些控制台就会均分视频适配器的内存,这是通过设置每个控制台的`c_start`和`c_limit`字段实现的。这会影响到滚屏。尽管硬件滚屏是有效的,但此时一般会使用软件滚屏。当控制台的数量达到可能的最大值时,每次滚屏都将是软件滚屏。
|
||||
|
||||
虽然光标位可以通过`c_column`和`c_row`计算出来,但用字段`c_cur`记录它的值会更高效。被打印的字符会放在`c_cur`的位置,然后`c_cur`和`c_column`都被更新。
|
||||
|
||||
通过调整`c_column`, `c_row`和`c_cur`就可以处理影响光标位置的字符(比如换行,回退)。`flush()`通过在最后调用`set_6845()`实现此功能。
|
||||
|
||||
##### 解析转义序列
|
||||
|
||||
Minix3实现了一个有限状态自动机来解析转义序列。控制台结构体里的字段`c_esc_state`一般为0。当`out_char()`检测到`ESC`字符,它就把`c_esc_state`置1,后继字符则由`parse_escape()`来处理。`parse_escape()`会依据`c_esc_state`的值来选择相应的操作,如果下一个字符是控制序列引导符`[`则将`c_esc_state`置2,否则就认为此序列已完成并调用`do_escape()`。如果是2,只要下一个字符是数字,则将`c_esc_parmp`转换为`c_esc_parmp*10 `加上该字符对应的数值(因为`c_esc_parmp`的初值为0,这就可以把数字的字符序列转化为对应的数值)。当下一个字符变成分号,则把处理转到`c_esc_parmp`数组的下一个元素(此数组只包含两个元素)。当下一个字符即非数字也非分号,则认为序列已完成,会再次调用`do_escape()`。`do_escape()`依据当前字符来选择采取什么动作,怎么解释参数。
|
||||
|
||||
##### 可加载的键盘映射
|
||||
|
||||
扫描码是按键的编号,按下时产生的扫描码高位是0,松开时产生的扫描码高位是0。通过记录按下和松开的状态,可产生大量的组合键。
|
||||
|
||||
操作系统使用**键盘映射**(keymap)来把键的状态转换为字符码。Minix3的键盘映射从逻辑上看就是一个128行,6列的数组。128行代表了可能的扫描码,之所以设计的这么大是为了兼容日文键盘。6列分别代表了无修饰键(no modifier),SHIFT键,Control键,左ALT键,右ALT键,ALT+SHIFT组合键。这种方案可产生720种字符码(`(128-6)*6`)。这就要求键盘映射里每个表项都是16位大小。
|
||||
|
||||
在`keyboard.c`里通过`#include keymaps/us-std.src`把标准键盘映射编译进了Minix3内核,但通过`ioctl(0, KIOCSMAP, keymap)`也可以把不同的映射载入内核地址`keymap`。 一个完整的映射占据1536字节(`128*6*2`)。额外的键盘映射会以压缩形式存储。程序`genmap`用于生成新的压缩的键盘映射。`genmap()`会为指定的键盘映射包含`<keymap>.src`文件,并把压缩的键盘映射输出为文件。`loadkeys`命令读取压缩的键盘映射,把它展开,然后调用`ioctl()`把键盘映射转移到内核内存。Minix3在启动时会自动执行`loadkeys`,用户也可以调用`loadkeys`。
|
||||
|
||||
键盘映射的源码在`src/kernel/keymaps/us-std.src`,里面定义了一个已经初始化的大数组。扫描码0不对应IBM-PC键盘上的任何一个键。扫描码1对应ESC键,当按下SHIFT或CTRL键不影响返回值,但ALT和ESC一同按下会返回不同的编码。在`include/minix/keymap.h`里的宏定义了这些返回值的计算方法:
|
||||
|
||||
```c
|
||||
#define C(c) ((c) & 0x1F) /* 映射到控制码 */
|
||||
#define A(c) ((c) | 0x80) /* 最高位置1 (ALT) */
|
||||
#define CA(c) A(C(c)) /* Control-Alt */
|
||||
#define L(c) ((c) | HASCAPS) /* 添加 "大写生效" 属性 */
|
||||
```
|
||||
|
||||
前三个宏通过位运算生成应用程序所需的编码。最后一个宏则是将高字节的第16位置1来使大写生效。观察扫描码2, 13, 16可以分别了解到数字,标点符号,字母是怎么处理的。扫描码28代表了ENTER键,一般来说ENTER键对应ASCII码的CR字符(0x0d),即`C('M')`的运算结果。然而在UNIX里新行是LF字符(0x0a),所以当前键盘映射提供了CTRL-ENTER组合键,即`C('J')`来对应`0x0a`。
|
||||
|
||||
扫描码29是修饰键CTRL的编码,不管按下什么键都不会对它产生影响。扫描码59对应功能键F1,它在ASCII表里没有对应的值,它所对应的值都定义在`include/minix/keymap.h`里。扫描码127是表里的最后一项,表里的靠后的很多项都和它一样。因为很多键盘类型都无法生成那么多的编码,所以那些项都被填充为0。
|
||||
|
||||
##### 可加载的字体
|
||||
|
||||
早期的PC要在显示器屏幕上生成字符,只能使用存储在ROM上的生成模型。但现代的显示器在显卡上提供了RAM,可用于加载自定义的字符自成模型。Minix3上`ioctl(0, TIOCSFON, font)`可支持此操作。
|
||||
|
||||
Minix3支持80列x25行的视频模式,字体文件包含4096字节。每个字符使用8x16个像素,故描述一个字符需要16个字节。但显卡使用32个字节来映射字符,这样就可以支持更高的分辨率了,但Minix3现在还不支持这种模式。`loadfont`命令把字体文件转化为`font`结构体,并调用`ioctl()`来加载字体。和键盘映射一样,即可以在启动时加载字体,也可以在随后任何时间加载字体。每个显卡都在它的ROM里保存了默认字体随时可用。没有必要把字体编译进Minix3,在内核里只有`TIOCSFON ioctl`支持必要的字体操作。
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
为了支持更复杂的屏幕显示,许多终端驱动程序都支持ANSI转义序列。
|
||||
|
||||
当驱动程序看到ESC字符(0x1B)时,它设置一个标志以接收全部的转义序列,并负责解释转义序列的含义。
|
||||
|
||||
有两种类型的转义序列:不包含可变参数的;可能包含参数的。不包含参数的比如`ESC M`。包含参数的以`ESC[`开头,这个`[`就是**控制序列引导码**(CSI, **control sequence introducer**)。
|
||||
|
||||
| 转义序列 | 含义 |
|
||||
| -------- | ------------------------------------------------------------ |
|
||||
| ESC[nA | 向上移动n行 |
|
||||
| ESC[nB | 向下移动n行 |
|
||||
| ESC[nC | 向右移动n行 |
|
||||
| ESC[nD | 向左移动n行 |
|
||||
| ESC[m;nH | 移动到m行n列 |
|
||||
| ESC[sJ | 清屏。s=0,光标到结尾。s=1,开头到光标。s=2,开头到结尾。 |
|
||||
| ESC[nL | 插入n行 |
|
||||
| ESC[nM | 删除n行 |
|
||||
| ESC[nP | 删除n个字符 |
|
||||
| ESC[n@ | 插入n个字符 |
|
||||
| ESC[nm | 开启字符特效。n为数字,m为字符'm'。n=0正常,n=1加粗,n=4下划线,n=5闪烁,n=7反显。 |
|
||||
| ESC M | 把光标向上移动一行,如果光标在顶行则向下滚动屏幕 |
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
#### fat32的磁盘结构
|
||||
|
||||
FAT文件系统=引导记录(512字节) + 文件分配表 + 根目录 + 数据区
|
||||
|
||||
#### 引导记录
|
||||
|
||||
引导记录=BIOS参数块(0x00-0x23) + 扩展引导记录(0x24-0x1ff)
|
||||
|
||||
FSInfo结构体占用512字节,扇区号在扩展引导记录里指定。
|
||||
|
||||
```rust
|
||||
pub(crate) struct BootSector {
|
||||
bootjmp: [u8; 3], // offset:0x00
|
||||
oem_name: [u8; 8], // offset:0x03
|
||||
pub(crate) bpb: BiosParameterBlock,
|
||||
boot_code: [u8; 448], // offset:0x5a
|
||||
boot_sig: [u8; 2], // offset:0x1fe, 启动分区的签名"0xAA55"
|
||||
}
|
||||
pub(crate) struct BiosParameterBlock {
|
||||
pub(crate) bytes_per_sector: u16, // offset:0x0b
|
||||
pub(crate) sectors_per_cluster: u8, // offset:0x0d
|
||||
pub(crate) reserved_sectors: u16, // offset:0x0e
|
||||
pub(crate) fats: u8, // offset:0x10, 文件分配表的个数,一般是2
|
||||
pub(crate) root_entries: u16, // offset:0x11, 根目录包含多少个条目
|
||||
pub(crate) total_sectors_16: u16, // offset:0x13
|
||||
pub(crate) media: u8, // offset:0x15
|
||||
pub(crate) sectors_per_fat_16: u16, // offset:0x16
|
||||
pub(crate) sectors_per_track: u16, // offset:0x18
|
||||
pub(crate) heads: u16, // offset:0x1a
|
||||
pub(crate) hidden_sectors: u32, // offset:0x1c
|
||||
pub(crate) total_sectors_32: u32, // offset:0x20
|
||||
|
||||
// Extended BIOS Parameter Block
|
||||
pub(crate) sectors_per_fat_32: u32, // offset:0x24
|
||||
pub(crate) extended_flags: u16, // offset:0x28
|
||||
pub(crate) fs_version: u16, // offset:0x2a
|
||||
pub(crate) root_dir_first_cluster: u32, // offset:0x2c, 根目录的簇号
|
||||
pub(crate) fs_info_sector: u16, // offset:0x30, FSInfo的扇区号
|
||||
pub(crate) backup_boot_sector: u16, // offset:0x32
|
||||
pub(crate) reserved_0: [u8; 12], // offset:0x34
|
||||
pub(crate) drive_num: u8, // offset:0x40
|
||||
pub(crate) reserved_1: u8, // offset:0x41
|
||||
pub(crate) ext_sig: u8, // offset:0x42, 只能是0x28或0x29
|
||||
pub(crate) volume_id: u32, // offset:0x43
|
||||
pub(crate) volume_label: [u8; 11], // offset:0x47
|
||||
pub(crate) fs_type_label: [u8; 8], // offset:0x52, 总是"FAT32"
|
||||
}
|
||||
|
||||
struct FsInfoSector {
|
||||
free_cluster_count: Option<u32>, // offset:0x1e8
|
||||
next_free_cluster: Option<u32>, // offset:0x1ec
|
||||
dirty: bool,
|
||||
}
|
||||
impl FsInfoSector {
|
||||
const LEAD_SIG: u32 = 0x4161_5252; // 先导签名的值,offset:0x0
|
||||
const STRUC_SIG: u32 = 0x6141_7272; // 中间签名的值,offset:0x1e4
|
||||
const TRAIL_SIG: u32 = 0xAA55_0000; // 结尾签名的值,offset:0x1fc
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 文件分配表
|
||||
|
||||
文件分配表里的元素为28字节大小,代表了当前文件下一个簇的编号。
|
||||
|
||||
当元素值大于等于0x0ffffff8,代表没有多余的簇了,即整个文件已读取完成。
|
||||
|
||||
当元素值等于0x0ffffff7,代表这个簇是坏的。
|
||||
|
||||
当元素值等于0,代表这个簇是空闲的。所以0号元素是被保留的。
|
||||
|
||||
1号元素是被保留的,留做将来使用。其值必须是0xffffffff。
|
||||
|
||||
#### 根目录
|
||||
|
||||
记录了整个磁盘的结构。是由目录条目组成的数组,数组元素为32字节大小。
|
||||
|
||||
```rust
|
||||
pub(crate) struct DirFileEntryData {
|
||||
name: [u8; SFN_SIZE], // offset:0, SFN_SIZE值为11.
|
||||
attrs: FileAttributes, // offset:11, u8类型。可以决定当前条目是目录还是文件。
|
||||
reserved_0: u8, // offset:12
|
||||
create_time_0: u8, // offset:13
|
||||
create_time_1: u16, // offset:14
|
||||
create_date: u16, // offset:16
|
||||
access_date: u16, // offset:18
|
||||
first_cluster_hi: u16, // offset:20, 当前文件第一个簇的簇号的高16位。
|
||||
modify_time: u16, // offset:22
|
||||
modify_date: u16, // offset:24
|
||||
first_cluster_lo: u16, // offset:26, 当前文件第一个簇的簇号的低16位。
|
||||
size: u32, // offset:28, 当前文件占用了多少字节。
|
||||
}
|
||||
```
|
||||
|
||||
#### rust-fs库
|
||||
|
||||
```rust
|
||||
mod log_macros;
|
||||
|
||||
mod boot_sector;
|
||||
mod dir;
|
||||
mod dir_entry;
|
||||
mod error;
|
||||
mod file;
|
||||
mod fs;
|
||||
mod io;
|
||||
mod table;
|
||||
mod time;
|
||||
|
||||
pub use crate::dir::*;
|
||||
pub use crate::dir_entry::*;
|
||||
pub use crate::error::*;
|
||||
pub use crate::file::*;
|
||||
pub use crate::fs::*;
|
||||
pub use crate::io::*;
|
||||
pub use crate::time::*;
|
||||
```
|
||||
|
||||
##### fs包
|
||||
|
||||
```rust
|
||||
pub struct FileSystem<IO: ReadWriteSeek, TP, OCC> {
|
||||
pub(crate) disk: RefCell<IO>,
|
||||
pub(crate) options: FsOptions<TP, OCC>,
|
||||
fat_type: FatType,
|
||||
bpb: BiosParameterBlock,
|
||||
first_data_sector: u32,
|
||||
root_dir_sectors: u32,
|
||||
total_clusters: u32,
|
||||
fs_info: RefCell<FsInfoSector>,
|
||||
current_status_flags: Cell<FsStatusFlags>,
|
||||
}
|
||||
|
||||
impl<IO: Read + Write + Seek, TP, OCC> FileSystem<IO, TP, OCC> {
|
||||
pub fn new() // 创建一个文件系统的实例
|
||||
...
|
||||
pub fn root_dir() // 返回根目录的对象
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
# SFS文件系统
|
||||
|
||||
## 概览
|
||||
|
||||
| 层级 | 描述 | 代码 |
|
||||
| -------------- | -------------------------------- | -------------------------------------------------------- |
|
||||
| 索引结点层 | 文件控制块数据结构与处理 | `rcore-fs::vfs::FileSystem` |
|
||||
| 磁盘块管理层 | 定义磁盘文件系统的数据结构及处理 | `rcore-fs-sfs::SimpleFileSystem` |
|
||||
| 磁盘数据结构层 | 磁盘上的数据结构及处理 | `rcore-fs-sfs::struct::{SuperBlock,DiskInode,DiskEntry}` |
|
||||
| 块缓冲层 | 在内存中缓存磁盘数据 | rcore-fs::dev::block_cache::{BlockCache,LRU} |
|
||||
| 块设备接口层 | 定义读写磁盘的接口 | `rcore-fs::dev::BlockDevice` |
|
||||
|
||||
制作sfs镜像
|
||||
|
||||
```bash
|
||||
rcore-fs-fuse sfs.img rootfs zip # 从目录rootfs生成镜像sfs.img
|
||||
rcore-fs-fuse dir image git-version # 版本验证,应为7f5eea
|
||||
cargo install rcore-fs-fuse --git https://github.com/rcore-os/rcore-fs --rev 7f5eea --force # 安装版本号为7f5eea的rcore-fs-fuse
|
||||
```
|
||||
|
||||
## 磁盘结构
|
||||
|
||||
| 名称 | 位置 | 大小 |
|
||||
| -------------- | ----- | ------------------------- |
|
||||
| 超级块 | 0号块 | 1个块 |
|
||||
| 根目录索引结点 | 1号块 | 1个块 |
|
||||
| 空闲位图区 | 2号块 | SuperBlock.freemap_blocks |
|
||||
| 索引结点区 | | |
|
||||
| 数据区 | | |
|
||||
|
||||
块大小:4k字节
|
||||
|
||||
inode直接块:12
|
||||
|
||||
# rcore-fs
|
||||
|
||||
zCore使用的文件系统的库是[rcore-fs](https://github.com/rcore-os/rcore-fs)。
|
||||
|
||||
其中,rcore-fs是OS可以用到的接口和实用工具。
|
||||
|
||||
对于VFS,会用到`FileSystem`和`INode`。
|
||||
|
||||
对于设备和缓冲层,会用到`BlockDevice`和`BlockCache`。
|
||||
|
||||
rcore-fs仅仅是对如下模块的包含:
|
||||
|
||||
## dev模块
|
||||
|
||||
设备接口层和块缓冲层实现
|
||||
|
||||
```rust
|
||||
/// FS读、写的接口
|
||||
pub trait Device: Send + Sync {
|
||||
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize>;
|
||||
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize>;
|
||||
fn sync(&self) -> Result<()>;
|
||||
}
|
||||
|
||||
/// 它是与底层驱动程序之间的接口,属于设备接口层。定义了对块设备的操作(读和写)。
|
||||
pub trait BlockDevice: Send + Sync {
|
||||
const BLOCK_SIZE_LOG2: u8;
|
||||
// 把块block_id写到缓冲区buf
|
||||
fn read_at(&self, block_id: BlockId, buf: &mut [u8]) -> Result<()>;
|
||||
// 把缓冲区buf写入到块block_id
|
||||
fn write_at(&self, block_id: BlockId, buf: &[u8]) -> Result<()>;
|
||||
fn sync(&self) -> Result<()>;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### block_cache模块
|
||||
|
||||
块缓冲层实现,采用LRU算法
|
||||
|
||||
```rust
|
||||
/// 定义块缓存
|
||||
pub struct BlockCache<T: BlockDevice> {
|
||||
device: T, // 通过它进行块的读写
|
||||
bufs: Vec<Mutex<Buf>>, // 代表内存中的缓冲区
|
||||
lru: Mutex<LRU>,
|
||||
}
|
||||
/// BlockCache的元数据
|
||||
struct Buf {...}
|
||||
enum BufStatus {...}
|
||||
|
||||
impl<T: BlockDevice> BlockCache<T> {
|
||||
pub fn new() // 创建块缓存,把块上的数据读到缓冲区
|
||||
fn get_buf() // 获取block_id对应的缓冲区地址
|
||||
fn _get_buf()
|
||||
fn get_unused()
|
||||
fn write_back()
|
||||
}
|
||||
|
||||
impl<T: BlockDevice> Drop for BlockCache<T> {
|
||||
fn drop(&mut self) {
|
||||
BlockDevice::sync(self).expect("failed to sync");
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BlockDevice> BlockDevice for BlockCache<T> {
|
||||
fn read_at()
|
||||
fn write_at()
|
||||
fn sync()
|
||||
}
|
||||
|
||||
/// 双向链表的LRU算法实现
|
||||
struct LRU {
|
||||
prev: Vec<usize>,
|
||||
next: Vec<usize>,
|
||||
}
|
||||
impl LRU {
|
||||
fn new()
|
||||
fn visit()
|
||||
fn victim()
|
||||
fn _list_remove()
|
||||
fn _list_insert_head()
|
||||
}
|
||||
```
|
||||
|
||||
### std_impl模块
|
||||
|
||||
|
||||
|
||||
## dirty模块
|
||||
|
||||
用于读写`dirty`标志。
|
||||
|
||||
## file模块
|
||||
|
||||
实现了`File`对象。
|
||||
|
||||
```rust
|
||||
pub struct File {
|
||||
inode: Arc<dyn INode>,
|
||||
offset: usize,
|
||||
readable: bool,
|
||||
writable: bool,
|
||||
}
|
||||
|
||||
impl File {
|
||||
pub fn new()
|
||||
pub fn read()
|
||||
pub fn write()
|
||||
pub fn info()
|
||||
pub fn get_entry()
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## util模块
|
||||
|
||||
## vfs模块
|
||||
|
||||
```rust
|
||||
/// 抽象的文件系统对象,代表了文件或目录
|
||||
pub trait INode {}
|
||||
impl dyn INode {}
|
||||
|
||||
/// INode的元数据
|
||||
pub struct Metadata {...}
|
||||
pub struct Timespec {...}
|
||||
pub enum FileType {...}
|
||||
|
||||
/// FileSystem的元数据
|
||||
pub struct FsInfo {...}
|
||||
pub enum FsError {...}
|
||||
impl fmt::Display for FsError {...}
|
||||
impl From<DevError> for FsError {...}
|
||||
|
||||
/// 抽象的文件系统
|
||||
pub trait FileSystem: Sync + Send {
|
||||
fn sync(&self) -> Result<()>; // 同步数据到存储
|
||||
fn root_inode(&self) -> Arc<dyn INode>; // 获取根索引节点
|
||||
fn info(&self) -> FsInfo; // 获取文件系统的信息
|
||||
}
|
||||
```
|
||||
|
||||
# rcore-fs-sfs库
|
||||
|
||||
```rust
|
||||
trait DeviceExt: Device {...}
|
||||
impl DeviceExt for dyn Device {}
|
||||
|
||||
/// SFS的INode
|
||||
pub struct INodeImpl {...}
|
||||
impl Debug for INodeImpl {...}
|
||||
impl INodeImpl {...}
|
||||
impl vfs::INode for INodeImpl {...}
|
||||
impl Drop for INodeImpl {...}
|
||||
|
||||
/// SFS的文件系统
|
||||
pub struct SimpleFileSystem {...}
|
||||
impl SimpleFileSystem {
|
||||
pub fn open() -> vfs::Result<Arc<Self>> {} // 从设备载入SFS
|
||||
pub fn create
|
||||
fn wrap
|
||||
fn alloc_block
|
||||
fn free_block
|
||||
pub fn new_device_inode
|
||||
fn _new_inode
|
||||
fn get_inode
|
||||
...
|
||||
}
|
||||
impl vfs::FileSystem for SimpleFileSystem {...}
|
||||
impl Drop for SimpleFileSystem {...}
|
||||
|
||||
trait BitsetAlloc {
|
||||
fn alloc(&mut self) -> Option<usize>;
|
||||
}
|
||||
impl BitsetAlloc for BitVec<Lsb0, u8> {...}
|
||||
impl AsBuf for BitVec<Lsb0, u8> {...}
|
||||
impl AsBuf for [u8; BLKSIZE] {}
|
||||
impl From<FileType> for vfs::FileType {...}
|
||||
```
|
||||
|
||||
## strust子模块
|
||||
|
||||
描述磁盘上的结构
|
||||
|
||||
```rust
|
||||
/// 超级块
|
||||
pub struct SuperBlock {
|
||||
pub magic: u32, // offset:0x0,必须是0x2f8dbe2b
|
||||
pub blocks: u32, // offset:0x4
|
||||
pub unused_blocks: u32, // offset:0x8
|
||||
pub info: Str32, // offset:0xc, 占32个字节,默认值"simple file system"
|
||||
pub freemap_blocks: u32, // offset:0x2c
|
||||
}
|
||||
|
||||
/// 磁盘上的索引结点
|
||||
pub struct DiskINode {
|
||||
pub size: u32, // 文件有多少个字节,对目录则无效
|
||||
pub type_: FileType, // 1:文件,2:目录,3:符号链接,4:字符设备,5:块设备
|
||||
pub nlinks: u16, // 硬链接数。包括"."和".."
|
||||
pub blocks: u32, // 文件占多少个块
|
||||
pub direct: [u32; NDIRECT], // 直接块的数组
|
||||
pub indirect: u32, // 间接块的地址
|
||||
pub db_indirect: u32, // 双间接块的地址
|
||||
pub device_inode_id: usize, // 用于设备,记录(主设备号,次设备号)
|
||||
pub atime: Timespec, // 最后访问时间
|
||||
pub mtime: Timespec, // 最后修改时间
|
||||
pub ctime: Timespec, // 最后改变时间
|
||||
}
|
||||
|
||||
pub type DeviceINode = dyn vfs::INode;
|
||||
|
||||
pub struct IndirectBlock {
|
||||
pub entries: [u32; BLK_NENTRY],
|
||||
}
|
||||
|
||||
/// 目录项,即目录文件的内容。一个目录项就代表了一个文件或子目录。
|
||||
pub struct DiskEntry {
|
||||
pub id: u32, // 索引结点号
|
||||
pub name: Str256, // 文件名,占256个字符
|
||||
}
|
||||
|
||||
impl SuperBlock {
|
||||
pub fn check() // 检查魔数是否匹配
|
||||
}
|
||||
|
||||
impl DiskINode {
|
||||
pub const fn new_file()
|
||||
pub const fn new_symlink()
|
||||
pub const fn new_dir()
|
||||
pub const fn new_chardevice()
|
||||
}
|
||||
```
|
||||
|
||||
# rcore-fs-hostfs库
|
||||
|
||||
```rust
|
||||
/// 宿主机上的文件系统
|
||||
pub struct HostFS {
|
||||
path: PathBuf,
|
||||
self_ref: Weak<HostFS>,
|
||||
}
|
||||
|
||||
impl HostFS {
|
||||
pub fn new() -> Arc<HostFS> {} // 创建HostFS
|
||||
fn wrap() -> Arc<Self> {} // new方法用此方法把HostFS打包为Arc
|
||||
}
|
||||
```
|
||||
|
||||
# rcore-fs-sefs
|
||||
|
||||
简单加密文件系统
|
||||
|
||||
# rcore-fs-fuse程序
|
||||
|
||||
main执行过程
|
||||
|
||||
1. `env_log::init()`初始化日志系统。
|
||||
2. 参数如果是zip则将`create`设为`true`表示创建镜像,如果是unzip则将`create`设为`false`表示解压镜像,如果是git-version则打印git commit号。
|
|
@ -0,0 +1,125 @@
|
|||
# zCore::fs子模块
|
||||
|
||||
`zCore/src/fs.rs`为`main()`函数提供了`fs::rootfs()`方法来生成文件系统的对象。在linux_libos模式下是获取HostFS,在linux_bare模式下是从设备载入SFS。
|
||||
|
||||
- linux_libos模式下的rootfs()
|
||||
|
||||
先指定根目录`rootfs`的值,再创建一个HostFS对象。
|
||||
|
||||
- linux_bare模式下的rootfs()
|
||||
|
||||
先指定设备`device`的值,再从设备载入SFS对象。
|
||||
|
||||
- init_ram_disk()函数
|
||||
1. 如果定义了`link-user-img`,则根据内存镜像的指针和大小来生成片断。
|
||||
2. 否则,返回初始化的RAM盘的片断。
|
||||
|
||||
# linux_object::fs子模块
|
||||
|
||||
`linux_object/src/fs`实现了fs模块,是文件系统具体的实现者。
|
||||
|
||||
```rust
|
||||
mod devfs;
|
||||
mod file;
|
||||
mod ioctl;
|
||||
mod pipe;
|
||||
mod pseudo;
|
||||
mod stdio;
|
||||
|
||||
pub trait FileLike: KernelObject {
|
||||
...
|
||||
}
|
||||
|
||||
/// 创建根文件系统,挂载DevFS和RamFS。
|
||||
pub fn create_root_fs() {...}
|
||||
|
||||
/// 把path分隔成(base_path, filename)
|
||||
pub fn split_path(path: &str) -> (&str, &str) {...}
|
||||
```
|
||||
|
||||
## devfs模块
|
||||
|
||||
```rust
|
||||
mod fbdev;
|
||||
mod input;
|
||||
mod random;
|
||||
mod uartdev;
|
||||
|
||||
pub use fbdev::FbDev;
|
||||
pub use input::{EventDev, MiceDev};
|
||||
pub use random::RandomINode;
|
||||
pub use uartdev::UartDev;
|
||||
```
|
||||
|
||||
### input模块
|
||||
|
||||
为MiceDev和EventDev实现INode
|
||||
|
||||
### fbdev模块
|
||||
|
||||
为framebuffer实现INode
|
||||
|
||||
### random模块
|
||||
|
||||
为RandomINode实现INode
|
||||
|
||||
### uartdev模块
|
||||
|
||||
为UartDev实现INode
|
||||
|
||||
## device模块
|
||||
|
||||
看起来没什么用的模块,没有在其它地方被使用。应该是`rcore_fs_wrapper`模块取代了此模块的地位。
|
||||
|
||||
## file模块
|
||||
|
||||
对文件的各种操作。
|
||||
|
||||
## ioctl模块
|
||||
|
||||
定义了IOR和IOW用到的一些常数。
|
||||
|
||||
## pipe模块
|
||||
|
||||
为管道实现INode
|
||||
|
||||
## pseudo模块
|
||||
|
||||
为伪文件系统实现INode
|
||||
|
||||
## stdio模块
|
||||
|
||||
为Stdin和Stdout实现INode
|
||||
|
||||
## rcore_fs_wrapper模块
|
||||
|
||||
是对`rcore_fs::dev::Device`的实现,这样就可以从设备载入文件系统了。
|
||||
|
||||
```rust
|
||||
/// 内存上的缓冲区,这个缓冲区是for device的
|
||||
pub struct MemBuf(RwLock<&'static mut [u8]>);
|
||||
|
||||
impl MemBuf {
|
||||
pub fn new () -> Self {} // 创建一个Membuf结构体
|
||||
}
|
||||
|
||||
impl Device for MemBuf {
|
||||
fn read_at()
|
||||
fn write_at()
|
||||
fn sync()
|
||||
}
|
||||
|
||||
/// 实现了BlockScheme的块设备
|
||||
pub struct Block(Arc<dyn BlockScheme>);
|
||||
|
||||
impl Block {
|
||||
pub fn new() -> Self {} // 创建一个Block结构体
|
||||
}
|
||||
|
||||
impl BlockDevice for Block {
|
||||
fn read_at()
|
||||
fn write_at()
|
||||
fn sync()
|
||||
}
|
||||
```
|
||||
|
|
@ -15,7 +15,7 @@ add <name> <url> # 添加远程仓库,url是远程仓库的地址,name是远程
|
|||
rename <old> <new> # 将远程仓库的名称从old改为new
|
||||
remove <name> # 删除对远程仓库name的追踪
|
||||
set-head <name> # 为远程仓库设置或删除默认分支
|
||||
set-branches <name> <branch> # 改变远程仓库的分支列表
|
||||
set-branches [--add] <name> <branchs> # 改变远程仓库<name>所要追踪的分支列表。用于在初始化远程仓库之后追踪指定的分支。
|
||||
show <name> # 显示远程仓库name与本地仓库之间分支的对应关系
|
||||
get-url <name> # 检索远程分支的url
|
||||
set-url <name> <newurl> # 改变远程分支的url
|
||||
|
@ -38,5 +38,8 @@ git push
|
|||
git remote add myrepo https://gitee.com/myname/newrepo.git
|
||||
git fetch myrepo
|
||||
git push myrepo local-branch
|
||||
|
||||
# 仅追踪远程仓库abc的master分支
|
||||
git remote set-branches abc master
|
||||
```
|
||||
|
||||
|
|
|
@ -23,5 +23,7 @@ git reset <mode> [<commit>] # 将当前分支的head指向commit
|
|||
```
|
||||
git reset HEAD <file> # 撤销对暂存区的修改
|
||||
git reset HEAD^ # 撤销最近一次提交
|
||||
git reset HEAD^^^ # 撤销最近三次提交
|
||||
git reset HEAD~3 # 撤销最近三次提交
|
||||
```
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
#### 简介
|
||||
|
||||
子模块就是将一个仓库作为另一个仓库的子目录
|
||||
|
||||
```
|
||||
git submodule <command> [options] # 初始化,更新或分析子模块
|
||||
```
|
||||
|
@ -63,14 +67,19 @@ update [options] [--] [<path>...]
|
|||
可以查看.gitmodules文件查看文件夹与子模块的对应关系
|
||||
|
||||
```shell
|
||||
# 子模块就是将一个仓库作为另一个仓库的子目录
|
||||
git submodule add <repo> <path> # 添加子模块
|
||||
# 添加子模块
|
||||
git submodule add <repo> [path]
|
||||
|
||||
# 克隆包含子模块的项目,默认包含子模块的目录,但目录内没有任何文件,需要如下命令使子模块充实起来
|
||||
git submodule init # 初始化子模块
|
||||
git submodule update # 更新子模块
|
||||
# --init --recursive = "git submodule init" + "git submodule update --recursive"
|
||||
|
||||
# 删除子模块
|
||||
git submodule deinit [path]
|
||||
git rm --cached [path]
|
||||
vim .gitmodules # 删除[path]对应的条目
|
||||
rm -r .git/modules/[path] # 删除脏文件
|
||||
```
|
||||
|
||||
#### 错误解决
|
||||
|
@ -91,4 +100,11 @@ git submodule update # 更新子模块
|
|||
cd .. && git add <submodule> && git commit -m ""
|
||||
```
|
||||
|
||||
3. fatal: No url found for submodule path 'xxx' in .gitmodules
|
||||
|
||||
```
|
||||
# 可能是仓库的原主人需要子模块xxx,但我不需要它,我只要把它删除即可
|
||||
git rm --cached xxx
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ git [optons] <command> [args]
|
|||
#### 选项
|
||||
|
||||
```
|
||||
|
||||
-C <path> # 指定git命令的执行目录。默认为当前目录。
|
||||
```
|
||||
|
||||
#### 高层命令(porcelain)
|
||||
|
@ -195,4 +195,4 @@ blob对象是通过**tree对象**组织在一起的。一个tree对象由一条
|
|||
|
||||
一个**reference**是对一个commit取了个别名,方便记忆。HEAD文件保存了当前分支的reference,这样在分支切换的时候就可以指向最后一次提交的commit ID。
|
||||
|
||||
一个**tag对象**是对一个commit的引用。共有两种tag:lightweight和annotated。lightweight tag仅仅是对一个commit的引用。annotated tag则会创建一个tag对象,此tag对象指向commit。
|
||||
一个**tag对象**是对一个commit的引用。共有两种tag:lightweight和annotated。lightweight tag仅仅是对一个commit的引用。annotated tag则会创建一个tag对象,此tag对象指向commit。
|
||||
|
|
|
@ -33,13 +33,19 @@ close <name> # 删除映射的name,并消除内核空间里的key。
|
|||
|
||||
##### resize
|
||||
|
||||
```
|
||||
resize [options] <name> # 修改<name>的大小,其中<name>是一个活动的映射。
|
||||
```
|
||||
|
||||
|
||||
|
||||
##### refresh
|
||||
|
||||
##### reencrypt
|
||||
|
||||
#### PLAIN
|
||||
#### PLAIN模式
|
||||
|
||||
#### LUKS
|
||||
#### LUKS扩展
|
||||
|
||||
##### luksFormat
|
||||
|
||||
|
@ -56,13 +62,13 @@ luksOpen <dev> <name> # 旧语法
|
|||
|
||||
|
||||
|
||||
#### loop-AES
|
||||
#### loop-AES扩展
|
||||
|
||||
#### TCRYPT
|
||||
#### TCRYPT扩展
|
||||
|
||||
#### BITLK
|
||||
#### BITLK扩展
|
||||
|
||||
#### 杂项
|
||||
#### 其它动作
|
||||
|
||||
#### 选项
|
||||
|
||||
|
|
|
@ -7,6 +7,11 @@ gzip [options] [names] # 压缩或解压
|
|||
#### 选项
|
||||
|
||||
```
|
||||
-[num], --fast, --best # 用[num]控制压缩速度。-1相当于--fast,代表最快但最弱的压缩。-9相当于--best,代表最慢但最强的压缩。[num]默认为6。
|
||||
-c, --stdout, --to-stdout # 输出到标准输出,这样就可以保持原文件不变。
|
||||
-d, --decompress, --uncompress # 解压
|
||||
-f, --force # 强制压缩或解压缩,不管文件是否有多个链接,不管相关文件是不存在,不管压缩数据是读取自还是写入到终端。
|
||||
-q, --quiet # 抑制所有的警告。
|
||||
-v, --verbose # 详细模式。
|
||||
```
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ mv [option]... -t DIRECTORY SOURCE... # 将SOURCES移动到DIRECTORY
|
|||
#### 选项
|
||||
|
||||
```
|
||||
--backup
|
||||
--backup[=CONTROL] # 如果目标文件已存在,则生成备份文件
|
||||
-b # 类似于--backup,但不接收参数
|
||||
-f # 在覆盖前不提示
|
||||
-i # 在覆盖前提示
|
||||
|
@ -29,3 +29,6 @@ mv [option]... -t DIRECTORY SOURCE... # 将SOURCES移动到DIRECTORY
|
|||
--version
|
||||
```
|
||||
|
||||
#### 注意
|
||||
|
||||
1. `mv`命令默认不移动隐藏文件或目录,除非显式地指定它。
|
||||
|
|
|
@ -35,6 +35,10 @@ sudo apt install ttf-mscorefonts-installer
|
|||
sudo cp -r fonts_dir /usr/share/fonts # 直接把字体文件夹复制到对应目录下即可
|
||||
```
|
||||
|
||||
#### 审阅
|
||||
|
||||
review -> track changes -> track changes开启审阅。
|
||||
|
||||
#### 问题解决
|
||||
|
||||
1. 无法输入中文
|
||||
|
|
|
@ -50,26 +50,30 @@ qemu-system-riscv64 [options] [disk_image]
|
|||
# 使用"-device help"获取可用的设备名。使用"-device <drive>,help"获取某个设备的可用属性。
|
||||
# 可用的prop有:
|
||||
## Controller/Bridge/Hub设备
|
||||
# usb-host:bus usb-bus
|
||||
usb-host:bus usb-bus
|
||||
## USB设备
|
||||
# qemu-xhci:bus PCI
|
||||
qemu-xhci:bus PCI
|
||||
## 存储设备
|
||||
# virtio-9p-pci:bus PCI, "virtio-9p"的别名
|
||||
# virtio-blk-pic:bus PCI, 是"virtio-blk"的别名
|
||||
virtio-9p-pci:bus PCI, "virtio-9p"的别名
|
||||
virtio-blk-pic:bus PCI, 是"virtio-blk"的别名
|
||||
## 网络设备
|
||||
# e1000 : bus PCI,"e1000-82540em"的别名,desc "Intel Gigabit Ethernet"
|
||||
e1000 : bus PCI,"e1000-82540em"的别名,desc "Intel Gigabit Ethernet"
|
||||
## 输入设备
|
||||
# isa-serial:bus ISA
|
||||
# usb-kbd:bus usb-bus
|
||||
# usb-mouse:bus usb-bus
|
||||
isa-serial:bus ISA
|
||||
usb-kbd:bus usb-bus
|
||||
usb-mouse:bus usb-bus
|
||||
## 显示设备
|
||||
## 声音设备
|
||||
## Misc设备
|
||||
# ich9-intel-hda:bus PCI, desc "Intel HD Audio Controller (ich9)
|
||||
# intel-hda:bus PCI, desc "Intel HD Audio Controller (ich6)"
|
||||
# intel-iommu : bus系统,desc "Intel IOMMU (VT-d) DMA Remapping device"
|
||||
# vhost-vsock-pci : bus PCI
|
||||
ich9-intel-hda:bus PCI, desc "Intel HD Audio Controller (ich9)
|
||||
intel-hda:bus PCI, desc "Intel HD Audio Controller (ich6)"
|
||||
intel-iommu : bus系统,desc "Intel IOMMU (VT-d) DMA Remapping device"
|
||||
loader : desc"通用loader"
|
||||
# file=<str> : 需指定<str>,代表要装载的文件
|
||||
# addr=<uint64> : 默认值0, 代表要装载的地址
|
||||
vhost-vsock-pci : bus PCI
|
||||
## CPU设备
|
||||
## 看门狗设备
|
||||
## 未分类的设备
|
||||
-global driver.property=value
|
||||
-global driver=driver, property=property, value=value
|
||||
|
@ -554,3 +558,15 @@ other:包括VMDK, VDI, VHD (vpc), VHDX, qcow1 and QED
|
|||
解决方法:使用`strace`命令观察`qemu-system-*`执行过程的输出,发现打开了一个非系统的`libusb.so`库,删除那个库所属的应用,再次安装qemu,问题解决
|
||||
|
||||
原因分析:使用了不正确的`libusb.so`库
|
||||
|
||||
##### 问题三
|
||||
|
||||
问题描述:`-kernel`、` -device loader`、` -bios`的区别。来源:[stackoverflow](https://stackoverflow.com/questions/58420670/qemu-bios-vs-kernel-vs-device-loader-file)
|
||||
|
||||
解答:
|
||||
|
||||
`-kernel`的意思是装载Linux内核。它会为当前架构使用最佳的装载和启动方式。对于x86架构,它只是把文件提供给BIOS,由BIOS做实际的装载工作。对于ARM,怎么加载内核取决于内核启动规则。
|
||||
|
||||
`-device loader`是一个“通用loader”,它在所有架构上都是相同的行为。它仅仅是把ELF镜像加载进内存。如果一个镜像支持裸机启动,应该使用此选项。
|
||||
|
||||
`-bios`的意思是装载BIOS镜像,依据机器模型和架构而不同。x86架构必须要加载BIOS,在用户没有指定的情况下会加载默认的二进制。如果是arm virt开发板,用户指定此选项才加载BIOS,否则不加载。注意,BIOS镜像不应该是ELF文件,而应该是原始的二进制文件,因为它们只是放进ROM或闪存中的原始数据,是硬件最初启动时的那段代码。还可以采用更正规的方式来指定BIOS镜像,比如`-drive if=pflash...`这样。
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
```bash
|
||||
ulimit [options] [limit] # 提供对shell及其子进程可用资源的控制
|
||||
# 选项
|
||||
-S # “软件”资源的限制
|
||||
-H # “硬件”资源的限制
|
||||
-a # 报告所有的限制
|
||||
```
|
||||
|
|
@ -11,3 +11,13 @@ pftp [options] [host [port]]
|
|||
open <host> [port] # 连接到ftp服务器
|
||||
```
|
||||
|
||||
#### 安装tftp服务进程
|
||||
|
||||
```
|
||||
# 注:tftp是Trivial File Transfer Protocol(简单的文件传输协议)
|
||||
# for debian
|
||||
sudo ufw allow tftp # 打开tftp端口
|
||||
sudo apt install tftpd-hpa # 安装tftp服务进程
|
||||
# 配置文件在/etc/default/tftpd-hpa
|
||||
```
|
||||
|
||||
|
|
|
@ -36,4 +36,9 @@ mkimage [options] [image-name] # 创建传统格式的镜像
|
|||
|
||||
|
||||
|
||||
##### 创建FIT镜像
|
||||
##### 创建FIT镜像
|
||||
|
||||
```
|
||||
-f [source-file | auto] # [source-file]描述了FIT镜像的结构和内容。对于一些简单的情况可以使用[auto]来自动生成,这相当于使用了-d, -A, -O, -T, -C, -a, -e, 且不需要指定 .its 文件。
|
||||
```
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
从文件名中删除目录和后缀。会删除名称中的所有先导目录。如指定的话,也可删除后面的后缀。
|
||||
|
||||
注:如果要留下的是目录则使用`dirname`命令。
|
||||
|
||||
#### 语法
|
||||
|
||||
```
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
```
|
||||
dirname [option] <names> # 给出name所在的目录。如name不包含'/',则输出'.'以表明在当前目录。
|
||||
# 注:如果要的是文件名则使用basename命令。
|
||||
```
|
||||
|
||||
选项
|
|
@ -0,0 +1,7 @@
|
|||
#### 快捷键
|
||||
|
||||
- Ctrl + Alt + 方向 : 切换窗口
|
||||
- Ctrl + Alt + Home/End :把应用程序移动到其它窗口
|
||||
- Print : 打开截图软件
|
||||
- Shift + Print : 选定区域截图
|
||||
- Alt + Print : 对活跃窗口截图
|
|
@ -185,13 +185,62 @@ update [options] # 把Cargo.lock里的依赖更新到最新的版本。Cargo.loc
|
|||
|
||||
#### 包命令
|
||||
|
||||
##### search
|
||||
|
||||
```
|
||||
init [options] [path] # 创建Cargo包
|
||||
install # 构建和安装一个rust库。
|
||||
new # 创建一个cargo包
|
||||
search [options] [query...] # 从crates.io查找包
|
||||
```
|
||||
|
||||
##### new
|
||||
|
||||
```
|
||||
new [options] <path> # 创建一个cargo包
|
||||
--bin # 默认选项。包里会有src/main.rs(二进制目标)。
|
||||
--lib # 包里会有src/lib.rs(库目标)。
|
||||
search [options] [query...] # 从crates.io查找包
|
||||
```
|
||||
|
||||
##### init
|
||||
|
||||
```
|
||||
init [options] [path] # 基于已有目录创建Cargo包
|
||||
# 初始化选项
|
||||
--bin # 创建带有二进制目标(src/main.rs)的包。这是默认行为。
|
||||
--lib # 创建带有库目标(src/lib.rs)的包。
|
||||
--edition <edition> # 定义要使用的Rust版本。默认2021。
|
||||
--name <name> # 设置包名。默认为目录名。
|
||||
--vcs <vcs> #
|
||||
--registry <reg> #
|
||||
# 显示选项
|
||||
-v
|
||||
-q
|
||||
--color <when>
|
||||
# 通用选项
|
||||
+toolchain
|
||||
--config <KEY=VALUE>
|
||||
-h
|
||||
-Z <flag>
|
||||
```
|
||||
|
||||
##### install
|
||||
|
||||
构建和安装一个rust二进制文件。只有包含**bin**和**example**对象的包才能被安装,所有的可执行文件都被安装在`bin`目录。
|
||||
|
||||
```
|
||||
install [options] <crate>[@version] # 从crate.io安装
|
||||
install [options] --path <path> # 从本地路径安装
|
||||
install [options] --git <url> [crate] # 从git仓库安装
|
||||
install [options] --list
|
||||
# 安装选项
|
||||
--git <url> # 从<url>来安装指定的库。
|
||||
--rev <sha> # 从git仓库安装的时候指定commit号
|
||||
--list # 列出所有已安装的包和它们的版本号
|
||||
-f, --force # 如果库或二进制文件已存在,则覆盖它们。
|
||||
# feature选项
|
||||
# 编译选项
|
||||
# 清单选项
|
||||
# 其它选项
|
||||
# 显示选项
|
||||
# 普通选项
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ gcc一般会进行预处理,编译,汇编,连接四步。
|
|||
|
||||
```
|
||||
-c # 不运行连接器,这样就只生成汇编器生成的目标文件。
|
||||
-pipe # 各编译阶段之间使用管道通信,而不是使用临时文件通信。
|
||||
-S|-E #
|
||||
-specs=<file> # 编译器读取标准specs文件后再处理<file>,用来覆盖传给cc1,cc1plus,as,ld等的默认开关。
|
||||
-v # 打印各个执行阶段所运行的命令。
|
||||
|
@ -16,8 +17,11 @@ gcc一般会进行预处理,编译,汇编,连接四步。
|
|||
##### C语言选项
|
||||
|
||||
```
|
||||
-ansi # C模式下,等价于-std=c90。C++模式下,等价于-std=c++98。
|
||||
-fno-buitin # 不使用C语言的内建函数
|
||||
-std=<standard> #
|
||||
-std=<standard> # 定义语言的标准。
|
||||
# c90 - ISO C90标准。
|
||||
# c99 - ISO C99标准。
|
||||
```
|
||||
|
||||
##### C++语言选项
|
||||
|
@ -38,6 +42,16 @@ gcc一般会进行预处理,编译,汇编,连接四步。
|
|||
-w # 不生成警告信息
|
||||
-Wall # 生成所有警告信息
|
||||
-Werror # 让所有的警告都变成错误
|
||||
-Werror=<switch> # 让<switch>代表的警告变成错误。
|
||||
# implicit-function-declaration
|
||||
# implicit-int
|
||||
# pointer-sign # 使用不同的签名对指针参数进行传递或赋值
|
||||
# pointer-arith # 关于函数类型的大小或"void"的计算
|
||||
-Wno-missing-braces
|
||||
-Wno-overflow # 当常量表达式在编译时溢出,不进行警告。
|
||||
-Wno-unknown-pragmas
|
||||
-Wno-unused # 对于所有unused-*选项,都不进行警告。
|
||||
-Wno-unused-function
|
||||
-Wunused-function #
|
||||
-Wunused-label # 当一个标签只声明而不使用时进行警告
|
||||
-Wunused-parameter # 当函数参数未使用时进行警告
|
||||
|
@ -61,9 +75,11 @@ gcc一般会进行预处理,编译,汇编,连接四步。
|
|||
-ffuction-sections
|
||||
-fdata-sections
|
||||
-fomit-frame-pointer # 在不需要frame指针的函数中忽略frame指针。
|
||||
-fno-builtin # 不以__builtin_为前缀的函数不被认为是内置函数(built-in function)
|
||||
-fno-omit-frame-pointer # 看起来与-fomit-frame-pointer相反。
|
||||
-foptimize-sibling-calls # 优化同级和尾部递归调用。
|
||||
-fno-optimize-sibling-calls # 看起来与-foptimize-sibling-calls相反。
|
||||
-frounding-math # 对于默认的浮点舍入行为,禁止转换和优化。
|
||||
-fno-stack-protector # 禁用堆栈保护
|
||||
-O0 # 不进行优化
|
||||
-O1或-O # 缺省优化
|
||||
|
@ -97,6 +113,7 @@ gcc一般会进行预处理,编译,汇编,连接四步。
|
|||
-pie # 产生一个动态链接的位置无关可执行文件。为获得一个可预测的结果,还需要指定编译时对应的选项(-fpic, -fPIE, 或模型子选项)。
|
||||
-no-pie # 不要产生动态链接的位置无关可执行文件。
|
||||
-l # 指定要链接的库。默认为系统的库的路径。可查看/etc/ld.so.conf文件获取系统库路径的详情。
|
||||
-s # 从可执行文件里删除所有的符号表和重定位(relocation)信息。
|
||||
-static # 覆盖-pie选项,且不链接到共享库。
|
||||
-T # 指定链接脚本。在没有操作系统的裸板上,可能需要-T选项来避免对未定义符号的引用。可以在搜索“lds链接脚本”关键字得到更详细信息。
|
||||
-Wl,option # 把option作为选项传递给链接器。如果option里包含逗号,传递给链接器的时候逗号会被替换为空格,这个语法的好处是:可以传递多个选项过去,或者给选项指定一个参数。
|
||||
|
|
|
@ -70,3 +70,7 @@ elf64lriscv elf64lriscv_lp64f elf64lriscv_lp64 elf32lriscv elf32lriscv_ilp32f el
|
|||
描述:/usr/bin/ld: cannot find -lxxxxx
|
||||
|
||||
原因分析:1,系统内是否有这个库?`apt search libxxxxx-dev`;2,gcc能否搜索到这个库?`gcc -lxxxxx --verbose`;3,gcc的搜索路径是否包含了该库文件?查看`/etc/ld.so.conf`文件或`LD_LIBRARY_PATH`环境变量。若修改`LD_LIBRARY_PATH`环境变量无效,还需要修改`LIBRARY_PATH`环境变量。
|
||||
|
||||
##### 动态分配只读段
|
||||
|
||||
描述:read-only segment has dynamic relocations
|
||||
|
|
|
@ -70,3 +70,11 @@ make SHELL="/bin/bash -x" # 要了解详细的编译过程,但"make -n"又失
|
|||
1. 不管执行什么命令都提示:No such file or directory.
|
||||
|
||||
原因分析:因为我在Makefile里错误地设置了PATH环境变量,导致make把那些命令都理解成了当前目录下的文件。
|
||||
|
||||
2. 描述:recipe commences before first target.
|
||||
|
||||
原因分析:第一个以冒号结尾的字符串前面有空白字符,make命令把这个字符串识别为target了。
|
||||
|
||||
3. 描述:使用`ifeq`后提示`*** **missing separator**. Stop.`
|
||||
|
||||
原因分析:形如`ifeq(...)`是不行的,在`ifeq`和`(`之间需要有个空格。
|
||||
|
|
|
@ -129,8 +129,3 @@ rustup component add llvm-tools-preview
|
|||
2 crates.io的索引无法更新
|
||||
|
||||
解决方法:参考cargo的"换源"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
debian里没有`cdrecord`命令,应使用`xorrecord`命令。
|
|
@ -1 +0,0 @@
|
|||
在Debian软件包里没有此应用,应使用`xorrisofs`命令。
|
|
@ -2,9 +2,19 @@
|
|||
|
||||
在Linux下创建MS-DOS文件系统
|
||||
|
||||
```
|
||||
mkfs.fat [options] <dev> [block-count]
|
||||
# <dev> : 设备文件或镜像文件。当使用-C选项时镜像文件可以不存在。
|
||||
# [block-count] : 设备上块的数量。每个块固定为1024字节,与扇区大小或簇(cluster)大小无关。当使用-C选项后必须指定此参数。
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 选项
|
||||
|
||||
```bash
|
||||
-C # 创建<dev>对应的镜像文件,这样就不需要再用dd命令来创建文件了。使用此选项后必须指定[block-count]。
|
||||
-F <FAT-SIZE> # 指定文件分配表的类型(12, 16或32位)。
|
||||
-n <volume-name> # 指定卷的名称。
|
||||
```
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
在Debian软件包里没有此应用`mkisofs`,应使用`xorrisofs`命令。
|
|
@ -0,0 +1,30 @@
|
|||
```
|
||||
xorrecord [options] dev=<device> [track_source] # 向CD, DVD, BD写入格式化的数据。
|
||||
```
|
||||
|
||||
选项
|
||||
|
||||
```
|
||||
# 定位设备
|
||||
dev=<device> # 设置要使用设备的libburn地址。比如dev=/dev/sr0。
|
||||
# 查询设备
|
||||
# 烧写相关的设置
|
||||
blank=<mode> # 让CD-RW或DVD-RW变为空的,这样就能重新使用它了。
|
||||
-eject # 工作完成后弹出驱动器托盘。
|
||||
fs=<size> # 设置fifo缓冲区的大小。默认为4m。
|
||||
padsize=size #
|
||||
speed=<value> # 设置写的速度。
|
||||
# 其它设置
|
||||
-v # 详细模式。
|
||||
# 与cdrecord不兼容的设置
|
||||
```
|
||||
|
||||
示例
|
||||
|
||||
```
|
||||
# 浏览设备
|
||||
xorrecord --devices
|
||||
# 写入单个iso镜像
|
||||
xorrecord -v dev=/dev/sr0 speed=12 fs=8m blank=as_needed -eject padsize=300k my_image.iso
|
||||
```
|
||||
|
|
@ -24,7 +24,7 @@ xorrisofs [options] [-o filename] <pathspecs>
|
|||
##### 标准的扩展
|
||||
|
||||
```
|
||||
-J, --joliet # 除了Rock Ridge目录树,再添加Joliet目录树。
|
||||
-J, --joliet # 除了Rock Ridge目录树,再添加Joliet目录树。为了与Windows兼容。
|
||||
-r, --rational-rock # 类似于-R。不同之处是:它不保留文件的属主信息,而是把用户id和组id都置为0,并且文件的访问权限是只读。
|
||||
-R, --rock # 开启Rock Ridge扩展。对于xorrisofs开说是默认开启的。
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue