这是 os summer of code 2020 项目每日记录的一部分:
每日记录github地址(包含根据实验指导实现的每个阶段的代码):https://github.com/yunwei37/os-summer-of-code-daily

这里参考的是rCore tutorial的第三版:https://github.com/rcore-os/rCore-Tutorial

rCore tutorial environment && lab 0

环境配置

本机环境(新安装的虚拟机系统,上一个系统是搞xv6的qemu,感觉可能实验环境会有冲突不如再开一个):

Linux ubuntu 5.4.0-26-generic #30-Ubuntu SMP Mon Apr 20 16:58:30 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

已换源至阿里云,但版本过低;

安装 QEMU

  • sudo apt-get install build-essential 安装编译工具链;
  • 下载源码编译:
wget https://download.qemu.org/qemu-4.2.0.tar.xz
tar xvJf qemu-4.2.0.tar.xz
cd qemu-4.2.0
./configure --target-list=riscv32-softmmu,riscv64-softmmu
  • 报错ERROR: glib-2.48 gthread-2.0 is required to compile QEMU:通过 sudo apt-get install libglib2.0-dev 安装;
  • 报错 ERROR: pixman >= 0.21.8 not present ,通过 sudo apt-get install libpixman-1-dev 安装
  • 测试
yunwei@ubuntu:~/rcore$ qemu-system-riscv64 --version
QEMU emulator version 4.2.0
Copyright (c) 2003-2019 Fabrice Bellard and the QEMU Project developers

安装 Rust 工具链

  • 运行curl https://sh.rustup.rs -sSf | sh

尝试运行 rCore

  • git clone https://github.com/rcore-os/rCore-Tutorial
  • make
Failed to execute tool: objcopy
No such file or directory (os error 2)
make[1]: *** [Makefile:28: target/riscv64imac-unknown-none-elf/debug/kernel.bin] Error 101

可能是我先做了下面导致的问题,之后再说;

lab 0: 跑起来

创建 Rust 项目

创建文件夹,并创建项目:

mkdir Project
cd Project
echo "nightly-2020-06-27" > rust-toolchain
cargo new os
cd os
  • Test:cargo run

移除标准库依赖:

main.rs

#![no_std]use core::panic::PanicInfo;/// 当 panic 发生时会调用该函数
/// 我们暂时将它的实现为一个死循环
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {loop {}
}

Cargo.toml

[package]
name = "os"
version = "0.1.0"
authors = ["yunwei"]
edition = "2018"# panic 时直接终止,因为我们没有实现堆栈展开的功能
[profile.dev]
panic = "abort"[profile.release]
panic = "abort"# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]

移除运行时环境依赖

main.rs

#![no_std]use core::panic::PanicInfo;/// 当 panic 发生时会调用该函数
/// 我们暂时将它的实现为一个死循环
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {loop {}
}
  • 运行:rustup target add riscv64imac-unknown-none-elf
  • 运行:cargo build --target riscv64imac-unknown-none-elf

结果:

yunwei@ubuntu:~/Project/os$ cargo build --target riscv64imac-unknown-none-elfCompiling os v0.1.0 (/home/yunwei/Project/os)Finished dev [unoptimized + debuginfo] target(s) in 0.06s

在 os/.cargo/config 里面添加了这个:

# 编译的目标平台
[build]
target = "riscv64imac-unknown-none-elf"

就可以 cargo build 啦

生成内核镜像

运行:

cargo install cargo-binutils
rustup component add llvm-tools-preview
  • rust-objdump --version
