0%

Configuration

mkdocs.yml 文件是 MkDocs 文档生成器的配置文件。

示例:mkdocs.yml

theme

该文件中的 theme 部分用于指定用于生成文档的主题。

在这种情况下,使用的主题是 Material。 name 字段指定主题的名称,而 custom_dir 字段指定包含主题自定义内容的目录。 features 字段指定可以为主题启用的可选功能列表。

以下是在此配置文件中列出的可选功能:

  • announce.dismiss: 启用可关闭的公告横幅,显示在文档的每个页面顶部。
  • content.action.edit: 在每个页面上启用“编辑”按钮,以便用户可以轻松编辑页面内容。
  • content.action.view: 在每个页面上启用“查看源代码”按钮,以便用户可以查看页面的源代码。
  • content.code.annotate: 启用代码注释功能,使用户可以添加注释以解释代码。
  • content.code.copy: 启用代码复制功能,使用户可以轻松复制代码。
  • content.tooltips: 启用工具提示功能,使用户可以在鼠标悬停时查看有关页面元素的信息。
  • navigation.footer: 在每个页面上启用页脚导航栏。
  • navigation.indexes: 启用索引导航栏,使用户可以轻松浏览文档中的索引。
  • navigation.sections: 启用部分导航栏,使用户可以轻松浏览文档中的各个部分。
  • navigation.tabs: 启用选项卡导航栏,使用户可以轻松浏览文档中的各个选项卡。
  • navigation.top: 在每个页面上启用顶部导航栏。
  • navigation.tracking: 启用导航跟踪功能,使用户可以跟踪他们在文档中的位置。
  • search.highlight: 启用搜索结果高亮显示功能。
  • search.share: 启用共享搜索结果功能,使用户可以轻松共享搜索结果。
  • search.suggest: 启用搜索建议功能,使用户可以在输入搜索查询时获得建议。
  • toc.follow: 启用目录跟随功能,使目录始终保持可见。

palette 字段指定了一个颜色方案,该方案包含以下内容:

  • scheme: 指定颜色方案的名称。
  • primary: 指定主要颜色。
  • accent: 指定强调颜色。
  • toggle: 指定切换到暗模式时使用的图标和名称。

第一个方案名为 default,其中主要颜色和强调颜色均为 indigo。切换到暗模式时,使用的图标为 material/brightness-7,名称为“切换到暗模式”。

第二个方案名为 slate,其中主要颜色和强调颜色均为 indigo。切换到亮模式时,使用的图标为 material/brightness-4,名称为“切换到亮模式”。

font 字段指定了用于文本和代码的字体。在此配置文件中,文本字体为 Roboto,代码字体为 Roboto Mono。 favicon 字段指定了网站图标的路径。 icon 字段指定了网站标志的路径。

favicon 和 icon 是网站的两个不同元素。

favicon 是网站的图标,通常显示在浏览器标签页上。它可以是一个小的图像文件,通常是 .ico 格式。在 mkdocs.yml 文件中,可以使用 favicon 字段来指定网站图标的路径。

icon 是网站的标志,通常显示在网站的标题栏或页眉中。它可以是一个图像文件,例如 .png 或 .jpg 文件。在 mkdocs.yml 文件中,可以使用 icon 字段来指定网站标志的路径。

plugin

这里列出了三个插件:blog、search和minify。其中,blog插件用于支持博客功能,search插件用于支持搜索功能,而minify插件用于压缩HTML文件。在search插件中,separator参数指定了搜索时的分隔符,这里的分隔符包括空格、连字符、逗号、冒号、等号、感叹号、方括号、括号、引号、反引号和斜杠等。

extra

analytics

analytics参数用于指定网站分析服务提供商和属性ID,例如衡量网站流量。

常见问题

See the doc.

  1. Keep underscore in file name

refer to:

