shyJyt's Blog

shyJyt's Blog

賽博筆記本

算是第一篇博客🥰,总结一下今天搭建博客的过程。

1. 安装 Hexo

按官方文档来就完事了。

遇到一个问题,只有全局安装 Hexo-cli 才能直接用 hexo 命令,否则就得带上 npx ?

TBD…

hexo 本地运行时并不具备热更新机制,甚至有的还需要先 hexo clean 把之前的缓存删了才会看到修改生效。

hexo 中的三种 layout 分别为 post, draft, page,这里的 page 对应的是一个二级路由,也就是列出的几个菜单栏,像分类、标签之类的,所以要创建多个菜单栏的话,先 new 一个 Page,再去主题的配置文件中的 menu 添加这一项就行了,因为主题才是渲染页面的核心。

hexo 图片不显示?

每篇博客的静态资源不隔离显然是很乱的,为此 hexo 配置文件中有一个选项 post_asset_folder ,开启后创建博客时会在同级目录创建一个同名文件夹,里面存放该博客的静态资源。按道理按照相对路径就没问题。

但是 hexo 在 build 出最终的网页文件时,文件位置发生了变化,如下图所示。而将 md 文件中的图片转为 img 标签时,src 属性没弄对,导致图片无法显示。

image-20240407183237493

解决办法:

在 _config.yml 中添加以下配置,而图片路径只用写文件名就行,唯一缺点就是在构建完成之前图片仍然不可见。

1
2
3
marked:
prependRoot: true
postAsset: true

不管是装插件,还是加配置,只要路径对了就行。

2. 配置 Github Pages

  1. 建库 blog,或者直接就用 username.github.io 这个库,区别就是最后生成的页面 url 是否要带上库名。

  2. 修改仓库设置。

    image-20240407163055732

  3. 编写 .github/workflows/pages.yml,一个自动化部署的脚本,文档有介绍,本质就是自动完成 build ,将生成的 public 文件夹部署上去,至于是自己的服务器还是 github pages 就随便了。

类似地,前端自动化部署流程也是差不多的,脚本也相对更简单。

3. 配置 NexT 主题

区分 _config.yml 和 _config.theme-name.yml ,两者分别为 hexo 和主题的配置文件。

Hexo 带个默认主题,其他的可以通过 git 和 npm 两种方式获取,主要介绍 npm.

通过 npm 下载下来后,主题的主体部分在 node_modules 中,包括默认的配置文件以及静态文件。NexT 的官方推荐不要去修改,而是单独在根目录创建一个一模一样的配置文件,再修改新创建的配置文件来实现定制。

据观察,主题配置文件会扫描根目录的 source 文件夹和主题包里面的 source 文件夹,且将其作为相对路径的起点,观察一些图片图标的路径就明白了,至于是否会覆盖还没试过。

最后在 _config.yml 中修改 theme 属性就可以了。

4. 杂项

Markdown 不能像 word 将标题作为列表项?这样如果要手动标号的话,后续修改就很麻烦。

感觉中英文切换以及空格很麻烦,以后或许尝试全英文。

文件名怎么这么难取?😵‍💫

1. 大致内容

熟悉环境以及一些基本的系统调用。

2. read()

2.1. 读管道的几种情况
  1. 有东西就读
  2. 没东西就阻塞
  3. 写端关闭(引用次数为0)且没东西返回 0

3. fork()

3.1. 对于文件描述符是怎么处理的?

父子进程共享文件描述符及其偏移指针,fork() 仅增加引用次数。

1
2
3
4
5
6
// xv6/kernel/proc.c
// increment reference counts on open file descriptors.
for(i = 0; i < NOFILE; i++)
if(p->ofile[i])
np->ofile[i] = filedup(p->ofile[i]);
np->cwd = idup(p->cwd);

close(fd) 只是减少一次引用,减到 0 才会关闭。

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
#include <stdio.h>
#include <unistd.h>

