0%

挂载基础

挂载首先是需要管理员权限的,而且必须是目标已经存在才能挂载并覆盖它。然后 mount 命令是挂载,umount 是解除挂载。

Linux 中有三个文件包含了挂载信息:

  1. /proc/mounts 包含了文件系统的挂载信息。因为 Linux 现在支持了每个进程有不同于系统的文件系统,所以 /proc/mounts 被实现为指向 /proc/self/mounts 的一个符号链接。
  2. /etc/mtab 文件由 mount(8) 和 umount(8) 来维护。
  3. /etc/fstab 由系统管理员来维护,其中指定的挂载项目会在 systemd 启动其他服务前执行。

创建 tmpfs 文件系统

tmpfs 并不只是把数据写到内存那么简单,它也是一个虚拟文件系统,因而在内存不足时,依然可以将文件写到交换中。tmpfs 除了用来把内存作为硬盘加速计算之外,也被用于 System V 共享内存和共享匿名内存映射、POSIX 共享内存和 POSIX 信号量。

以下代码是创建 tmpfs 文件系统并挂载到 /tmp。这里的 mytmpfs 是挂载源,但是因为是 tmpfs,所以这里只充当名称的作用,并不是从这个路径挂载。

总览

700

和时间相关的很多函数都是不可重入的。它们修改同一块静态分配的内存区域并返回,因此不是线程安全的。在新版本已经有 *_r 这样的可重入函数来替代它们。

timeval 精确时间,微秒级

struct timeval 是带有秒和微秒的结构体,可以用 gettimeofday 获取,gettimeofday 的第二个参数 struct timezone_t * 建议填 NULL

#include <sys/time.h>

struct timeval {
   time_t       tv_sec;   /* Seconds */
   suseconds_t  tv_usec;  /* Microseconds */
};

time_t 日历时间,秒级

C 标准库 time 函数获得的秒就是 timeval 结构体的一个分量。尽管 timeval 信息更全面,在分解和格式化时间的时候都只支持了秒级别(time_t)。

首先,更新系统时间要求程序具有相应的权限,即 CAP_SYS_TIME

settimeofday 允许我们以 timeval 结构体表示的时间设置系统时间,不过,虽然它的精度很高,但是我们的计时器不一定能够达到这么高的精度,因此在微秒级设定的时间也可能是不准确的。

stime 类似,但是只能设置秒级时间。

adjtime 函数则是可以减慢或者加快始终速度,从而温和地完成时间调整。不过 adjtime 在执行的时间里可能无法完成所有的时间调整,这个时候剩余未修改的时间将保存在第二个参数中。(settimeofday 可能会突然改变系统时间,从而使得依赖绝对时间戳工作的程序不能正常运行,比如数据库或者 make。)

什么是进程时间

进程时间包含:

  1. CPU 在用户模式下消耗的时间。
  2. CPU 在内核模式下消耗的时间(比如 I/O、页错误处理)。

times 系统调用

#include <sys/times.h>

clock_t times(struct tms *buf);

其中 tms 结构体包含了当前进程和等待的子进程的时间信息。“等待的子进程”含义是:只有调用 wait 或者 waitpid 回收了一个子进程,子进程的时间才会累加到当前进程的 tms_cutimetms_cstime 上。

struct tms {
   clock_t tms_utime;  /* user time */
   clock_t tms_stime;  /* system time */
   clock_t tms_cutime; /* user time of children */
   clock_t tms_cstime; /* system time of children */
};

可以从哪些地方获取系统限制信息?

  1. limits.h 头文件中定义的限制。
  2. sysconf 获取的限制,这些限制在进程运行期间保持恒定。获取限制一般使用 _SC 开头的宏作为参数。
  3. pathconffpathconf 获取的限制,这些限制在进程运行期间是可以改变的。获取限制一般使用 _PC 开头的宏作为参数。

pid_t 类型的表示上限

在我的 wsl 中来看,pid_t 这个数据类型占用 4 个字节,是一个有符号整数(实际上是 int),我这里 /proc/sys/kernel/pid_max 显示可用的最大 pid 号为 4194304,即 $2^{22}$。这其实和 PID_MAX_LIMIT 相等,不可能将 pid_max 调整得更大,但是可以小于这个值。在 32 位机器上,这个系统限制最大是 32768,即 $2^{15}$。最大可用的 pid 值比文件中的值小 1,见后文。

参考 https://unix.stackexchange.com/a/162105/

$ sudo sysctl -w kernel.pid_max=4194303  # 改小可以
kernel.pid_max = 4194303
$ sudo sysctl -w kernel.pid_max=4194305  # 改大不行
sysctl: setting key "kernel.pid_max": Invalid argument
$ sudo sysctl -w kernel.pid_max=4194304  # 改回默认值
kernel.pid_max = 4194304

直接向 /proc/sys/kernel/pid_max 里面 echo 也是可以的,只是 sudo 给命令的提权并不会作用于当前 shell 的重定向符号(重定向使用的权限是当前的 shell 的)。这种情况下可以用 ... | sudo tee,也可以用 sudo bash -c '...'echo 和重定向包在内部。

在 Linux/UNIX 系统编程手册中看到这样的一段代码:

(py310) xxx /data/apue $ file /proc/self/fd/0
/proc/self/fd/0: symbolic link to /dev/pts/2
(py310) xxx /data/apue $ file /proc/self/fd/0 < .
/proc/self/fd/0: symbolic link to /data/apue

这说明目录也是能够被重定向并使用 0 号 fd 打开的,只要程序支持标准输入流不是普通文件就行。

/proc 下的进程信息

这些信息都是以 /proc/PID 开头的,其中 PID 为进程自己的进程号。用 /proc/self 可以表示进程本身。

在内核 2.4 之后,Linux 增加了线程组概念,正式支持了 POSIX 线程模型。/proc/PID/task/TID 是进程号为 PID 的进程中线程号为 TID 的线程的信息文件夹。

由于 $$ 表示当前 shell 的进程号,而一个进程中的主线程号和进程号相同,我们可以用以下方式来访问当前 shell 主线程的文件夹:

(py310) xxx /data/apue $ ls /proc/$$/task/$$
arch_status  cgroup      cmdline  cwd      fd       io        maps       mounts  oom_adj        pagemap      root       sessionid  smaps_rollup  statm    uid_map
attr         children    comm     environ  fdinfo   limits    mem        net     oom_score      personality  sched      setgroups  stack         status   wchan
auxv         clear_refs  cpuset   exe      gid_map  loginuid  mountinfo  ns      oom_score_adj  projid_map   schedstat  smaps      stat          syscall

C 语言标准库的缓冲

C 语言输入输出函数会将数据缓冲到用户区域,从而减少了系统调用次数

可以用函数 setvbuf 来改变一个 FILE * 的缓冲方式:

int setvbuf(FILE *restrict stream, char buf[restrict .size],
           int mode, size_t size);

其中 mode 有三种:

  • _IONBF: unbuffered
  • _IOLBF: line buffered
  • _IOFBF: fully buffered