文件示例

  • /proc: 查看手册 man proc

  • /proc/self/exe: 是指向当前进程的程序文件的软链接。

  • /proc/<pid>/exe: 是指向进程<pid>的程序文件的软链接。

    How to Find File System Names

  • /proc/[pid]/status:可查看进程的内存使用峰值等信息,关键字为”VmHWM”、”VmPeak”、”VmRSS”。

  • /proc/[pid]/stat:get current memory

  • /dev/shm:

    What Is /dev/shm And Its Practical Usage

    1
    $ df -h

    Sample outputs:

    1
    2
    3
    4
    5
    6
    7
    Filesystem            Size  Used Avail Use% Mounted on
    /dev/mapper/wks01-root
    444G 70G 351G 17% /
    tmpfs 3.9G 0 3.9G 0% /lib/init/rw
    udev 3.9G 332K 3.9G 1% /dev
    tmpfs 3.9G 168K 3.9G 1% /dev/shm
    /dev/sda1 228M 32M 184M 15% /boot

分区(Partition)和文件系统(Filesystem)

本小节内容来自 参考链接

分区:

Linux上的分区指:存储设备中划分出来的一个片段,该片段与其他片段逻辑上分离,好比一个个独立的房间。

分区表(partition table):

分区表存储各个分区的元数据,比如起始位置、终止位置、大小等。
有两种主要的分区表类型,MBR(older)和GPT(newer):

Partition tables Maximum primary partitions Maximum size for each partition Security Operating system Support
Master Boot Record (MBR) 4 2TB No such security features Supports most modern OS
Guid Partition Table (GPT) No such limit 18 Exabytes CRC32 checksum mechanism to verify the integrity of files Supports most modern OS

从上表可以很明显地看出,为什么GPT更推荐。

文件系统

文件系统是我们在每个分区中管理数据的方式。它负责索引、存储、检索、命名文件和维护文件的元数据(文件所有者、大小、权限等)。存储在分区中。

一个文件保存在多个连续的 扇区(sector) 中,现代每个扇区大约为4096字节。
文件系统负责组织哪些扇区准备好使用了、一个文件必须存储在哪个扇区、哪个扇区存储了什么文件。
如果没有这种组织,就无法无法检索任何文件,因为系统无法得知文件的位置(block,块)。

主要的文件系统分类:

  • FAT

文件分配表(FAT,File Allocation Table)是Microsoft开发的第一个文件系统。
从1997发布之后,有多个版本,称为FAT12、FAT16、FAT32,连续地增加了最大支持文件大小(file size)和驱动器大小(drive size)。

FAT32允许的最大文件大小为4Gb。直至WindowsXP,FAT32是默认的文件系统,之后被NTFS取代。
虽然FAT非常基础,但是它支持几乎所有的设备和操作系统。

注: 驱动器(drive),是一个能存储和读取非易失信息的位置,比如磁盘(disk)或光盘(disc)。

如下图,驱动器A:是一个软盘(floppy drive),
驱动器C:是主硬盘(primary hard drivce),
驱动器D:和E:是分区,F:是CD-ROM
CD-ROM常常是最后一个盘符(drive letter)。
在多数情况下,硬盘是C:驱动器,CD-ROM或其他光盘是D:驱动器。

驱动器示例

  • NTFS

新技术文件系统(New Technology File System,NTFS)是FAT的现代替代者。
除了支持高达16EB(大于170亿GB)的驱动器大小和256TB的单文件大小外,还支持日志系统(journaling system)。

  • ext/ext2/ext3/ext4

Linux的扩展文件系统(extended file system)或ext于1992年发布。之后有了3次更新:

ext2引入了文件属性(文件权限),ext3引入了日志功能(journaling)。

ext4对ext2和ext3向后兼容,增加了存储限制和一些性能调整。
可以支持高达1EB的卷(volumn),单个文件可以达16TB。

ext4也引入了延迟内存分配的概念,即在文件被强制刷新到存储设备时才为其分配扇区。
这提高了CPU的性能并减少了坏的扇区。
今天几乎所有的现代Linux发行版都使用ext4作为默认的文件系统。

  • ZFS

  • Btrfs

总结

下图是一个分区和文件系统的层次结构示例:

分区和文件系统的层次结构示例图

我有一个500GB的SSD,分成3个分区(boot、home、root),使用GPT作为分区表。