@ubuntu:~/Project/os$ rust-objdump --version
LLVM (http://llvm.org/):LLVM version 10.0.1-rust-1.46.0-nightlyOptimized build.Default target: x86_64-unknown-linux-gnuHost CPU: skylakeRegistered Targets:aarch64    - AArch64 (little endian)aarch64_32 - AArch64 (little endian ILP32)aarch64_be - AArch64 (big endian)arm        - ARMarm64      - ARM64 (little endian)arm64_32   - ARM64 (little endian ILP32)armeb      - ARM (big endian)avr        - Atmel AVR Microcontrollerhexagon    - Hexagonmips       - MIPS (32-bit big endian)mips64     - MIPS (64-bit big endian)mips64el   - MIPS (64-bit little endian)mipsel     - MIPS (32-bit little endian)msp430     - MSP430 [experimental]nvptx      - NVIDIA PTX 32-bitnvptx64    - NVIDIA PTX 64-bitppc32      - PowerPC 32ppc64      - PowerPC 64ppc64le    - PowerPC 64 LEriscv32    - 32-bit RISC-Vriscv64    - 64-bit RISC-Vsparc      - Sparcsparcel    - Sparc LEsparcv9    - Sparc V9systemz    - SystemZthumb      - Thumbthumbeb    - Thumb (big endian)wasm32     - WebAssembly 32-bitwasm64     - WebAssembly 64-bitx86        - 32-bit X86: Pentium-Pro and abovex86-64     - 64-bit X86: EM64T and AMD64

运行:

target/riscv64imac-unknown-none-elf/debug/os: ELF 64-bit LSB executable, UCB RISC-V, version 1 (SYSV), statically linked, with debug_info, not stripped

运行:(和教程里面并不完全一样)

yunwei@ubuntu:~/Project/os$ rust-objdump target/riscv64imac-unknown-none-elf/debug/os -x --arch-name=riscv64target/riscv64imac-unknown-none-elf/debug/os:  file format ELF64-riscvarchitecture: riscv64
start address: 0x0000000000011120Program Header:PHDR off    0x0000000000000040 vaddr 0x0000000000010040 paddr 0x0000000000010040 align 2**3filesz 0x00000000000000e0 memsz 0x00000000000000e0 flags r--LOAD off    0x0000000000000000 vaddr 0x0000000000010000 paddr 0x0000000000010000 align 2**12filesz 0x0000000000000120 memsz 0x0000000000000120 flags r--LOAD off    0x0000000000000120 vaddr 0x0000000000011120 paddr 0x0000000000011120 align 2**12filesz 0x0000000000000004 memsz 0x0000000000000004 flags r-xSTACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**64filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-Dynamic Section:
Sections:
Idx Name            Size     VMA              Type0                 00000000 0000000000000000 1 .text           00000004 0000000000011120 TEXT2 .debug_str      000003fd 0000000000000000 3 .debug_abbrev   00000113 0000000000000000 4 .debug_info     0000053c 0000000000000000 5 .debug_aranges  00000040 0000000000000000 6 .debug_ranges   00000030 0000000000000000 7 .debug_pubnames 000000a4 0000000000000000 8 .debug_pubtypes 00000308 0000000000000000 9 .debug_frame    00000050 0000000000000000 10 .debug_line     0000005b 0000000000000000 11 .comment        00000013 0000000000000000 12 .symtab         00000108 0000000000000000 13 .shstrtab       000000a5 0000000000000000 14 .strtab         0000002d 0000000000000000 SYMBOL TABLE:
0000000000000000 l    df *ABS*  00000000 3gqd1qcioyc9uzqc
0000000000011120         .text  00000000
0000000000011120         .text  00000000
0000000000011120         .text  00000000
0000000000011124         .text  00000000
0000000000000000         .debug_info    00000000
0000000000000000         .debug_ranges  00000000
0000000000000000         .debug_frame   00000000
0000000000000000         .debug_line    00000000 .Lline_table_start0
0000000000011120 g     F .text  00000004 _start

反汇编:

yunwei@ubuntu:~/Project/os$ rust-objdump target/riscv64imac-unknown-none-elf/debug/os -d --arch-name=riscv64target/riscv64imac-unknown-none-elf/debug/os:  file format ELF64-riscvDisassembly of section .text:0000000000011120 _start:11120: 09 a0                            j   211122: 01 a0                           j   0

生成镜像:

rust-objcopy target/riscv64imac-unknown-none-elf/debug/os --strip-all -O binary target/riscv64imac-unknown-none-elf/debug/kernel.bin

调整内存布局

可以参考:https://sourceware.org/binutils/docs/ld/Scripts.html 但还没去看

编写链接脚本:

/* 有关 Linker Script 可以参考:https://sourceware.org/binutils/docs/ld/Scripts.html *//* 目标架构 */
OUTPUT_ARCH(riscv)/* 执行入口 */
ENTRY(_start)/* 数据存放起始地址 */
BASE_ADDRESS = 0x80200000;SECTIONS
{/* . 表示当前地址(location counter) */. = BASE_ADDRESS;/* start 符号表示全部的开始位置 */kernel_start = .;text_start = .;/* .text 字段 */.text : {/* 把 entry 函数放在最前面 */*(.text.entry)/* 要链接的文件的 .text 字段集中放在这里 */*(.text .text.*)}rodata_start = .;/* .rodata 字段 */.rodata : {/* 要链接的文件的 .rodata 字段集中放在这里 */*(.rodata .rodata.*)}data_start = .;/* .data 字段 */.data : {/* 要链接的文件的 .data 字段集中放在这里 */*(.data .data.*)}bss_start = .;/* .bss 字段 */.bss : {/* 要链接的文件的 .bss 字段集中放在这里 */*(.sbss .bss .bss.*)}/* 结束地址 */kernel_end = .;
}

os/.cargo/config里面添加:

# 使用我们的 linker script 来进行链接
[target.riscv64imac-unknown-none-elf]
rustflags = ["-C", "link-arg=-Tsrc/linker.ld",
]
  • cargo build
  • rust-objdump target/riscv64imac-unknown-none-elf/debug/os -h --arch-name=riscv64

输出:

Idx Name            Size     VMA              Type0                 00000000 0000000000000000 1 .text           00000004 0000000080200000 TEXT2 .debug_str      000003fd 0000000000000000 3 .debug_abbrev   00000113 0000000000000000 4 .debug_info     0000053c 0000000000000000 5 .debug_aranges  00000040 0000000000000000 6 .debug_ranges   00000030 0000000000000000 7 .debug_pubnames 000000a4 0000000000000000 8 .debug_pubtypes 00000308 0000000000000000 9 .debug_frame    00000050 0000000000000000 10 .debug_line     0000005b 0000000000000000 11 .comment        00000013 0000000000000000 12 .symtab         000001b0 0000000000000000 13 .shstrtab       000000a5 0000000000000000 14 .strtab         0000007f 0000000000000000 
  • rust-objdump target/riscv64imac-unknown-none-elf/debug/os -d --arch-name=riscv64
yunwei@ubuntu:~/Project/os$ rust-objdump target/riscv64imac-unknown-none-elf/debug/os -d --arch-name=riscv64target/riscv64imac-unknown-none-elf/debug/os:  file format ELF64-riscvDisassembly of section .text:0000000080200000 text_start:
80200000: 09 a0                         j   2
80200002: 01 a0                         j   0

似乎并没有把错误处理函数编译出来;

重写程序入口点 _start

  • OpenSBI 固件(Firmware)
  • OpenSBI 所做的一件事情就是把 CPU 从 M Mode 切换到 S Mode,接着跳转到一个固定地址 0x80200000,开始执行内核代码。

使用 QEMU 运行内核

main.rs 换成:

//! # 全局属性
//! - `#![no_std]`
//!   禁用标准库
#![no_std]
//!
//! - `#![no_main]`
//!   不使用 `main` 函数等全部 Rust-level 入口点来作为程序入口
#![no_main]
//! # 一些 unstable 的功能需要在 crate 层级声明后才可以使用
//! - `#![feature(asm)]`
//!   内嵌汇编
#![feature(asm)]
//!
//! - `#![feature(global_asm)]`
//!   内嵌整个汇编文件
#![feature(global_asm)]// 汇编编写的程序入口,具体见该文件
global_asm!(include_str!("entry.asm"));use core::panic::PanicInfo;/// 当 panic 发生时会调用该函数
/// 我们暂时将它的实现为一个死循环
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {loop {}
}/// 在屏幕上输出一个字符,目前我们先不用了解其实现原理
pub fn console_putchar(ch: u8) {let _ret: usize;let arg0: usize = ch as usize;let arg1: usize = 0;let arg2: usize = 0;let which: usize = 1;unsafe {llvm_asm!("ecall": "={x10}" (_ret): "{x10}" (arg0), "{x11}" (arg1), "{x12}" (arg2), "{x17}" (which): "memory": "volatile");}
}/// Rust 的入口函数
///
/// 在 `_start` 为我们进行了一系列准备之后,这是第一个被调用的 Rust 函数
#[no_mangle]
pub extern "C" fn rust_main() -> ! {// 在屏幕上输出 "OK\n" ,随后进入死循环console_putchar(b'O');console_putchar(b'K');console_putchar(b'\n');loop {}
}

编译的时候会出现:

yunwei@ubuntu:~/Project/os$ cargo buildCompiling os v0.1.0 (/home/yunwei/Project/os)
error[E0658]: use of unstable library feature 'llvm_asm': prefer using the new asm! syntax instead--> src/main.rs:38:9|
38 |         llvm_asm!("ecall"|         ^^^^^^^^|= note: see issue #70173 <https://github.com/rust-lang/rust/issues/70173> for more information= help: add `#![feature(llvm_asm)]` to the crate attributes to enableerror: aborting due to previous errorFor more information about this error, try `rustc --explain E0658`.
error: could not compile `os`.To learn more, run the command again with --verbose.

根据编译器的提示,在头部添加:

#![feature(llvm_asm)]

编译通过;

尝试建立makefile

TARGET      := riscv64imac-unknown-none-elf
MODE        := debug
KERNEL_FILE := target/$(TARGET)/$(MODE)/os
BIN_FILE    := target/$(TARGET)/$(MODE)/kernel.binOBJDUMP     := rust-objdump --arch-name=riscv64
OBJCOPY     := rust-objcopy --binary-architecture=riscv64.PHONY: doc kernel build clean qemu run env# 默认 build 为输出二进制文件
build: $(BIN_FILE) # 通过 Rust 文件中的注释生成 os 的文档
doc:@cargo doc --document-private-items# 编译 kernel
kernel:@cargo build# 生成 kernel 的二进制文件
$(BIN_FILE): kernel@$(OBJCOPY) $(KERNEL_FILE) --strip-all -O binary $@# 查看反汇编结果
asm:@$(OBJDUMP) -d $(KERNEL_FILE) | less# 清理编译出的文件
clean:@cargo clean# 运行 QEMU
qemu: build@qemu-system-riscv64 \-machine virt \-nographic \-bios default \-device loader,file=$(BIN_FILE),addr=0x80200000# 一键运行
run: build qemu
  • 输入 make run ,报错: Makefile:16: *** missing separator. Stop.

问题解决:

如果是直接复制的话,可能会把 Makefiletab 键替换为空格键,而 makefile 的命令行,开头必须用 tab 键;比如把

doc:@cargo doc --document-private-items

@前面的空格替换为 tab 即可解决问题。

输出:

OpenSBI v0.5 (Oct  9 2019 12:03:04)____                    _____ ____ _____/ __ \                  / ____|  _ \_   _|| |  | |_ __   ___ _ __ | (___ | |_) || || |  | | '_ \ / _ \ '_ \ \___ \|  _ < | || |__| | |_) |  __/ | | |____) | |_) || |_\____/| .__/ \___|_| |_|_____/|____/_____|| ||_|Platform Name          : QEMU Virt Machine
Platform HART Features : RV64ACDFIMSU
Platform Max HARTs     : 8
Current Hart           : 0
Firmware Base          : 0x80000000
Firmware Size          : 116 KB
Runtime SBI Version    : 0.2PMP0: 0x0000000080000000-0x000000008001ffff (A)
PMP1: 0x0000000000000000-0xffffffffffffffff (A,R,W,X)
OK