int main() {
int fa[2];
pipe(fa);

int tmp = 10;
write(fa[1], &tmp, sizeof(tmp));

if (fork() == 0) {
sleep(5);
close(fa[1]);
printf("child wake\n");
}
else {
close(fa[1]);

read(fa[0], &tmp, sizeof(tmp));
printf("fa read %d\n", tmp);
int ret = read(fa[0], &tmp, sizeof(tmp));
printf("ret: %d\n", ret);
}

return 0;
}

输出如下图,且后两行在 5s 后才输出。已知 read 在管道写端被关闭时返回 0 ,推断分别在父子进程 close() 一次后,写端引用次数减到 0 后关闭,read 不再阻塞, 返回 0 .

image-20240408111553010

4. primes

尽早关闭不用的读 / 写端,避免文件描述符耗尽。

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include "kernel/types.h"
#include "user/user.h"

void solve(int *fa) {
close(fa[1]);

int flag;
read(fa[0], &flag, sizeof(flag));
printf("prime %d\n", flag);

int tmp;
if (read(fa[0], &tmp, sizeof(tmp))) {
int child[2];
pipe(child);

if (fork() == 0) {
solve(child);
}
else {
close(child[0]);
if (tmp % flag) {
write(child[1], &tmp, sizeof(tmp));
}

while (read(fa[0], &tmp, 4)) {
if (tmp % flag) {
write(child[1], &tmp, sizeof(tmp));
}
}
close(fa[0]);
close(child[1]);

wait(0);
}
}

exit(0);
}

int main(int argc, char *argv[]) {
int child[2];
pipe(child);

if (fork() == 0) {
solve(child);
}
else {
for (int i = 2; i <= 35; i++) {
write(child[1], &i, sizeof(i));
}
close(child[1]);
wait(0);
}

exit(0);
}

5. xargs

1
command1 | xargs [options] [command2 [initial-arguments]]

作用是将 command1 的输出处理后,使其作为 command2 参数,command2 为可选项,默认为 echo.

实验要求只用实现 -n 1,即一次只从左边取一个参数,而 xargs 的默认参数分隔符为 \n,所以一次取一行作为参数,交由 exec 执行。

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include "kernel/types.h"
#include "kernel/param.h"
#include "kernel/stat.h"
#include "user/user.h"

#define MAXN 1024

int readLine(char *param[MAXARG], int cntp) {
char buf[MAXN];
int l = 0, r = 0;

while (read(0, buf + r, 1)) {
// split by '\n'
if (buf[r] == '\n') {
buf[r] = 0;
break;
}
r++;
if (r == MAXN) {
fprintf(2, "Too many params!\n");
exit(1);
}
}

if (r == 0) {
return -1;
}

// split by space
// ....abc...asd..asd..\0
while (l < r) {
while(l < r && buf[l] == ' ') l++;
param[++cntp] = buf + l;
while (l < r && buf[l] != ' ') l++;
buf[l++] = 0;
}

return cntp;
}

int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: xargs [options] [command [initial-arguments]]\n");
exit(1);
}

char *param[MAXARG];

for (int i = 0; i < argc - 1; i++) {
param[i] = malloc(strlen(argv[i + 1]) + 1);
strcpy(param[i], argv[i + 1]);
}

int cntp = -1;
while ((cntp = readLine(param, argc - 2)) != -1) {
// printf("%s, %d\n", param[0], cntp);
param[++cntp] = 0;
if (fork() == 0) {
exec(param[0], param);
// exec() will return only if a error occurs
exit(1);
}
else {
wait(0);
}
}

exit(0);
}

6. make grade

image-20240408204847240

7. 遗留问题

头文件的引入顺序不对会导致类型未定义等错误,为什么 Java 中没碰到过?两种语言的模块管理怎么对比来看?

#include 和其他 # 开头的命令(宏定义、条件编译)一样,为预处理命令,在程序编译的第一步预处理(gcc -E)中被执行对应的文本层面的操作,即将对应的头文件内容展开,生成对应的 .i 文件(需要 -o 指定生成文件,否则默认输出到控制台,仍为文本类型可直接查看)。由于展开是按顺序的,如果出现调用先于声明的情况就会出错。

对于 Java 的 import 只是指定了要引用的类,而具体的被引用类的代码则在执行的时候才调入内存,这个步骤是类加载器来实现的。

0%