0%

记账

记账功能打开后,系统会在每个进程结束后记录一条账单信息。标准工具 sa(8) 对账单文件进行汇总,lastcomm(1) 则就先前执行的命令列出相关信息。

Tip

Linux 系统的进程记账功能属于可选内核组件,可以通过 CONFIG_BSD_PROCESS_ACCT 选项进行配置。 在 Debian 上,需要使用 apt install acct 来安装(否则 man 8 sawhich lastcomm 都找不到)。

特权进程可以使用 acct 系统调用来打开或关闭记账功能,应用程序则很少使用到这种调用。

系统调用 clone()

clone() 的地位和 fork()vfork() 一样,它们在内核中都是用 do_fork() 来实现的,但是它能支持比后两个函数更加精细的控制,因此也被用作线程实现的方式。我们一般使用的 clone() 是 glibc 的包装函数,它和内核提供的 SYS_clone 系统调用有一点差异。

/* Prototype for the glibc wrapper function */

#define _GNU_SOURCE
#include <sched.h>

int clone(int (*fn)(void *_Nullable), void *stack, int flags,
         void *_Nullable arg, ...  /* pid_t *_Nullable parent_tid,
                                      void *_Nullable tls,
                                      pid_t *_Nullable child_tid */ );

参考

https://stackoverflow.com/a/59314670/

https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html 的解释:

Specifies the build type on single-configuration generators (e.g. Makefile Generators or Ninja). Typical values include DebugReleaseRelWithDebInfo and MinSizeRel, but custom build types can also be defined.

Stack Overflow 回答里提到的 BetaTest 等其他构建类型应该就是上面所说的 custom build types.

在尽可能复现错误的情况下调试

使用 RelWithDebInfo?

考虑编译器 gcc 或者 clang:

说明

这里放的是我觉得没有必要专门写一篇文章的内容。

2024-07-06

V75K 不同的键有点高低不一致了,怀疑是定位板被我弄的不平了。数字 5、6、7、8 这几个位置不仅突起,而且手感还偏硬,估计是下面有电池(这同时意味着这个地方不应该再使劲按下去了,可能不安全);回车的位置下陷了,我觉得是装键帽使劲按回车导致的。

2024-07-07

  1. 鲸海轴一定要对准孔位小心插入。和其他轴体清脆一声按下去就是插好了不同,鲸海轴听到清脆一声按下去了可能是方向不正,有可能针脚都跪了。我已经跪两颗了。
  2. 卫星轴是调不好的,只有放弃强迫症安心用键盘。

2024-07-10

鲸海轴还是太重了,换回阿尼亚轴了。昨天晚上把键盘里面也拆了,定位板是有一点变形,怪不得键帽高高低低的。调是调不好了。又在可乐蛙的店铺买了特氟龙和空格卫星轴,其他店铺一般都是多个 2U 和一个 6.25U 捆绑销售的,这个店铺可以单买。

2024-07-13

  1. 还是觉得因为定位板变形导致键帽高低不平看着难受,下次换钢板定位板算了。
  2. 最好选准轴体不要自己换了。
  3. 避开短行程轴体的键盘。厂家可能为了更好的效果使用短行程的卫星轴,自己换长行程的轴体时大键按下去就会有异样的感觉。我的 V75K 大概率用的就是短行程卫星轴。我试了一下用 3.5±0.4mm 的鲸海轴和 3.5mm 的卫星轴(单独买的,不知道 V75K 标配卫星轴多长键程)也会有卡涩的感觉,说明轴体应该明显小于卫星轴键程。

eval $(ssh-agent -s)
ssh-add ~/.ssh/id_ed25519 # 根据实际情况操作
ssh -A server1
scp server2:/data /data

由于 sever1 可能没有把 server2 作为主机存储,在 scp 的时候需要输入一长串的用户名、主机、端口。

exec() 函数

SYNOPSIS
#include <unistd.h>

extern char **environ;

int execl(const char *pathname, const char *arg, ...
               /*, (char *) NULL */);
int execlp(const char *file, const char *arg, ...
               /*, (char *) NULL */);
int execle(const char *pathname, const char *arg, ...
               /*, (char *) NULL, char *const envp[] */);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]); /* GNU 扩展 */

/* 不知道为什么单独出来一个手册页,和上面没有放在一起? */
int execve(const char *pathname, char *const _Nullable argv[],
          char *const _Nullable envp[]);

函数命名规则:

  1. 其中 exec 前缀是一定有的。
  2. 然后用 lva_list 形式)或者 v(数组形式)表示命令行参数,这也是一定有的。
  3. (可选)如果允许在 PATH 中搜索(可执行)文件,而不是用绝对或相对路径搜索文件,则有 p 后缀。
  4. (可选)如果允许(以字符串数组的形式)设置环境变量,则有 e 后缀。