看起来是OK的;

执行 kill, 退出 qemu

接口封装和代码整理

可以考虑去看看 openSBI的文档:https://github.com/riscv/riscv-sbi-doc/blob/master/riscv-sbi.adoc#legacy-sbi-extension-extension-ids-0x00-through-0x0f

新建os/src/sbi.rs文件:

//! 调用 Machine 层的操作
// 目前还不会用到全部的 SBI 调用,暂时允许未使用的变量或函数
#![allow(unused)]/// SBI 调用
#[inline(always)]
fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {let ret;unsafe {llvm_asm!("ecall": "={x10}" (ret): "{x10}" (arg0), "{x11}" (arg1), "{x12}" (arg2), "{x17}" (which): "memory"      // 如果汇编可能改变内存,则需要加入 memory 选项: "volatile");  // 防止编译器做激进的优化(如调换指令顺序等破坏 SBI 调用行为的优化)}ret
}const SBI_SET_TIMER: usize = 0;
const SBI_CONSOLE_PUTCHAR: usize = 1;
const SBI_CONSOLE_GETCHAR: usize = 2;
const SBI_CLEAR_IPI: usize = 3;
const SBI_SEND_IPI: usize = 4;
const SBI_REMOTE_FENCE_I: usize = 5;
const SBI_REMOTE_SFENCE_VMA: usize = 6;
const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7;
const SBI_SHUTDOWN: usize = 8;/// 向控制台输出一个字符
///
/// 需要注意我们不能直接使用 Rust 中的 char 类型
pub fn console_putchar(c: usize) {sbi_call(SBI_CONSOLE_PUTCHAR, c, 0, 0);
}/// 从控制台中读取一个字符
///
/// 没有读取到字符则返回 -1
pub fn console_getchar() -> usize {sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0)
}/// 调用 SBI_SHUTDOWN 来关闭操作系统(直接退出 QEMU)
pub fn shutdown() -> ! {sbi_call(SBI_SHUTDOWN, 0, 0, 0);unreachable!()
}

新建os/src/console.rs文件:

//! 实现控制台的字符输入和输出
//!
//! # 格式化输出
//!
//! [`core::fmt::Write`] trait 包含
//! - 需要实现的 [`write_str`] 方法
//! - 自带实现,但依赖于 [`write_str`] 的 [`write_fmt`] 方法
//!
//! 我们声明一个类型,为其实现 [`write_str`] 方法后,就可以使用 [`write_fmt`] 来进行格式化输出
//!
//! [`write_str`]: core::fmt::Write::write_str
//! [`write_fmt`]: core::fmt::Write::write_fmtuse crate::sbi::*;
use core::fmt::{self, Write};/// 一个 [Zero-Sized Type],实现 [`core::fmt::Write`] trait 来进行格式化输出
///
/// ZST 只可能有一个值(即为空),因此它本身就是一个单件
struct Stdout;impl Write for Stdout {/// 打印一个字符串/// /// 对于每一个字符调用 [`console_putchar`]fn write_str(&mut self, s: &str) -> fmt::Result {for c in s.chars() {console_putchar(c as usize);}Ok(())}
}/// 打印由 [`core::format_args!`] 格式化后的数据
///
/// [`print!`] 和 [`println!`] 宏都将展开成此函数
///
/// [`core::format_args!`]: https://doc.rust-lang.org/nightly/core/macro.format_args.html
pub fn print(args: fmt::Arguments) {Stdout.write_fmt(args).unwrap();
}/// 实现类似于标准库中的 `print!` 宏
///
/// 使用实现了 [`core::fmt::Write`] trait 的 [`console::Stdout`]
#[macro_export]
macro_rules! print {($fmt: literal $(, $($arg: tt)+)?) => {$crate::console::print(format_args!($fmt $(, $($arg)+)?));}
}/// 实现类似于标准库中的 `println!` 宏
///
/// 使用实现了 [`core::fmt::Write`] trait 的 [`console::Stdout`]
#[macro_export]
macro_rules! println {($fmt: literal $(, $($arg: tt)+)?) => {$crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));}
}