我没有分出swap分区。所有的分区都跑在ext4文件系统上。

在一个双启动(dual-booted)存储设备(Windows和Linux)上,还有几个适用于Windwows的NTFS分区。

你可以使用以下命令在任意存储设备上查看分区:

1
lsblk

更多资源请访问:

挂载

挂载:使设备上的文件和目录可以通过文件系统访问的一个过程。见维基百科

挂载点:A mount point is a location in the partition used as a root filesystem.

驱动

相关命令一览表

lsblk

lsblk [options] [device...]

list all avaivable or specified block devices.
Reads the sysfs filesystem to gather information.

df

df [OPTION]... [FILE]...

report file system disk space usage on which each FILE resides.

df -T 打印文件系统的类型。

du

du [OPTION]... [FILE]...

estimate file space usage.

quota

quota -s -u user...

display users' disk usage and limits.
quota reports the quotas of all the filesystems listed in /etc/mtab.
For filesystems that are NFS-mounted a call to the rpc.rquotad on the server machine is performed to get the information.

-s, --human-readable

repquota

prints a summary of disc usage and quotas for the specified file system.

mount

lsof - list open files

https://unix.stackexchange.com/questions/11238/how-to-get-over-device-or-resource-busy

概念

可重入函数:如果一个函数在执行过程中被中断服务程序打断,执行中断服务程序之后恢复执行,还能不妨碍之前的执行,就称该函数是可重入的

可重入函数一般用于硬件中断处理递归等应用程序中。

可重入程序/可重入子例程:在多个处理器上能被安全地多次并发调用。

与线程安全的区别:可重入函数的概念在多任务操作系统出现之前就存在了,所以该概念仅仅针对的是单线程执行过程。
一个函数可以是线程安全但非可重入的,例如,该函数每次都使用互斥量来包裹。但是,如果该函数用于中断服务程序,那么,它可能在等待第一次执行过程释放互斥量时陷入饥饿。TODO:陷入饥饿为什么就不是可重入了?

要实现可重入性,函数通常需要满足以下条件:

  • 不使用静态或全局变量:这些变量在多次调用之间共享,可能导致数据竞争。
  • 不依赖于不可重入的函数:例如,标准库中的某些函数可能不是可重入的。
  • 不使用动态内存分配:动态内存分配可能会导致竞争条件。
  • 不使用信号处理:信号处理可能会中断函数的执行。

可重入性在多线程编程和中断处理程序中尤为重要,因为它确保了函数在并发环境下的安全性。

可重入 VS 线程安全

可重入性和线程安全虽然都涉及到并发编程,但它们有不同的侧重点:

可重入性:

定义:一个函数可以在被中断后安全地再次调用,而不会影响其执行结果。
条件:不使用静态或全局变量、不依赖不可重入的函数、不使用动态内存分配、不使用信号处理。
应用场景:主要用于中断处理程序和嵌入式系统。
线程安全:

定义:一个函数或代码块在多线程环境下可以安全地并发执行,而不会导致竞争条件或数据不一致。
条件:通常需要使用同步机制(如互斥锁、信号量)来保护共享资源。
应用场景:主要用于多线程编程。
总结来说,可重入性关注的是函数在被中断后能否安全地再次调用,而线程安全关注的是在多线程环境下能否安全地并发执行。可重入函数不一定是线程安全的,线程安全的函数也不一定是可重入的。

  1. 可重入函数不一定是线程安全的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

int counter = 0;

void reentrantFunction() {
int localCounter = counter;
localCounter++;
counter = localCounter;
}

int main() {
reentrantFunction();
printf("Counter: %d\n", counter);
return 0;
}

解释:

reentrantFunction 是可重入的,因为它满足以下条件:

不使用静态或全局变量:虽然 counter 是一个全局变量,但 reentrantFunction 中的操作都是基于局部变量 localCounter,并且没有依赖于函数外部的状态。
不依赖不可重入的函数:reentrantFunction 中没有调用任何不可重入的函数。
不使用动态内存分配:函数中没有使用 malloc 或其他动态内存分配函数。
不使用信号处理:函数中没有涉及信号处理。
因此,reentrantFunction 可以在被中断后安全地再次调用,而不会影响其执行结果。