如果在设置环境变量的 exec() 系列系统调用中没有提及 PATH 环境变量,则使用默认 PATH。根据系统不同,默认的 PATH 可能不同,常见的 PATH.:/usr/bin:/bin。处于安全考虑,特权用户的 PATH 中常常没有 .(当前工作目录)。

If this variable isn’t defined, the path list defaults to a list that includes the directories returned by confstr(_CS_PATH) (which typically returns the value “/bin:/usr/bin”) and possibly also the current working directory

启用标记(例子:大小写不敏感搜索)

- 加上其他字符。比如 -i 表示开关大小写敏感搜索(ignore case in searches)。但是,这只对全小写的输入有用,如果查询的词中包含大写字母、又想要大小写不敏感搜索,则需要使用 -I(ignore case in searches and in patterns)。

这些标记不仅可以从命令行选项中设置,还能从 LESS 环境变量中读取,当然也能像上一段所说的那样在 less 已经打开后设置。Man 手册打开的 less 分页器似乎就是带有 -i 标志的。

Tip

在我的电脑上 vim/ 默认是大小写敏感,而 less 是默认带有 -i 标志。有些老的电脑上面 less 可能需要自己加标志。

如果想要在 vim 中使用大小写不敏感搜索,需要在搜索条目中加上 \c 转义,这个标志可以加在搜索条目的任何位置,参考 https://stackoverflow.com/a/2287449/ 。还有一种方式使用命令 :set ic 开启大小写不敏感搜索。

过滤行

先按 &(而不是 /)可以将输出过滤,只显示匹配的行。

_exit() 系统调用

虽然参数是 int 类型,但是只有低 8 位可用。而且由于终端中用信号值 + 128 表示进程因信号退出的状态码($?),所以最好也不要使用超过 127 的退出值。

exit() 函数

会做这几件事:

  1. 执行退出处理程序,这些程序是用 atexit()on_exit() 注册的。
  2. 刷新 stdio 流缓冲区。
  3. 调用 _exit()

由于在执行退出处理程序时,main() 函数已经退出,所以退出处理函数不能使用 main() 函数中定义的变量。

如果用户程序没有调用 _exit()exit(),并正常从 main() 函数中返回,则使用 main() 函数的返回值作为对 exit() 调用时传入的参数,这个过程是由运行时函数处理的。如果 main() 函数没有显式提供返回值,则在 C89 中属于未定义行为,在 C99/C++ 中会隐式返回 0。

子进程的状态

1. 状态的分类

  • 调用 _exit() 正常退出(无论是手动调用还是运行时在 main() 函数退出后自动调用)。
  • 收到信号而终止。
  • 收到信号而暂停执行。
  • 收到信号而恢复执行。

2. 如何在父进程中区分子进程状态

为了区分这 4 种状态,等待函数中 int 类型的输出参数实际上会有低 2 个字节被使用(虽然实际上返回状态只需要用 1 个字节表示):

可以分别通过 WIFEXITED (status)WIFSIGNALED (status)WIFSTOPPED (status)WIFCONTINUED (status) 来判断是否属于这几种情况。这几种情况是互斥的。在进程被信号所杀时,内核转储标志可能会被设置,这时如果宏 WCOREDUMP 被定义,可以用 WCOREDUMP 来检查内核转换标志是否被设置。

3. 在信号处理函数中终止子进程

如果需要对某个信号做出一定的处理再终止子进程,则不能调用 _exit() 函数,否则子进程会被视为正常终止。如果想要让子进程终止时可以反应真实的终止原因,需要在信号处理器函数中做出善后处理之后,再将该信号的处置方式设置成默认,然后用 raise() 系统调用发送相同的信号。

几个系统调用 / 库函数的介绍

  • fork():创建子进程。
  • exit(status):库函数,退出当前进程,是 _exit 的包装。
  • wait(&status):挂起当前进程并等待一个子进程。
  • execve(pathname, argv, envp):加载一个新的程序。

Tip

SUSv3 还规定了 posix_spawn() 函数,相当于 fork + exec,在有些实现中速度会快一些,有的实现中则不是。网上对此的讨论也很多。

fork 和文件共享

fork 后父子进程是可以共享打开的文件描述符的,而且这些文件描述符都指向内核打开文件表的同一个项目,共享文件属性和偏移量。不过可以在打开文件时设置 O_CLOEXEC 选项,使得子进程将来调用 exec 时关闭文件。

vfork 和写时复制

早前 fork 函数没有 copy-on-write 功能,因而在子进程要调用 exec 时,显得不划算。vfork 让父子进程共享内存,只是复制极小部分用来管理进程的数据,并且将父进程挂起,直到子进程调用 exec 或者 _wait 才恢复。如果子进程在此期间做出了别的行为,则行为未定义。

这里的别的行为指:1. 修改了除了 vfork 返回值以外的任何数据;2. 从调用 vfork 的函数中返回;3. 在调用 exec / _wait 之前调用了任何函数。