新建os/src/panic.rs文件:

//! 代替 std 库,实现 panic 和 abort 的功能use core::panic::PanicInfo;
use crate::sbi::shutdown;/// 打印 panic 的信息并 [`shutdown`]
///
/// ### `#[panic_handler]` 属性
/// 声明此函数是 panic 的回调
#[panic_handler]
fn panic_handler(info: &PanicInfo) -> ! {// `\x1b[??m` 是控制终端字符输出格式的指令,在支持的平台上可以改变文字颜色等等// 这里使用错误红// 需要全局开启 feature(panic_info_message) 才可以调用 .message() 函数// 参考:https://misc.flogisoft.com/bash/tip_colors_and_formattingprintln!("\x1b[1;31mpanic: '{}'\x1b[0m", info.message().unwrap());shutdown()
}/// 终止程序
///
/// 调用 [`panic_handler`]
#[no_mangle]
extern "C" fn abort() -> ! {panic!("abort()")
}

新建os/src/main.rs文件:

//! # 全局属性
//! - `#![no_std]`
//!   禁用标准库
#![no_std]
//!
//! - `#![no_main]`
//!   不使用 `main` 函数等全部 Rust-level 入口点来作为程序入口
#![no_main]
//!
//! - `#![deny(missing_docs)]`
//!   任何没有注释的地方都会产生警告:这个属性用来压榨写实验指导的学长,同学可以删掉了
#![warn(missing_docs)]
//! # 一些 unstable 的功能需要在 crate 层级声明后才可以使用
//! - `#![feature(asm)]`
//!   内嵌汇编
#![feature(asm)]
//!
//! - `#![feature(global_asm)]`
//!   内嵌整个汇编文件
#![feature(global_asm)]
//!
//! - `#![feature(llvm_asm)]`
//!   声明需要使用 llvm_asm 宏特性
#![feature(llvm_asm)]
//!
//! - `#![feature(panic_info_message)]`
//!   panic! 时,获取其中的信息并打印
#![feature(panic_info_message)]#[macro_use]
mod console;
mod panic;
mod sbi;// 汇编编写的程序入口,具体见该文件
global_asm!(include_str!("entry.asm"));/// Rust 的入口函数
///
/// 在 `_start` 为我们进行了一系列准备之后,这是第一个被调用的 Rust 函数
#[no_mangle]
pub extern "C" fn rust_main() -> ! {println!("Hello rCore-Tutorial!");panic!("end of rust_main")
}