然而,正因为它使用了全局变量 counter,在多线程环境下可能会导致竞争条件,所以它不是线程安全的。

  1. 线程安全的函数不一定是可重入的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <pthread.h>

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int counter = 0;

void* threadSafeFunction(void* arg) {
pthread_mutex_lock(&lock);
counter++;
pthread_mutex_unlock(&lock);
return NULL;
}

int main() {
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, threadSafeFunction, NULL);
pthread_create(&thread2, NULL, threadSafeFunction, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
printf("Counter: %d\n", counter);
return 0;
}

解释:

这个函数 threadSafeFunction 是线程安全的,因为它使用了互斥锁来保护对 counter 的访问。
然而,它不是可重入的,因为它使用了互斥锁,互斥锁在中断处理程序中可能会导致死锁:

互斥锁的使用:互斥锁用于确保线程安全,但它们在中断处理程序中可能会导致死锁。如果一个线程在持有锁时被中断,然后中断处理程序尝试再次调用该函数并试图获取同一个锁,就会发生死锁。

依赖于锁的状态:函数的执行依赖于锁的状态。如果锁已经被其他线程持有,函数就无法继续执行,必须等待锁释放。这种依赖性使得函数在中断后无法安全地再次调用。

不可重入的行为:由于锁的存在,函数在中断后重新进入时可能无法正确处理锁的状态,从而导致不可预期的行为。

Reference

Get IP from the host name

Key function: getaddrinfo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#define _POSIX_C_SOURCE 200112L

#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/types.h>
#include <errno.h>
#include <strings.h>
#include <arpa/inet.h>

int main() {
struct addrinfo hints;
bzero(&hints, sizeof hints);
// hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

struct addrinfo *res = 0;
// The following 3 hostnames are legal.
int rc = getaddrinfo("www.baidu.com", NULL, &hints, &res);
// int rc = getaddrinfo("localhost", NULL, &hints, &res);
// int rc = getaddrinfo("PC-XXX", NULL, &hints, &res); // PC-XXX is a hostname
if (rc != 0)
perror("getaddrinfo failed");

for (struct addrinfo* res_i = res; res_i != NULL; res_i = res_i->ai_next) {
if (res_i->ai_addr) {
if (res_i->ai_addr->sa_family == AF_INET) {
char ip4[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(((struct sockaddr_in*)(res_i->ai_addr))->sin_addr), ip4, INET_ADDRSTRLEN);
printf("IP: %s\n", ip4);
} else {
char ip6[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &(((struct sockaddr_in6*)(res_i->ai_addr))->sin6_addr), ip6, INET_ADDRSTRLEN);
printf("IP: %s\n", ip6);
}
}
}

return 0;
}

预定义宏

#if defined(__linux)
#ifdef LINUX2

C标准预定义宏

  • __LINE__
  • __func__
  • __FILE__
  • NDEBUG:参考_DEBUG和NDEBUG的区别,其中,_DEBUG是Visual Studio定义的,NDEBUG是C/C++标准。

GNU C预定义宏

官方文档

  • __COUNTER__: 扩展为从0开始的连续整数值,每次在源码中出现,则加1。不同源文件的__COUNTER__互不影响。

    可以用来生成唯一的命名。
    参考链接

    1
    2
    3
    4
    5
    6
    7
    8
    #define CONCAT_IMPL(x,y) x##y
    #define CONCAT(x,y) CONCAT_IMPL(x,y)
    #define VAR(name) CONCAT(name,__COUNTER__)
    int main() {
    int VAR(myvar); // 展开为 int myvar0;
    int VAR(myvar); // 展开为 int myvar1;
    int VAR(myvar); // 展开为 int myvar2;
    }
  • program_invocation_name:参考man page

#pragma

#pragma weak

Synopsis

1
#pragma weak function-name1 [= function-name2]

#pragma weak means that even if the definition of the symbol is not found, no error will be reported.

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>