运行:make run:

OpenSBI v0.5 (Oct  9 2019 12:03:04)____                    _____ ____ _____/ __ \                  / ____|  _ \_   _|| |  | |_ __   ___ _ __ | (___ | |_) || || |  | | '_ \ / _ \ '_ \ \___ \|  _ < | || |__| | |_) |  __/ | | |____) | |_) || |_\____/| .__/ \___|_| |_|_____/|____/_____|| ||_|Platform Name          : QEMU Virt Machine
Platform HART Features : RV64ACDFIMSU
Platform Max HARTs     : 8
Current Hart           : 0
Firmware Base          : 0x80000000
Firmware Size          : 116 KB
Runtime SBI Version    : 0.2PMP0: 0x0000000080000000-0x000000008001ffff (A)
PMP1: 0x0000000000000000-0xffffffffffffffff (A,R,W,X)
Hello rCore-Tutorial!
panic: 'end of rust_main'

看起来是好啦!

rust写操作系统 rCore tutorial 学习笔记:实验指导零 创建项目与启动相关推荐

  1. GTSAM Tutorial学习笔记

    GTSAM Tutorial学习笔记 GTSAM Tutorial学习笔记 1. 基本原理 2. Demo代码 3. LIO-SAM中部分代码分析 3.1 预积分因子图 3.2 关键帧因子图 GTSA ...

  2. 操作系统真相还原学习笔记

    录像51 操作系统真相还原–学习笔记 这个笔记是把郑钢先生的代码在windows上编译运行了,希望大家在win上也调试地愉快. 链接:https://pan.baidu.com/s/1T7Sj_ZwA ...

  3. 嵌入式知识-ARM裸机-学习笔记(9):SD卡启动详解(S5PV210)

    嵌入式知识-ARM裸机-学习笔记(9):SD卡启动详解(S5PV210) 一.SD卡介绍 1. SD卡背景知识和特点 SD卡.MMC卡.MicroSD.TF卡:这些卡其实内部就是Flash存储颗粒,比 ...

  4. V-rep学习笔记:机器人模型创建2—添加关节

    下面接着之前经过简化并调整好视觉效果的模型继续工作流,为了使模型能受控制运动起来必须在合适的位置上添加相应的运动副/关节.一般情况下我们可以查阅手册或根据设计图纸获得这些关节的准确位置和姿态,知道这些 ...

  5. APPCAN学习笔记005---AppCan IDE安装下载,AppCan IDE启动

    APPCAN学习笔记005---AppCan IDE安装下载,AppCan IDE启动 创梦技术交流平台:资源下载,技术交流,网络赚钱: 交流qq群:1群:248318056 2群:251572072 ...

  6. CMake 学习笔记 02 - 更复杂的项目

    CMake 学习笔记 02 - 更复杂的项目 源代码见 https://github.com/fengyc/cmake-tutorial 源代码目录组织 一般的项目,会划分为多个子目录,每个子目录中包 ...

  7. 「学习笔记」品优购项目-上(页面公共部分 )

    「学习笔记」品优购项目-上 品优购项目-上 目标 品优购项目规划 网站制作流程 品优购项目介绍 品优购项目的学习目的 开发工具以及技术栈 开发工具 技术栈 品优购项目搭建工作 创建的文件夹如下(称为项 ...

  8. Pyqt学习笔记(1) ----- 创建按钮,并添加单击事件

    Pyqt学习笔记(1) ----- 创建按钮,并添加单击事件 from PyQt5 import QtCore, QtGui, QtWidgets import sys from PyQt5.QtGu ...

  9. UE5 Shader基础学习笔记——01-12 图形管线/创建shader/数学节点/贴图压缩/LerpDotUV/常用向量/坐标空间/MinMaxClampSaturate/法线贴图混合

    UE5 Shader基础学习笔记--01-12 图形管线/创建shader/数学节点/贴图压缩/LerpDotUV/常用向量/坐标空间/MinMaxClampSaturate/法线贴图混合 Lec01 ...

  10. Hadoop学习笔记—20.网站日志分析项目案例(一)项目介绍

    Hadoop学习笔记-20.网站日志分析项目案例(一)项目介绍 网站日志分析项目案例(一)项目介绍:当前页面 网站日志分析项目案例(二)数据清洗:http://www.cnblogs.com/edis ...

最新文章

  1. 将shp导入SDE中出现“表或视图不存在”问题
  2. Spark中的内存计算是什么?
  3. Exchange 2016证书配置
  4. android 自定义view实现拖动放大缩小_自定义itemCheckView
  5. 数据库连接池的设计思路及java实现
  6. 【2016年第1期】专题导读:农业大数据
  7. matlab 显示歌词,【Matlab编程】生日快乐歌(显示歌词)—matlab版
  8. websocket替代方案_WebSocket 有没有可能取代 AJAX ?
  9. python怎么打开turtle_python的turtle库真好玩
  10. matlab直方图线性拉伸,直方图均衡化与直方图规定化的MATLAB实现
  11. 图像的仿射变换原理、Homography、alpha通道以及python实现
  12. Oracle错误处理机制
  13. 使用adb工具向android设备上传/下载文件
  14. aspcms用mysql_关于ASPCMS标签调用的一些总结
  15. 【测试源】【rtmp flv hls】拉流播放测试
  16. 刀片计算机学习资料第192篇:6U VPX i7 刀片计算机
  17. “胜兵先胜而后求战,败兵先战而后求胜”—如何通过系统分析师考试
  18. 科恩二进制静态漏洞检测工具BinAbsInspector
  19. 如何选择一款适合自己的医疗险,看保通来详解
  20. 10步成为一个优秀的Java开发!

热门文章

  1. manjaro搭建java_Manjaro配置JDK
  2. 基于spring boot的实验自动评分系统
  3. 牛客网项目——项目开发(六):点赞,关注和取关,优化登录
  4. 【samba】Wodows同步Linux文件|搭建共享文件服务器——在windows上映射网络驱动器...
  5. 集成底座项目实施总结
  6. python提示syntaxerror什么意思_“SyntaxError:print”调用中缺少括号在Python中是什么意思?...
  7. 启动计算机按住del不放,电脑黑屏bios界面都进不去怎么办
  8. JS 中删除节点的两个方法
  9. 取消管理员取得所有权_解决win7系统下管理员取得所有权的技巧
  10. 用Python3为您随机产生一个密码,是不是很高级嘞?