// It is not an error for symbol to never be defined at all.
// Without this line, the address of "foo" will always evaluate to "true",
// so the linker will report an "undefined reference to 'foo'" error.
#pragma weak foo
// The declaration is needed.
/* extern */ void foo();

int main() {
if (foo)
foo();

return 0;
}

Reference: 123

Host site

有两个托管个人站点的网站:Github pagesreathedocs.

Github Pages

使用github pages可以搭建个人主页。

github pages简介:官方链接

github pages使用了CNAME record技术,参考:链接1链接2Custom domains in Github Pages

注:Read the Docs也是一个很好的搭建个人主页的网站。

Github Pages 站点类型

有3种类型的 Github Pages 站点(sites):project, user 和 organization 。

Project sites 连接到 github 上特定 project ,比如 Javascript library 或 recipe collection。user 或 organization sites 连接到 github.com 的特定账户。

发布 user site ,你必须创建一个你的个人账户下的一个名为 <username>.github.io 的 repository 。发布 organization site ,你必须创建一个组织所有的名为 <organization>.github.io 的 repository 。除非你使用 custom domain ,否则 user 和 organization sites 将位于 http(s)://<username>.github.iohttp(s)://<organization>.github.io

project site 的源文件存储在作为 project 的相同的 repository 中。除非使用 custom domain , 否则 project sites 将位于 http(s)://<username>.github.io/<repository>http(s)://<organization>.github.io/<repository>

有关如何自定义影响您网站的域名的更多信息,参见”About custom domains and GitHub Pages“。

每个 github 账户允许创建 1 个 user 或 organization 站点。无论是被组织还是个人所有,project 站点的个数不限制。

GitHub Pages 访问方法

参考官方文档

例如,你的project站点配置的发布源是gh-pages分支,然后在gh-pages分支上创建了一个about/contact-us.md文件,你将可以在https://<user>.github.io/<repository>/about/contact-us.html访问它。

你也可以使用Jekyll等静态站点生成器来给你的github page配置一个主题。

站点发布常见问题的解决方法

Github workflows

参考官方文档

Github个人访问令牌

Github个人访问令牌

readthedocs

Similar to the github pages.

Tutorial

配置前准备

Markdown编辑器

推荐的markdown编辑器

  • VSCode:免费。VSCode原生支持Markdown,安装一些插件可以帮助更快地编写markdown文件。
  • Typora:现在已经开始收费。

VSCode markdown插件:

  • Mardown All in One: 提供快捷键,帮助更快的编写markdown文件。
  • Markdown+Math:提供数学公式支持。
  • Markdown Preview Enhanced: 将原生markdown预览的黑色背景改成白色。
  • Markdown Preview Github Styling:提供Github风格的预览。

在线表格生成器:可以生成Markdown、Text、HTML、LaTex、MediaWiki格式的表格。

轻量级虚拟机WSL

WSL,Windows Subsystem for Linux,是Windows提供的轻量级Linux虚拟机。

安装教程:见链接

WSL默认没有启用systemctl:

启用systemctl的方法:链接

替代方法:不需要启动systemctl,因为会比较占用资源,启动也会变慢。可以使用service命令替代。

WSL默认没有安装openssl-server:

使用ssh连接到服务器时,需要服务器运行着sshd程序,否则连接不上,会出现”Connection refused“错误。

参考链接

查看openssh-server有没有安装:

1
dpkg --list | grep ssh

注:如果安装了openssh-server,执行which sshd可以看到路径。

WSL默认没有安装openssh-server,安装方法:

1
sudo apt-get install openssh-server

启动ssh:

1
sudo service ssh start

ssh-keygen

1
2
# 删除错误的 know_hosts 项
ssh-keygen -f "/home/<user>/.ssh/known_hosts" -R "ubuntu"

通过https登录到github

git push不再支持输入用户名和密码,当提示输入密码时,需要输入personal access token.

步骤1:在github上创建personal access token

步骤2:在命令行上使用personal access token

步骤3:为了避免每次都需要输入personal access token,可以将其缓存在git client上

1
gh auth login

注:使用gh命令需要先安装GitHub CLI:

1
sudo apt-get install gh

WSL常见问题

在WSL的Linux下,在.bashrc中加入:

1
2
3
4
function browser() {
explorer.exe $(wslpath -w $(realpath $1))
}
export -f browser

打开html:

1
browser index.html

静态站点生成器

以下几种静态站点生成器都可以用来搭建个人主页。如果使用除JekyII外的工具,则需要配置Github Actions以构建和发布你的站点。

mkdocs

mkdocs是一个快速的静态网页生成器。

mkdocs.yml 文件是 MkDocs 文档生成器的配置文件,其格式说明参见这里

JekyII

Jekyll 是一个静态站点生成器,内置对 GitHub Pages 的支持和简化的构建进程。

参见 About GitHub Pages and Jekyll

Hexo

Sphinx

Install Sphinx
Hosting the documentation

python

python建立个人站点

python -m http.server

Syntax

Markdown

reStructuredText

.rst

大型文件存储

图片

Postimages

  • 网站: https://postimages.org/
  • 特点:
  • 适合快速上传和生成图片链接。
  • 不需要注册,可以直接上传图片并获得链接。
  • 提供图片缩略图和直接链接。
  • 上传后图片会长期存储,适用于网站和博客图片托管。

视频

TinyPic (由 Photobucket 提供)

  • 网站: https://tinypic.com/
  • 特点:
  • 支持上传并生成快速共享链接。
  • 上传后,链接可以嵌入到社交媒体、论坛、博客中。
  • 图片和视频都支持托管,但TinyPic已关闭,现由Photobucket服务接管。

Git LFS

GitHub 提供的用于存储大型文件的服务叫 Git LFS(Git Large File Storage)。

什么是 Git LFS?

Git LFS 是一个 Git 扩展,用于处理 Git 仓库中的大型文件(如图片、视频、音频等),它通过将大型文件存储在 GitHub 的独立存储区来解决 Git 的性能瓶颈。

Git LFS 的免费额度:

GitHub 为免费账户提供一定的 LFS 存储和带宽额度:

免费存储:每个 GitHub 账户提供 1GB 的 LFS 存储。

免费带宽:每个月 1GB 的下载带宽。

如果超出免费额度,你可以选择购买更多的存储和带宽。

Git LFS 的工作原理:

文件替代:Git LFS 用一个指向大文件的指针来替代文件内容,将文件本身存储在 GitHub 提供的 LFS 存储空间中。

文件存储:实际的文件内容存储在 GitHub 的 LFS 存储中,Git 只会管理这些文件的指针。

下载/上传:当你从仓库克隆或拉取代码时,Git LFS 会自动下载大文件,反之,当你提交大文件时,Git LFS 会将其上传到 GitHub。

如何使用 Git LFS?

  1. 安装 Git LFS:
    首先需要安装 Git LFS,可以通过 Git LFS 官方网站 下载和安装。
1
git lfs install
  1. 初始化 Git LFS:
    在你的 Git 仓库中初始化 Git LFS。
1
2
3
4
5
git lfs track "*.jpg"   # 例如跟踪所有 jpg 格式的图片
git add .gitattributes # 添加 .gitattributes 文件
git add <large-file> # 添加大型文件
git commit -m "Add large file"
git push origin main # 推送到 GitHub
  1. 使用 Git LFS 进行提交:
    每次提交时,Git LFS 会自动处理大文件,并将它们上传到 GitHub LFS 存储区。
优势:

减少仓库体积:通过将大文件存储在 Git LFS 中,可以避免将大文件直接存储在 Git 仓库中,从而提高 Git 的性能。

适用于大文件:适合管理图像、视频、二进制文件等大型文件,避免 Git 克隆和拉取过程变得过于缓慢。

总结:

Git LFS 是 GitHub 提供的用于管理和存储大型文件的扩展服务,适合存储需要频繁更改或管理的大文件(如图片、音频、视频等)。

推荐书籍

  1. 多人在线游戏架构实战:基于C++的分布式游戏编程/彭放编著. –北京:机械工业出版社,2020.11 (ISBN 978-7-111-66792-6)