Linux 文件系统配置

硬件

磁盘挂载

/etc/fstab

/etc/fstab:包含存储设备、文件系统信息,配置自动挂载 各种文件系统格式硬盘、分区、可移动设备、远程设备 (即mount参数存盘)

1
<fs> <mountpoint> <type> <opts> <dump> <pass>
  • <fs>:挂载设备/分区名

    • /dev/sda:设备/分区名
    • UUID=xxxxx:使用设备UUID值表示设备
    • tmpfs:tmpfs分区,默认被设置为内存的一半(可在 <opts>中添加size=2G指定最大空间)
    • 所有设备/分区都有唯一UUID,由文件系统生成工具mkfs. 创建文件系统时生成
  • <mountpoint>:挂载点,路径名(文件夹)

    • /
    • /boot
    • 路径名中包含可以空格使用\040(8进制)表示
  • <type>:文件系统类型

    • ext2
    • ext3
    • reiserfs
    • xfs
    • jfs
    • iso9660
    • vfat
    • ntfs
    • swap
    • tmpfs:临时文件系统,驻留在交换分区、内存中
      • 提高文件访问速度,保证重启时自动清除这些文件
      • 常用tmpfs的目录:/tmp/var/lock/var/run
    • auto:由mount自动判断
  • <opts>:文件系统参数

    • noatime:关闭atime特性

      • 不更新文件系统上inode访问记录,提高性能,否则 即使从缓冲读取也会产生磁盘写操作
      • 老特性可以放心关闭,能减少loadcycle
      • 包含nodiratime
    • nodiratime:不更新文件系统上目录inode访问记录

    • relatime:实时更新inode访问记录,只有记录中访问 时间早于当前访问才会被更新

      • 类似noatime,但不会打断其他程序探测,文件在 上次访问后是否需被修改(的进程)
    • auto:在启动、终端中输入$ mount -a时自动挂载

    • noauto:手动挂载

    • ro:挂载为自读权限

    • rw:挂载为读写权限

    • exec:设备/分区中文件可执行

    • noexec:文件不可以执行

    • sync:所有I/O将以同步方式进行

    • async:所有I/O将以异步方式进行

    • user:允许任何用户挂载设备,默认包含 noexec,nosuid,nodev(可被覆盖)

    • nouser:只允许root用户挂载

    • suid:允许set-user/group-id(固化权限)执行

      • set-user/group-id参见linux/shell/config_files
    • nosuid:不允许set-user/group-id权限位

    • dev:解释文件系统上的块特殊设备

    • nodev:不解析文件系统上块特殊设备

    • umask:设备/分区中文件/目录默认权限掩码

      • 权限掩码参见linux/kernel/file_system.md
    • dmask:设备/分区中目录默认权限掩码
    • fmask:设备/分区中普通文件默认权限掩码

    • nofail:设备不存在则直接忽略不报错

      • 常用于配置外部设备
    • defaults:默认配置,等价于 rw,suid,exec,auto,nouser,async

  • <dump>:决定是否dump备份

    • 1:dump对此文件系统做备份
    • 0:dump忽略此文件系统
    • 大部分用户没有安装dump,应该置0
  • <pass>:是否以fsck检查扇区,按数字递增依次检查(相同 则同时检查)

    • 0:不检验(如:swap分区、/proc文件系统)
    • 1:最先检验(一般根目录分区配置为1
    • 2:在1之后检验(其他分区配置为2
  • /etc/fstab是启动时配置文件,实际文件系统挂载是记录到 /etc/mtab/proc/mounts两个文件中

  • 根目录/必须挂载,必须先于其他的挂载点挂载

文件系统配置

Ext 配置文件

  • /etc/mke2fs.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[defaults]
base_features = sparse_super,large_file,filetype,resize_inode,dir_index,ext_attr
default_mntopts = acl,user_xattr
enable_periodic_fsck = 0
blocksize = 4096 # 块大小
inode_size = 256 # Inode 大小
inode_ratio = 16384 # 分配 Inode 号间隔

[fs_types]
ext3 = {
features = has_journal
}
ext4 = {
features = has_journal,extent,huge_file,flex_bg,metadata_csum,64bit,dir_nlink,extra_isize
inode_size = 256
}

[options]
fname_encoding = utf8

Linux 文件系统命令

文件系统状态

dudf

目录、文件操作

pwd

pwd:显示当前工作目录绝对路径

cd

cd:更改工作目录路径

  • 缺省回到用户目录
  • -:回到上个目录

ls

1
$ ls [params] expr

列出当前工作目录目录、文件信息

参数

  • -a:列出所有文件,包括隐藏文件
  • -l:文件详细信息
    • 详细信息格式、含义参见config_file
  • -t:按最后修改时间排序
  • -S:按文件大小排序
  • -r:反向排序
  • -h:显示文件大小时增加可读性
  • -F:添加描述符到条目后
    • @:符号链接
    • *:文件
    • /:目录
  • -i:显示索引节点

输出结果

ls_results.png

  • 文件权限:包括10个字符

    • 第1字符:文件类型
      • -;普通文件
      • d:目录
      • l:link,符号链接
      • s:socket
      • b:block,块设备
      • c:charactor,字符设备(流)
      • p:FIFO Pipe
    • 第2-4字符:owner,文件属主权限
    • 第5-7字符:group,同组用户权限
    • 第8-10字符:other,其他用户权限
      • 权限分别为r读、w写、x执行
      • 相应位置为-表示没有此权限
    • 执行位还可能是其他特殊字符
      • users:文件set-user-id、执行权限同时被置位
      • groups:文件set-group-id、执行权限同时被置位
      • userS:文件set-user-id被置位,执行权限未置位
      • groupS:文件set-group-id被置位,执行权限未置位
      • othert:文件sticky bit、执行权限均被置位
      • otherT:文件sticky bit被置位、执行权限未置位
    • 关于权限具体含义,参见linux/kernel/file_system
    • 权限设置,参见linux/shell/cmd_fds
  • 文件数量

    • 一般文件:硬链接数目
    • 目录:目录中第一级子目录个数
  • 文件属主名

  • 文件属主默认用户组名

  • 文件大小(Byte)

  • 最后修改时间

  • 文件名

dirs

显示目录列表

touch

创建空文件或更改文件时间

mkdir

创建目录

rmdir

删除空目录

cp

复制文件和目录

mv

移动、重命名文件、目录

rm

删除文件、目录

  • 删除目录link时,注意末尾不要带/,否则被认为是目录, 此时rm -r <target>会删除源目录中文件

file

查询文件的文件类型

du

显示目录、文件磁盘占用量(文件系统数据库情况)

参数

  • -a/--all:显示所有后代各文件、文件夹大小
    • 否则默认为显示所有后代文件夹大小
  • -c/--total:额外显示总和
  • -s/--summarize:仅显示总和
  • --max-depth=[num]:显示文件夹的深度
  • -S/--separate-dirs:文件夹大小不包括子文件夹大小

  • -b/--bytes:以byte为单位

  • -k/--kilobytes:以KB为单位
  • -m/--megabytes:以MB为单位
  • -h:human-readable,提升可读性
  • -H/--si:同-h,但是单位换算以1000为进制

  • -x/--one-file-system:以最初处理的文件系统为准,忽略 其他遇到的文件系统

  • -L=/--dereference=:显示选项中指定的符号链接的源文件 大小
  • -D/--dereference-args:显示指定符号链接的源文件大小
  • -X=/--exclude-from=[file]:从文件中读取指定的目录、 文件
  • --exclude=[dir/file]:掠过指定目录、文件
  • -l/--count-links:重复计算hard-link

wc

统计文件行数、单词数、字节数、字符数

-    `-l, -w, -c`

tree

树状图逐级列出目录内容

cksum

显示文件CRC校验值、字节统计

mk5sum

显示、检查MD5(128bit)校验和

sum

为文件输出校验和及块计数

dirname

输出给出参数字符串中的目录名(不包括尾部/) ,如果参数中不带/输出.表示当前目录

basename

输出给出参数字符串中的文件、目录

ln

创建链接文件

stat

显示文件、文件系统状态

文件、目录权限、属性

chown

更改文件、目录的用户所有者、组群所有者

chgrp

更改文件、目录所属组

umask

显示、设置文件、目录创建默认权限掩码

getfacl

显示文件、目录ACL

setfacl

设置文件、目录ACL

chacl

更改文件、目录ACL

lsattr

查看文件、目录属性

chattr

更改文件、目录属性

umask

查看/设置权限掩码,默认0000

1
2
3
4
5
6
$ umask
# 数字形式返回当前权限掩码
$ umask -S
# 符号形式返回当前权限掩码
$ umask 0003
# 设置权限掩码为`0003`
  • 权限掩码参见linux/kernel/permissions

chmod

关于文件、目录权限参见config_files###文件描述

  • 普通用户只能修改user权限位
  • root用户可以修改任意用户、任意文件权限

参数

  • -R:对整个目录、子文件(目录)同时修改权限

操作

1
2
$ chmod [ugoa][+-=][rwxst] file
$ chmod xxxx file
  • ugoa:分别表示user、group、other、all权限位
  • +-=:表示增加、减少、设置权限
  • rwxst:表示5不同的权限

    • ST不是一种权限,只是一种特殊的状态
    • 设置状态时s时,是根据相应的x是否有确定s/S
    • 设置状态t同理
  • xxxx:每个8进制数表示一组权限,对应二进制表示相应权限 是否置位

    • 第1个数字:set-user-id、set-group-id、sticky bit
    • 后面3个数字分别表示user、group、other权限
    • 第1个数字位0时可以省略(常见)

示例

1
2
$ chmod u+s file1
$ chmod 7777 file1

磁盘

df

文件系统信息

fdisk

查看系统分区

mkfs

格式化分区

fsck

检查修复文件系统

mount

查看已挂载的文件系统、挂载分区

umount

卸载指定设备

free

查看系统内存、虚拟内存占用

Ext 文件系统

Ext 文件系统

Ext 文件系统结构

linux_file_system_ext_structure

Index Node

Index Node/Inode 索引节点:记录文件的元信息

  • 文件元信息

    • 文件大小
    • 访问权限
    • 创建时间
    • 修改时间
    • 数据块位置
  • Inode 是文件的唯一标识,和文件一一对应

    • 存储在磁盘中,占用磁盘空间,大小为 126B 整数倍
    • 通常会把索引节点加载到内存中,加速文件的访问
  • Inode 并未存储 Inode 号、文件名,而是存储在文件所在目录的数据块中

Inode

  • Inode 号在同一文件系统内部唯一,可以、且被用于确定、定位 Inode

    • 创建文件系统时,每个块组中起始 Inode 号、Inode Table 起始地址确定
      • Inode 号按分配比率(间隔字节数)预分配
      • 若系统中大文件较多,Inode 分配比率应较大,避免 Inode Table 占用过大空间
    • 根据 Inode 号、Inode 结构大小计算偏移即可查确定
  • Ext 预留部分 Inode 作特殊用途

    • 0:不存在,用于标识已删除文件
    • 1:虚拟文件系统,如:/proc/sys
    • 2:根目录
    • 3ACL 索引
    • 4ACL 数据
    • 5boot loader
    • 6:未删除的目录
    • 7Reserved GDT
    • 8:日志
    • 11:首个非预留 Inode 号,通常是 lost+found 目录
  • Inode 大小默认值在 /etc/mke2fs.conf 文件中指定
  • ls -i 可查看文件 Inode
  • find 可以使用 -inum 参数寻找指定 Inode 号文件

Inode 寻址

  • Ext2/3Inode 寻址混合直接查找、多级索引
  • Inode 中包含 15 个指针 i_block[0..14],分为 4 级

    • i_block[0..11]:直接寻址指针,直接指向数据块
    • i_block[12]:一级间接寻址指针,指向存储指针的块
    • i_block[13]:二级间接寻址指针
    • i_block[14]:三级间接寻址指针
  • 根据文件大小选择不同的寻址方法

    • 文件小于 12block 时,直接用直接寻址指针
    • 文件较大时则利用更高级别的间接寻址指针,多级索引
    • Ext2/3 对超大文件存取效率低下,需要核对指针过多

    linux_file_system_storage_ext

Inode 内容

  • 硬链接:指向 Inode(文件) 的 目录项

    • 此处文件、硬链接说明
      • 文件:Inode、相应数据块
      • 硬链接:目录文件中目录项
    • 硬链接是目录项
      • 同一个文件的多个硬链接应是仅文件名不同的目录项
      • Ext 文件系统中 Inode 号、文件名均在存储在目录项中即完美支持硬链接
  • 硬链接创建、删除

    • 创建硬链接会增加文件的硬链接数
      • 不能跨分区创建硬链接:不同分区 Inode 号会重复
      • 不能手动对目录文件创建硬链接:防止路径混乱,文件系统已经为目录创建硬链接
        • .:当前目录硬链接
        • ..:上级目录硬链接
      • 目录的硬链接数 = 2 + 一级子目录数
        • 父目录中目录项
        • 自身 . 目录项
        • 子目录中 .. 目录项
    • 删除文件实质上就是删除硬链接,文件的硬链接数量归 0 时才被真正删除
    • ls -l 中第二行即为文件的硬链接数(包含文件自身)
  • / 根目录是自引用的(唯一),即 .. 也指向自身
  • 符号链接 / 软链接:通过文件名的链接文件的 文件
    • 符号链接是文件
      • 文件内容可认为是指向的目标路径,这也决定温文件大小
      • 符号链接文件本身也可以有多个硬链接、符号链接
    • 一般不占用数据块,Inode 记录即可描述完成
      • 只有符号链接指向的目标路径过长(大于 60B)时才会分配数据块
    • 符号链接权限不重要,取决于最终目标文件
  • readlink 可以查看符号链接之

设备文件、FIFOSocket

  • 设备文件、FIFOSocket
    • 没有大小,不占用数据块,在 Inode 记录中即可描述完成
      • 主设备号:标识设备类型
      • 次设备号:标识同种设备类型的不同编号

Block Group

Block Group 块组:逻辑上对块分组,提高查询效率

  • 块组划分是文件系统创建的一部分

    • 一个磁盘分区包含多个块组
    • 块组是逻辑层面的划分,不会类似分区在磁盘上标记、划分
  • 每个块组包含多个元数据区、数据区

    • 元数据区:存储 BmapInode TableImap 等数据
    • 数据区:存储文件数据
  • 块组特点

    • 块组大小(包含块数)= 块 bit 数,即单个 block (作为)Bmap 能标记的块数量
      • 此大小包含元数据区(也需要 Bmap 标记是否被占用)
    • 块组设置的 Inode 数量、Inode Table 由系统决定

分区级 Block

  • 以下这些块不会出现在所有块组中,存储文件系统级别信息

Boot Block

  • Boot Block / Boot Sector:存放有 boot loader 的块

    • 特点
      • 位于分区的首个块
      • 占用 1024B
      • 只有装有系统的主分区、逻辑分区才有 Boot Block
    • Boot Block 在不同分区时称为
      • 主分区装有操作系统时:Volume Boot Records
      • 逻辑分区装有操作系统时:Extended Boot Records
  • MBR 会引导 VBR/EBR,开机启动时,首先加载 MBRboot loader

    • 单操作系统时,直接定位到所在分区的 Boot Block,加载此处的 boot loader
    • 多操作系统时,加载 MBRboot loader 后列出操作系统菜单,指向各分区的 boot block

    linux_file_system_ext_super_block_mbr

    • 通过 MBR 管理启动菜单方式已经被 Grub 取代

Super Block

  • Super Block:存储文件系统信息、属性元数据

    • 存储的信息包括
      • 块组的 block 数量、Inode 号数量
      • 文件系统本身的空闲 block 数量、Inode 数量
      • 文件系统本身的属性信息:时间戳、是否正常、自检时间
    • (首个)超级块的位置取决于块大小
      • 块大小为 1KB 时,引导块正好占用 1 个 block,则超级块号为 1
      • 块大小大于 1KB 时,超级块和引导块均位于 0 号块
  • 超级块对文件系统至关重要

    • 超级块丢失和损坏必然导致文件系统损坏
    • Ext2 只在 0、1 和 3、5、7 的幂次块组中保存超级块信息

      • 但文件系统只使用首个块组的超级块信息获取文件系统属性,除非损坏或丢失
      • 有些旧式文件系统将超级块备份至每个块组
  • df 命令读取文件系统的超级块,统计速度快

Group Descriptor Table

  • GDT 块组描述符表:存储块组的信息、属性元数据

    • Ext 每个块组使用 32B 描述,被称为块组描述符,所有块组描述符组成 GBT
      • GDT 和超级块同时出现在某些块组中
      • 默认也只会读取 0 号块组的中 GDT
  • Reserved GDT:保留作为 GDT 使用的块(扩容之后块组增加)

    • GDT、超级块同时出现,同时修改
  • GDTReserved GDT、超级块在某些块组同时出现,能提升维护效率

块组级 Block

Block Bitmap

Block Bitmap/Bmap 块位图:标记各块空闲状态

  • Bmap 只优化写效率
    • 向磁盘写数据时才需要寻找空闲块,读数据时按照索引读取即可
    • Bamp 查询速度足够快,则向磁盘写数据效率极大取决于磁盘的随机读写效率

Inode Table

  • Inode Table:物理上将多个 Inode 合并存储在块中
    • Inode 大小一般小于块大小,合并存储能节约存储空间

Inode Bitmap

Inode Bitmap/Imap 位图:标记各 Inode 号占用状态

Data Blocks

  • Data Blocks:直接存储数据的块
    • 数据占用的块由文件对应 Inode 记录中块指针找到
    • 不同类型文件在数据块中存储的内容不同
      • 常规文件:存储文件数据
      • 目录:存储目录下文件、一级子目录
      • 符号链接:目标路径名较短则直接存储在 Inode 中,否则分配数据块保存

目录文件数据块

linux_file_system_ext_data_block_directory

  • 目录文件数据块存储多条目录项,每条目录项包含目录下
    • 文件 Inode
    • 目录项长度 rec_len
    • 文件名长度 name_len
    • 文件类型
      • 0:未知
      • 1:普通文件
      • 2:目录
      • 3:*character devicev
      • 4block device
      • 5:命名管道
      • 6socket
      • 7:符号链接
    • 文件名:文件名、一级子目录名、...

Directory Entry

Directory Entry/Dentry 目录项(缓存):存放内存中的缩略版磁盘文件系统目录树结构

  • Dentry 中需要记录

    • 文件名称
    • Inode 指针:与文件名建立映射关系
    • 与其他目录项的层级关联关系
      • 包括:父目录、子目录链表
      • 多个目录项通过指针关联起来就形成目录结构
  • Dentry 是由内核维护,缓存在内存中

    • 内核会把读过的文件用 Dentry 缓存在内存中,提高文件系统效率
  • InodeDentry 是一对多的关系

    • 即一个文件可以有多个别字
    • 硬链接实现就是多个 Dentry 中的 Inode 指向同一个文件

文件系统挂载

  • Mount 挂载:将文件系统关联到路径

  • 文件系统必须要挂载在一定路径下才能被使用

    • 文件系统体现在系统中即目录,即其文件系统的入口目录(根目录)
    • 而入口目录无名、无显式 Inode
      • Ext 中文件名、Inode 号存储在父目录中,入口目录是文件系统最底层目录,不存在父目录
      • 入口目录无名,所以挂载在任何目录下都是合理的
      • 入口目录被预留 Inode 号为 2,可直接寻址
  • 挂载方式

    • / 根目录下挂载根文件系统,在系统启动之初即挂载
    • 其余文件系统则挂载在根文件系统的目录之下

挂载逻辑

  • 挂载实现逻辑

    • 新建 Inode,将其寻址指针指向待挂载文件系统
    • 将挂载点目录 Inode 标记为不可用
    • 修改挂载点目录在其父目录目录项至新建 Inode
      • 挂载期间原目录会被遮蔽
      • 挂载点仍然是所在文件系统的文件,但是其数据不在
  • 同步挂载信息

    • 挂载完成后,将挂载记录、相关信息写入 /proc/self/{mounts, mountstats, mountinfo}
    • 同步 /proc/self/mounts 同步至 /etc/mtab(若有必要)

文件操作

文件读取

  • / 文件系统在系统启动时即挂载,此时已经读取超级块、GDT 等文件系统块
  • 同文件系统内 / 开头绝对地址

    • 根据 GDT 确定各块组 Inode Table 块号
    • Inode Table 中查找 / 目录文件 Inode
      • / Inode 号已知为预留 Inode 号 2,可直接在 Inode Table 中定位
    • 获取 / 数据块,并读取其中目录项
    • 在目录项中查找目标记录,获取文件 Inode
    • 如上重复,直到找到目标文件 Inode,根据 i_block 寻址指针读取数据块
  • 相对地址

    • 按照所处目录的目录项获取 Inode 号,同绝对地址即可
  • 跨文件系统地址

    • 类似同文件系统
    • 但挂载点目录会指向目标文件系统入口目录,再同绝对地址即可

文件删除

  • 删除普通文件

    • 同读取找到文件 Inode、数据块
      • 将文件 Inode 硬链接数量减一
      • 若硬链接数量归 0,执行删除,否则不变
    • Inode 中寻址指针删除
      • 此时即无法找到文件数据
    • Imap 中标记文件 Inode 号为未使用
    • 删除文件所属目录的目录项
      • 实务中会将目录项 Inode 号标记为 0,避免产生空洞
      • 此时文件即不可见
    • Bmap 中文件数据块块号标记为未使用
      • 此时即释放文件占用空间,若此时有其他进程持有数据块的指针,则文件系统不会立即释放该空间
      • Ext 系统中此步骤会导致删除大文件效率低
  • 删除目录文件

    • 若目录非空,则尝试递归删除其中文件、子目录
    • 若目录为空,类似普通文件删除目录

文件移动、重命名

  • 同目录文件重命名:修改文件所属目录的目录项中文件名

  • 同文件系统下移动:增、删目录项

    • 文件移动不修改 InodeInode 号等
  • 不同文件系统下移动:先复制、再删除

  • 命名冲突时,覆盖会删除冲突文件,并修改相应目录项至新文件
  • 因此,Ext 无法用同名子目录覆盖父目录,在尝试删除父目录时即失败

文件存储、复制

  • 文件存储

    • 读取 GDT,寻找空闲块组
    • 根据块组 Imap 为文件分配未使用 Inode
    • Inode Table 中完善 Inode 中文件元数据
    • 在所属目录中添加目录项
    • 将数据写入数据块
      • Ext2/3 中每次调用 block 分配器为数据分配 1 个数据块,直至写入完毕
      • Ext4 中允许一次分配多个数据块
    • Inode Table 中更新 Inode 寻址指针
  • 文件复制同文件存储

Ext2/3/4 迭代

  • Ext 文件系统特点
    • 在创建时即划分好,方便使用时分配
      • 不支持动态划分、分配
      • 格式化超大磁盘时较慢

Ext3 日志功能

  • Ext2 中只有两个区:元数据区、数据区

    • 从数据块中写入数据的中断中恢复检查一致性需要大量时间,甚至失败
  • Ext3 增加日志区

    • 在向数据块中写入数据前会在日志区标记
    • 则根据日志区的标记即可判断操作完成情况,提高一致性确认效率

Ext4 段分配

  • Ext2/3

    • Bmap 标记、分配块能提高效率,但扫描 Bmap 效率仍很低
    • 多级索引寻址效率低
  • Ext4 中使用 extent 管理数据块

    • extent 尽可能包含物理上连续的块
    • Inode 中使用 4 个 extent 片段流替代多级索引指针
      • 每个 extent 片段流设定起始块号、块数量
      • extent 指向的块保存数据或索引指针
    • 支持调用一次 block 分配器分配多个块,并标记对应 Bmap

    linux_file_system_ext4_extent_structure

  • Ext 删除数据

    • 会依次释放 Bmap 位、更新目录结构、释放 Inode 空间

Nginx 使用、配置

配置

  • 配置文件夹为/etc/nginx

基本配置

nginx.conf

nginx.conf:Nginx主配置文件,包含“全部配置”

  • 默认include

    • modules-enabled/*.conf:已启用模块
    • mime.types:代理文件类型
    • conf.d/*.conf:服务器设置
    • sites-enabled/*:已启用站点
  • user表示启动nginx进程的用户

    • 键值默认为www-data
    • 可修改为其他用户,使nginx具有访问某些文件的权限
    • 若是在nginx启动之后修改,需修改/var/log/nginx中 log文件的属主,否则无法正常log

http服务器设置

1
2
3
4
5
6
7
8
9
10
server{
listen 8080;
server_name localhost;
location / {
root /home/xyy15926/Code;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
}
  • root:需要nginx进程可访问,否则403 Forbidden
  • autoindex:自动为文件生成目录
    • 若目录设置index index.XXX,不设置autoindex访问 目录则会403 Forbidden

https服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server{
listen 443 ssl;
server_name localhost;
ssl_certificate /home/xyy15926/Code/home_config/nginx/localhost.crt;
ssl_certificate_key /home/xyy15926/Code/home_config/nginx/localhost.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
location / {
root /home/xyy15926/Code/statics;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
}
  • ssl_certificatessl_certificate_key:SSL证书、私钥

    1
    2
    3
    4
    5
    6
    # 个人签发https证书,个人一般不会是受信任机构,所以还需要
    # 将证书添加进受信任机构(但是windows下有些有问题)
    openssl req -x509 -out localhost.crt -keyout localhost.key \
    -newkey rsa:2048 -nodes -sha256 \
    -subj '/CN=localhost' -extensions EXT -config <( \
    printf "[dn]\nCN=localhost\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:localhost\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth")

运行

1
2
3
 # Nginx启动、重启动
$ /etc/init.d/nginx start
$ /etc/init.d/nginx restart

Linux System Call

Kernel

内核:提供硬件抽象层、磁盘及文件系统控制、多任务等功能的系统 软件

  • 内核是操作系统的核心、基础,决定系统的性能、稳定性

    • 内核单独不是完整的操作系统
  • 内核为应用程序提供对硬件访问

    • 应用程序对硬件访受限
      • 内核决定程序何时、对何种硬件操作多长时间
    • 内核提供硬件抽象的方法隐藏对硬件操作的复杂
      • 为应用程序和硬件提供简洁、统一的接口
      • 简化程序设计

内核功能

  • 进程管理:实现了多个进程在CPU上的抽象

    • 负责创建、销毁进程,处理它们和外部输入、输出
    • 处理进程之间通讯
      • 信号
      • 管道
      • 通讯原语
    • 调度器控制进程如何共享CPU
  • 内存管理:内存是主要资源,对其管理策略对性能影响非常重要

    • 为所有进程在有限资源上建立虚拟寻址空间
    • 内核不同部分与内存管理子系统通过函数调用交互,实现 mallocfree等功能
  • 文件管理:Linux很大程度上基于文件系统概念,几乎任何东西 都可以视为是文件

    • 在非结构化硬件上建立了结构化文件系统
    • 支持多个文件系统,即物理介质上的不同数据组织方式
  • 驱动管理

    • 除CPU、内存和极少的硬件实体外,基本设备控制操作都由 特定的、需寻址的设备相关代码(设备驱动)进行
    • 内核中必须嵌入系统中出现每个外设驱动
  • 网络管理

    • 网络必须由系统管理
      • 大部分网络操作不是特定于某个进程:进入系统的报文 是异步事件
      • 系统在进程接手报文前收集、识别、分发,在程序和 网络接口间递送数据报文,根据程序的网络活动控制 程序执行
    • 路由、地址解析也在内核中实现

System Call

系统调用:操作系统提供的实现系统功能的子程序、访问硬件资源 的唯一入口

  • 系统调用是用户空间进程访问内核、硬件设备的唯一手段

    • 用户空间进程不能直接访问内核、调用内核函数
    • 对计算机硬件资源的必须经过操作系统控制
      • 计算机系统硬件资源有限,多个进程都需要访问资源
  • 系统调用与硬件体系结构紧密相关

    • 在用户空间进程和硬件设备之间添加中间层,是二者沟通的 桥梁
    • 是设备驱动程序中定义的函数最终被调用的一种方式

syscall_routine_procedure

系统调用意义

  • 用户程序通过系统调用使用硬件,简化开发、移植性

    • 分离了用户程序和内核的开发
      • 用户程序忽略API具体实现,仅借助其开发应用
      • 内核忽略API被调用,只需关心系统调用API实现
    • 为用户空间提供了统一硬件抽象接口,用户程序可以方便在 具有相同系统调用不同平台之间迁移
  • 系统调用保证了系统稳定和安全

    • 内核可以基于权限和其他规则对需要进行的访问进行 裁决
    • 避免程序不正确的使用硬件设备、窃取其他进程资源、 危害系统安全
  • 保证进程可以正常运行在虚拟寻址空间中

    • 程序可以随意访问硬件、内核而对此没有足够了解, 则难以实现多任务、虚拟内存
    • 无法实现良好的稳定性、安全性

通知内核

  • 大部分情况下,程序通过API而不是直接调用系统调用
  • POSIX标准是Unix世界最流行的API接口规范,其中API和系统 调用之间有直接关系,但也不是一一对应

系统调用表

系统调用表sys_call_table:其中元素是系统调用服务例程的 起始地址

1
2
3
4
5
// arch/x86/entry/syscall_64.c
asmlinkage const sys_call_ptr sys_call_table[__NR_syscall_max+1] ={
[0 ... __NR_syscall_max] = &sys_ni_syscall,
#include <asm/syscalls_64.h>
};
  • ...是GCCDesignated Initializers插件功能,此插件允许 无序初始化元素值
  • sys_call_table是长为__NR_syscall_max+1的数组

  • __NR_syscall_max是给定系统架构所允许的最大系统调用 数目

    1
    2
    // include/generated/asm-offsets.h
    #define __NR_syscall_max 547
    • 此数值必然和arch/x86/entry/syscalls/syscall_64.tbl 中最大系统调用数值相同
  • sys_call_ptr是指向系统调用表指针类型

    1
    typedef void (*sys_call_ptr_t)(void);
  • sys_ni_syscall是返回错误的函数

    1
    2
    3
    asmlinkage long sys_ni_syscall(void){
    return -ENOSYS;
    }
    • 未在<asm/syscalls_64.h>定义系统调用号将会对应此 响应函数,返回ENOSYS专属错误
  • <asm/syscalls_64.h>由脚本 arch/x86/entry/syscalls/syscalltbl.sharch/x86/entry/syscalls/syscall_64.tbl中生成

    1
    2
    3
    4
    5
    6
    7
    // <asm/syscalls_64.h>
    __SYS_COMMON(0, sys_read, sys_read)
    __SYS_COMMON(0, sys_write, sys_write)

    // <arch/x86/entry/syscall_64.c>
    #define __SYSCALL_COMMON(nr, sym, compat) __SYSCALL_64(nr, sym, compat)
    #define __SYSCALL_64(nr, sym, compat) [nr] = sym

系统调用号

1
2
3
4
5
6
7
/* fs/xattr.c */
#define __NR_setxattr 5
__SYSCALL(__NR_setxattr, sys_setxattr)
#define __NR_lsetxattr 6
__SYSYCALL(__NR_lsetxattr, sys_lsetattr)
#define __NR_fsetxattr 7
__SYSYCALL(__NR_fsetxattr, sys_fsetattr)

系统调用号_NR_XXX:系统调用唯一标识

  • 系统调用号一旦分配不能有任何变更,否则编译好的程序会崩溃

    • 系统调用号定义在include/asm/unisted.h
  • 用户空间进程通过系统调用号指明需要执行的系统调用

    • 系统调用号就是系统调用在sys_call_table中的偏移
    • 根据系统调用号在sys_call_table中找到对应表项内容, 即可找到系统调用响应函数sys_NAME的入口地址
  • 所有系统调用陷入内核的方式相同,所以必须把系统调用号一并 传给内核

    • X86机器上,系统调用号通过eax寄存器传递给内核
      • 陷入内核态,用户空间进程已经把系统调用对应系统 调用号放入eax
      • 系统调用一旦运行,就可以从eax中得到数据

陷入指令

  • 系统调用通过陷入指令进入内核态

    • 然后内核根据存储在寄存器中的系统调用号在系统调用表 中找到相应例程函数的入口地址
    • 根据入口地址调用例程函数
  • 陷入指令是特殊指令,且依赖于机器架构,在X86机器中指令为 int 0x80

    • 不应直接使用陷入指令
    • 应实现系统调用库函数,以系统调用号为参数,执行陷入 指令陷入内核态,并执行系统调用例程函数,即 __syscall[N]系列宏

__syscall[N]

_syscall[N]:方便用户程序访问系统调用的一系列宏

1
2
3
4
5
6
// linux/include/asm/unistd.h
__syscall0(type, name)
__syscall1(type, name, type1, args1)
__syscall2(type, name, type1, arg1, type2, arg2)
// 0-6共7个宏
__syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5, type6, arg6)
1
2
3
4
5
6
7
8
9
10
#define _syscall2(type, name, type1, arg1, type2, arg2) \
type name(type1, arg1, type2, arg2) \
{ \
long _res; \
__asm__ volatile ("int $0x80" \
: "=a" (_res) \
: "0" (__NR##name), "b" ((long)(arg1)), "c" ((long)(arg2))); \
// some code
__syscall_return(type, __res)
}
  • 7个宏分别可以适用于参数个数为0-6的系统调用 (超过6个参数的系统调用罕见)

  • __syscall[N]宏根据系统调用名创建name同名函数,通过 该函数即可访问系统调用

  • 大部分情况下用户程序都是通过库函数访问系统调用,调用 __syscall[N]系列宏通常由库函数完成
  • Linux2.6.19内核之后弃用

syscall

syscall:通过系统调用号相应参数访问系统调用

1
2
3
4
5
6
7
8
9
10
11
int syscall(int number, ...);

#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>

int main(int argc, char *argv){
pid_t tid;
// `SYS_[NAME]`是系统调用号常量
tid = syscall(SYS_getpid);
}

System Call Service Routine

系统调用服务例程/响应函数:系统调用的实际处理逻辑

  • 一般以sys_开头,后跟系统调用名

    • fork()的响应函数是sys_fork()
    • exit()的响应函数是sys_exit()
  • 系统调用可以看作是系统调用服务例程在内核中注册的 名,内核通过系统调用名寻找对应服务例程

    1
    2
    3
    4
    // arch/x86/entry/syscalls/syscall_64.tbl
    0 common read sys_read
    1 common write sys_write
    2 common open sys_open

参数传递

参数传递

  • 除系统调用号外参数输入同样存放在寄存器中
    • X86机器上,ebxecxedxesiedi按照顺序 存放前5个参数
    • 需要6个以上参数情况不多,此时应该用单独的寄存器存放 指向所有参数在用户空间地址的指针

参数验证

  • 系统调用需要检查参数是否合法有效、正确

    • 文件IO相关系统调用需要检查文件描述符是否有效
    • 进程相关函数需要检查PID是否有效
  • 用户指针检查,内核必须保证

    • 指针指向的内存区域属于用户空间:不能哄骗内核读取 内核空间数据
    • 指针指向的内存区域在进程地址空间:不能哄骗内核读取 其他进程数据
    • 进程不能绕过内存访问限制:内存读、写应被正确标记

访问系统调用

系统调用初始化

  • 系统调用初始化就是对陷入指令的初始化,在X86机器上就是 初始化INT 0x80指令

  • 系统启动时

    • 汇编子程序setup_idt()准备256项的idt表
    • start_kernel()trap_init()调用的C宏定义 set_system_gate(0x80, &system_call)设置0x80号 软中断服务程序为system_call
    • system_call就是所有系统调用总入口

系统调用上下文

  • 内核在执行系统调用时处于进程上下文

    • current指针指向当前任务,即引发系统调用的进程
  • 在进程上下文中,内可以休眠、被抢占

    • 能够休眠:系统调用可以使用内核提供的绝大部分功能, 方便内核编程
    • 能被抢占:新进程可以使用相同的系统调用
      • 保证系统调用可重入
  • 系统调用返回时,控制权依然在system_call()中,其会负责 切换到用户空间并让用户进程继续执行

系统调用返回值

errno错误码

  • 系统调用将错误码放入名为errno的全局变量中

    • 为防止和正常返回值混淆,系统调用不直接返回错误码
    • errno值只在函数发生错误时设置,若函数不发生错误, errno值无定义,并不置为0
      • 0值通常表示成功
      • 负值表示系统调用失败
        • 错误值对应错误消息定义在error.h
        • 可以通过perror()库函数翻译误码
    • 处理errno前最好将其存入其他变量中,因为在错误处理 过程中errno值可能会被改变
  • 系统调用具有明确的操作

ret_from_sys_call

  • ret_from_sys_call为入口的汇编程序段在Linux进程管理中 起到重要作用

    • 系统调用结束前、大部分中断服务返回前,都会跳转至此处 入口地址
    • 还处理中断嵌套、CPU调度、信号等
  • 给用户空间进程的返回值同样通过寄存器传递

    • X86机器上,存放在eax寄存器中

Linux系统调用、派生函数

进程管理

进程控制

  • fork:创建新进程
  • clone:按照指定条件创建子进程
  • execve:运行可执行文件
  • exit:终止进程
  • _exit:立即终止当前进程
  • getdtablesize:进程能打开的最大文件数
  • getpgid:获取指定进程组标识号
  • setpgid:设置指定进程组标识号
  • getpgrp:获取当前进程组标识号
  • setpgrp:设置当前进程组标识号
  • getpid:获取进程标识号
  • getppid:获取父进程标识号
  • getpriority:获取调度优先级
  • setpriority:设置调度优先级
  • modify_ldt:读写进程本地描述符
  • nanosleep:使指定进程睡眠
  • nice:改变分时进程的优先级
  • pause:挂起进程,等待信号
  • personality:设置进程运行域
  • prctl:对进程进行特定操作
  • ptrace:进程跟踪
  • sched_get_priority_max:取得静态优先级上限
  • sched_get_priority_min:取得静态优先级下限
  • sched_getparam:取得进程调度参数
  • sched_getscheduler:取得指定进程的调度策略
  • sched_rr_get_interval:取得按RR算法实调度的实时进程 时间片
  • sched_setparam:设置进程调度参数
  • sched_setscheduler:设置进程调度策略和参数
  • sched_yield:进程主动出让处理器,并添加到调度队列队尾
  • vfork:创建执行新程序的子进程
  • wait/wait3:等待子进程终止
  • watipid/wait4:等待指定子进程终止
  • capget:获取进程权限
  • capset:设置进程权限
  • getsid:获取会晤标识号
  • setsid:设置会晤标识号

进程间通信

  • ipc:进程间通信控制总控制调用

信号

  • sigaction:设置对指定信号的处理方法
  • sigprocmask:根据参数对信号集中的号执行阻塞、解除 阻塞等操作
  • sigpending:为指定被阻塞信号设置队列
  • sigsuspend:挂起进程等待特定信号
  • signal
  • kill:向进程、进程组发信号
  • sigvec:为兼容BSD设置的信号处理函数,作用类似 sigaction
  • ssetmask:ANSI C的信号处理函数,作用类似sigaction

消息

  • msgctl:消息控制操作
  • msgget:获取消息队列
  • msgsnd:发消息
  • msgrcv:取消息

管道

  • pipe:创建管道

信号量

  • semctl:信号量控制
  • semget:获取一组信号量
  • semop:信号量操作

内存管理

内存管理

  • brk/sbrk:改变数据段空间分配
  • mlock:内存页面加锁
  • munlock:内存页面解锁
  • mlockall:进程所有内存页面加锁
  • munlockall:进程所有内存页面解锁
  • mmap:映射虚拟内存页
  • munmap:去除内存映射页
  • mremap:重新映射虚拟内存地址
  • msync:将映射内存中数据写回磁盘
  • mprotect:设置内存映像保护
  • getpagesize:获取页面大小
  • sync:将内存缓冲区数据写回磁盘
  • cacheflush:将指定缓冲区中内容写回磁盘

共享内存

  • shmctl:控制共享内存
  • shmget:获取共享内存
  • shmat:连接共享内存
  • shmdt:卸载共享内存

文件管理

文件读写

  • tcntl:文件控制
  • open:打开文件
  • creat:创建新文件
  • close :关闭文件描述字
  • read:读文件
  • write:写文件
  • readv:从文件读入数据到缓存区
  • writev:将缓冲区数据写入文件
  • pread:随机读文件
  • pwrite:随机写文件
  • lseek:移动文件指针
  • _llseek:64位地址空间中移动文件指针
  • dup:复制已打开的文件描述字
  • dup2:按指定条件复制文件描述字
  • flock:文件加/解锁
  • poll:IO多路切换
  • truncat/ftruncate:截断文件
  • vumask:设置文件权限掩码
  • fsync:将内存中文件数据写入磁盘

文件系统操作

  • access:确定文件可存取性
  • chdir/fchdir:改变当前工作目录
  • chmod/fchmod:改变文件模式
  • chown/fchown/lchown:改变文件属主、用户组
  • chroot:改变根目录
  • stat/lstat/fstat:获取文件状态信息
  • statfs/fstatfs:获取文件系统信息
  • ustat:读取文件系统信息
  • mount:安装文件系统
  • umount:卸载文件系统
  • readdir:读取目录项
  • getdents:读取目录项
  • mkdir:创建目录
  • mknod:创建索引节点
  • rmdir:删除目录
  • rename:文件改名
  • link:创建链接
  • symlink:创建符号链接
  • unlink:删除链接
  • readlink:读取符合链接值
  • utime/utimes:改变文件的访问修改时间
  • quotactl:控制磁盘配额

驱动管理

系统控制

  • ioctl:IO总控制函数
  • _sysctl:读写系统参数
  • acct:启用或禁用进程记账
  • getrlimit:获取系统资源上限
  • setrlimit:设置系统资源上限
  • getrusage:获取系统资源使用情况
  • uselib:选择要使用的二进制库
  • ioperm:设置端口IO权限
  • iopl:改变进程IO权限级别
  • outb:低级端口操作
  • reboot:重启
  • swapon:开启交换文件和设备
  • swapoff:关闭交换文件和设备
  • bdflush:控制bdflush守护进程
  • sysfs:获取核心支持的文件系统类型
  • sysinfo:获取系统信息
  • adjtimex:调整系统时钟
  • getitimer:获取计时器值
  • setitimer:设置计时器值
  • gettimeofday:获取时间、时区
  • settimeofday:设置时间、时区
  • stime:设置系统日期和时间
  • time:获取系统时间
  • times:获取进程运行时间
  • uname:获取当前unix系统名称、版本、主机信息
  • vhangup:挂起当前终端
  • nfsservctl:控制NFS守护进程
  • vm86:进入模拟8086模式
  • create_module:创建可载入模块项
  • delete_module:删除可载入模块项
  • init_module:初始化模块
  • query_module:查询模型信息

网络管理

网络管理

  • getdomainname:获取域名
  • setdomainname:设置域名
  • gethostid:获取主机标识号
  • sethostid:设置主机标识号
  • gethostname:获取主机名称
  • sethostname:设置主机名称

Socket控制

  • socketcall:socket系统调用
  • socket:建立socket
  • bind:绑定socket到端口
  • connect:连接远程主机
  • accept:响应socket连接请求
  • send/sendmsg:通过socket发送信息
  • sendto:发送UDP信息
  • recv/recvmsg:通过socket接收信息
  • recvfrom:接收UDP信息
  • listen:监听socket端口
  • select:对多路同步IO进行轮询
  • shutdown:关闭socket上连接
  • getsockname:获取本地socket名称
  • getpeername:获取通信对方socket名称
  • getsockopt:获取端口设置
  • setsockopt:设置端口参数
  • sendfile:在文件端口间传输数据
  • socketpair:创建一对已连接的无名socket

用户管理

  • getuid:获取用户标识号
  • setuid:设置用户标识号
  • getgid:获取组标识号
  • setgid:设置组标识号
  • getegid:获取有效组标识号
  • setegid:设置有效组标识号
  • geteuid:获取有效用户标识号
  • seteuid:设置有效用户标识号
  • setregid:分别设置真实、有效的组标识号
  • setreuid:分别设置真实、有效的用户标识号
  • getresgid:分别获取真实、有效、保存过的组标识号
  • setresgid:分别设置真实、有效、保存过的组标识号
  • getresuid:分别获取真实、有效、保存过的用户标识号
  • setresuid:分别设置真实、有效、保存过的用户标识号
  • setfsgid:设置文件系统检查时使用的组标识号
  • setfsuid:设置文件系统检查时使用的用户标识号
  • getgroups:获取候补组标志清单
  • setgroups:设置候补组标志清单

通知内核

  • 一般系统调用都是通过软件中断向内核发请求,实现内核提供的 某些服务

    • 128号异常处理程序就是系统调用处理程序system_call()
  • 用户空间进程不能直接执行内核代码,需要通过中断通知内核 需要执行系统调用,希望系统切换到内核态,让内核可以代表 应用程序执行系统调用

  • 通知内核机制是靠软件中断实现,X86机器上软中断由int产生

    • 用户程序为系统调用设置参数,其中一个参数是系统调用 编号
    • 程序执行“系统调用”指令,该指令会导致异常
    • 保存程序状态
    • 处理器切换到内核态并跳转到新地址,并开始执行异常处理 程序,即系统调用处理程序
    • 将控制权返还给用户程序
  • arch/i386/kernel/head.s

  • init/main.c
  • arhc/i386/kernel/traps.c
  • include/asm/system.h

参数传递

  • _syscalN()用于系统调用的格式转换和参数传递

    • 参数数量为N的系统调用由_syscallN()负责
    • N取值为0-5之间的整数
  • 启动INT 0x80后,规定返回值送eax寄存器

    • 定义于include/asm/unistd.h,用于系统调用的格式转换 和参数传递

Linux Interrupt

中断

  • 软中断:

  • 硬中断:外围设备完成用户请求后,会向CPU发出中断信号

    • CPU会暂停执行下条将要执行的指令,转而执行中断信号 对应处理程序,并将进程转入内核态

Interrupt

  • 中断属性

    • 中断号:标识不同中断
    • 中断处理程序:不同中断有不同处理程序
  • interrupt vector table:内核中维护,存储所有中断处理 程序地址

    • 中断号就是相应中断在中断向量表中偏移量

Pandoc介绍

Pandoc

Pandoc:将文本在不同标记语言之间相互转换的工具

  • Pandoc使用Haskell开发,最新版本Pandoc可以使用Haskell平台 包管理器cabal安装

    1
    2
    3
    $ sudo apt install haskell-platform
    $ cabal update
    $ cabal install pandoc
  • Pandoc支持的标记语言格式包括 (具体可通过--list-input-formats查看)

    • Markdown
    • ReStructuredText
    • HTML
    • LaTeX
    • ePub
    • MS Word Docx
    • PDF
  • Pandoc输入、输出文本

    • 可从输入、输出的文件扩展名推测输入、输出格式
    • 缺省输入格式为Markdown、缺省输出格式为html
    • 若未指明输入、输出文件,则读取、写入标准输入、输出
    • 文本默认为utf-8编码,否则可以用iconv转换文本 编码进行输入、输出

Pandoc相关选项

基础选项

  • -f/-t:输入、输出格式
  • -o:输出文件
  • --number-sections/-N:为标题添加编号
  • --verbose:详细调试信息
    • 其中会给出资源文件目录~/.pandoc,存放模板等
  • --log:日志信息
  • --file-scope:分别转换每个文件
    • 指定多个输入文件时默认将多个文件拼接,空行分隔

信息选项

  • --list-input-formats/--list-output-formats:输入、 输出格式
  • --list-extensions[=FORMAT]:列出Markdown扩展支持情况
    • <FORMAT>-<EXT>可以增减格式中一个、多个扩展选项
  • --list-highlight-languages:语法高亮支持语言
  • --list-highlight-styles:语法高亮支持样式

模板选项

  • -standalone/-s:生成完整文件 (仅对可生成片段的某些格式:html、LaTeX)
    • 默认生成文档片段
    • 采用相应内值模板生成完整文件
  • --print-default-template=<FORMAT>/-D <FORMAT>:输出 对应格式的默认模板
  • --template=<TPL>:指定创建文档所需模板
  • --css=<URL>/-c <URL>:指定CSS样式表

自定义值传递

  • --variable=<KEY[:<VAL>]>/-V <KEY[:<VAL>]>:指定模板 变量取值
  • --metadata=<KEY[:<VAL>]>/-M <KEY[:<VAL>]>:指定 元数据字段取值
    • 元数据字段影响模板变量取值,同时影响底层文档元数据
  • --metadata-file=<FILE>:从YAML格式文件中设置元数据字段 取值

PDF生成相关

  • --toc:生成目录
  • --template=<TPL>:编译使用的LaTeX模板,缺省为自带
  • --latex-engine=<ENG>:指定LaTeX引擎,需安装
    • 默认pdflatex对中文支持缺失,建议使用xelatex
  • --highlight-style=<STY>:代码块语法高亮
    • 自带高亮样式可通过--list-highlight-styles查看
    • 也可指定高亮样式文件
  • --listings:LeTeX文档中使用Listings包格式化代码块
  • --biblatex/--natbib:指定处理参考文献程序
  • --bibliography=<FILE>:设置文档元数据中参考文献信息
  • -f markdown-implicit_figures设置Markdown格式,指定 图像保持原始位置,避免pdf中错位

HTML生成相关

  • --self-contained:将css、图片等所有外部文件压缩进html 文件中
  • Markdown中使用html标记可以在转换为html后保留,可以据此 设置转换后的样式

DOCX生成相关

  • --reference-doc=<FILE>:指定格式参考文件
    • 参考文件内容被忽略,就样式、文档属性被使用
  • --print-default-data-file=reference.docx:输出系统默认 模板
  • --extract-media=<DIR>:提取文档中多媒体文件至文件夹, 并在目标文件中设置对其引用

EPUB生成相关

  • --epub-cover-image=<FILE>
  • --epub-metadata=<FILE>
  • --epub-embed-font=<FILE>

数学公式渲染

  • --mathjax[=<URL>]
  • --mathml
  • --katex[=<URL>]

Pandoc模板

模板变量

模板变量:Pandoc模板中可以包含变量用于自定义模板

1
2
3
4
5
6
7
8
9
10
11
$title$						# 变量表示方法

$if(var)$ # 变量条件语句
X
$else$
Y
$endif$

$for(var)$ # 变量循环语句
X
$endfor$
  • 变量赋值方式
    • 命令行参数提供
    • 文档元数据中查找

Todo.txt

Todo.txt格式

  • todo.txt格式使用纯文本存储、表示任务
  • 存储任务的文本文件中每行表示一项task
  • todo.txt类清单管理应用因此一般具备如下特点
    • 待完成任务、已完成任务分为两个文件分别存储
    • 可以自由修改任务存储文件以修改任务内容

基本要素

todotxt_format_description

  • 任务结构:各部分之间使用空格分隔

    • x:任务完成标识符
    • 优先级:([A-Z])
    • 完成日期、创建日期:Y-m-d格式
    • 任务描述:任务主体部分
  • 任务描述部分可以包含各种类型的tag,tag格式上可以放在任务 任何部分,但习惯上放在末尾

    • project+标识
    • context@标识
    • [key]:[value]:metadata键值对,key表示键值对含义 (中间一般不用空格分隔,便于处理)
  • 常用特殊metadata键值对
    • 时间戳:t:Y-m-d
    • 截至日期:due:Y-m-d

Todo.txt工具

todo.txt-cli

todo.txt-cli:基于shell脚本管理todo.txt文件

  • 只提供基本todo.txt功能,可以通过自行添加脚本插件方式扩展

    • 插件文件即普通可执行脚本文件,todo.sh会将命令参数 传递给相应脚本文件
    • todo.sh [action]将直接调用相应action名称脚本 (可建立同名文件夹、文件管理,同名文件夹文件被调用)
  • 使用两个文件分别存储待完成任务、已完成任务

    • 待完成任务文件中可以自行x标记完成,然后使用命令 归档至已完成任务文件中

topydo

topydo:基本python脚本管理todo.txt文件

  • 提供了cli、prompt、column三种模式
  • 原生支持以下标签
    • due、start日期
    • 管理任务之间依赖管理
    • 重复任务

Shell编程基础

变量

定义、使用、删除

  • 定义变量

    • 定义时不加$符号
    • 变量名和等号之间不能有空格
    • 变量名只能由英文字母、数字、下划线,且不以数字开头, 不能用bash中的关键字
    • 已经定义变量可以重新定义
  • 使用变量

    • 即可使用, 未定义变量直接使用不报错,返回空值
    • {}可选,但是{}能够明确定义变量名范围,帮助阅读、 解释器执行
  • 匿名变量

    • 可以认为所以语句(命令)的执行结果都存储在一个匿名 变量中,可以在其之前加上$获取其结果
  • readonly:设置变量为只读变量,更改(重定义)变量报错

  • unset:删除变量,不能删除只读变量

局部变量

局部变量:在脚本、命令中定义

  • 仅在当前shell实例中有效

    • shell变量默认为global,作用域从被定义处开始到脚本 末尾(即使在函数内部定义)
    • 显式local声明作用域局限于函数内部
  • 其他shell启动的程序不能访问局部变量

环境变量

环境变量:就是shell中的普通变量,只是这些变量往往被进程读取 用于自身初始化、设置

  • 狭义:export/declare -x声明的变量,只有这样的变量 才能默认被子进程继承
  • 广义;shell中所有的变量(包括局部变量)

环境变量设置

在shell中设置环境变量有两种方式

  • export/declare -x:设置全局(环境)变量

    • 任何在该shell设置环境变量后,启动的(子)进程都会 继承该变量
    • 对于常用、许多进程需要的环境变量应该这样设置
  • <ENV_NAME>=... cmd:设置临时环境变量

    • <ENV_NAME>=...不能被子进程继承,所以必须在其后立刻 接命令
    • 只对当前语句有效,也不能覆盖同名变量

用途

  • 环境变量是某些程序正常运行的必要条件
  • 所有程序都能访问环境变量,可用作进程通信
    • 在程序中设置环境变量,供其他进程访问变量
    • 父进程为子进程设置环境变量,供其访问

Shell变量

Shell变量:shell程序设置的特殊变量,包括环境变量、局部变量, 保证shell的正常运行

  • $0:shell执行脚本名

    • 交互式bash(即普通终端中)该参数为-bash
      • source执行脚本时,该参数可能非预期
    • 以下shell变量均不考虑$0,如
      • 函数中$0也是脚本名
      • shift无法跳过该参数
  • $1,..., $9:向脚本传递的位置参数

  • $@:脚本、函数参数列表
  • $*:脚本、函数参数字符串
  • $#:脚本、函数参数数量
  • $?:上条命令执行结果
  • $$$$:脚本进程号
  • $!:执行脚本最后一条命令

字符串

拼接

  • shell中字符默认为字符串,自动拼接

通配符

  • *:匹配任意长度字符串

    • 不包括./,必须显式匹配
  • ?:匹配一个字符

  • []:匹配出现在[]中的字符

    1
    2
    ls /[eh][to][cm]*
    # 匹配`/etc`、`/home`等
  • {}

    • 枚举全部

      1
      2
      3
      4
      5
      6
      $ mkdir {user1, user2}-{home, bin}
      # 笛卡尔积(字符串连接)
      # 等价于建立`user1-home, user1-bin, user2-home, user2-bin`
      $ echo {1..10}
      # 生成序列
      # 见`for`中`RangeIterator`部分

子串

1
2
$ file=/dir1/dir2/dir3/file.txt.bak
$ null=""

切片

  • ${<var_name>:n[:m]}:从第n开始m个字符
    • nm非负值
      • n:从0开始的下标起始
      • m切片长度缺省表示到末尾
    • nm使用0-负值表示
      • n:从0开始的负向下标起始
      • m:从0开始的负向下标终止
命令 解释 结果
${file:0:5} 提取首个字符开始的5个字符 /dir1
${file:5:5} 提取第5个字符开始的5个字符 /dir2
${file:5} 提取第5个字符开始至末尾 /dir2...
${file:0-5} 反向计算下标 t.bak
${file:0-5:0-1} 反向计算下标 t.ba
${file:5:0-2} 提取第5个字符开始至-2下标处 /dir2.../t.b

子串匹配

1
2
3
4
${<var_name>%<pattern>}
${<var_name>%%<pattern>}
${<var_name>#<pattern>}
${<var_name>##<pattern>}
  • #:去掉字符串左边最短pattern
  • ##:去掉字符串左边最长pattern
  • %:去掉字符串右边最短pattern
  • %%:去掉字符串右边最长pattern
  • 注意:var_name前不要变量标志$
  • 以上4种模式返回新值,不改变原变量值
  • 仅在pattern中使用通配符时,最短、最长匹配才有区别
命令 解释 结果
${file#*/} 去除首个/及其左边 dir2/dir3/file.txt.bak
${file##*/} 仅保留最后/右边 file.txt.bak
${file#*.} 去除首个.及其左边 txt.bak
${file##*.} 仅保留最后.右边 bak
${file%/*} 去除最后/及其右边 /dir1/dir2/dir3
${file%%*/} 去除首个/及其右边 空值
${file%*.} 去除最后.及其右边 /dir1/dir2/dir3/file.txt
${file%%*.} 去除首个.及其右边 /dir1/dir2/dir3/file.txt

替换

  • /from/to:替换首个fromto
  • //from/to:替换全部fromto
命令 解释 结果
${file/dir/path} 替换首个dirpath /path1/dir2/dir3/file.txt.bak
${file/dir/path} 替换全部dirpath /path1/path2/path3/file.txt.bak

默认值替换

  • -:变量未设置返回
  • +:变量设置返回
  • =:变量未设置返回、设置变量
  • ?:变量未设置输出至stderr
  • ::以上命令条件包括空值(空值视为未设置)

shell_variable_assignment

命令 解释 示例 结果
${井<var_name>} 获取字符串长度 ${井file} 27
${<var_name>-<default>} 变量未设置返回默认值 ${invalid-file.txt.bak} file.txt.bak
${<var_name>:-<default>} 变量未设置、空值返回默认值 ${null-file.txt.bak} file.txt.bak
${<var_name>+<default>} 变量设置返回默认值 ${file-file.txt.bak} fil.txt.bak
${<var_name>:+<default>} 变量非空返回默认值 ${file-file.txt.bak} file.txt.bak
${<var_name>=<default>} 变量未设置,返回默认值、并设置变量为默认值 ${invalid=file.txt.bak} file.txt.bak
${<var_name>:=<default>} 变量未设置、空值返回默认值、并设置变量为默认值 ${null=file.txt.bak} file.txt.bak
{$<var_name>?<default>} 变量未设置输出默认值至stderr {invalid?file.txt.bak} file.txt.bak输出至stderr
{$<var_name>:?<default>} 变量未设置、空值输出默认值至stderr {$null:?file.txt.bak} file.txt.bak输出至stderr

字符串比较

  • =, !=, -z, -n:字符串相等、不相等、为空、非空

  • 使用=比较变量是否为某字符串时,其中在两侧添加字符 保证=不为空值,否则若$test为空值,表达式报错

    1
    2
    3
    if [[ "$text"x = "text"x ]]; then
    command
    fi
  • 比较变量时要在两侧加上双引号"",否则可能报错、结果不 符合预期

数组

1
$ A=(a b c def)

取值

  • []:选择数组元素
    • [n]:返回数组第n个元素
    • [*]/[@]:返回数组全部元素
命令 解释 结果
${A[@]} 返回全部元素 a b c def
${A[*]} 同上 同上
${A[0]} 返回数组第一个元素 a
${井A[@]} 返回数组元素总数 4
${井A[*]} 同上 同上
${井A[3]} 返回数组第4个元素长度 3
A[3]=xyz 设置数组第4个元素值

数值

  • (())/[]:在其中执行整数运算
  • let:执行数值运算

规则

  • 返回的数据结果相当于存储在一个匿名变量中,使用的话需要 在之前加上$

  • 1
    2
    3
    $ a=5; b=7; c=2;
    $ echo $((a + b * $c))
    # 19
  • $((N#xx))/$[$#xx]:将其他进制数据转换为10进制

    1
    2
    $ echo $((2#110))
    # 6
  • 自增、自减运算可以在(())/[]中直接执行

    1
    2
    $ a=5; ((a++)); echo $a;
    # 6

特殊运算符

  • ~:位与
  • |:位或
  • ^:位异或
  • >>:位右移
  • <<:位左移
  • **:数值平方
  • +=, ++, -=, --, *=, \=:自变化运算符

逻辑运算

  • -eq, -ne, -ge, -le, -lt, -gt:比较
  • ==, !=, >=, <=, <, >:数值比较

文件

  • -e:目标存在
  • -f:文件为一般文件(非目录、设备)
  • -s:文件大小非0
  • -d:目标为目录
  • -b:文件为块设备(软盘、光驱)
  • -c:文件为流设备(键盘、modem、声卡)
  • -p:文件为管道
  • -h/L:目标为符号链接
  • -S:目标为Socket
  • -t:文件描述符被关联到终端
    • 一般用于检测脚本的stdin是否来自终端[ -t 0 ],或 输出是否输出至终端[ -t 1 ]
  • -r:目标是否可读权限
  • -w:目标是否可写权限
  • -x:目标是否可执行权限
  • -g:目标是否设置有set-group-id
  • -u:目标是否设置有set-user-id
  • -k:目标是否设置sticky bit
  • -O:用户是否是文件所有者
  • -G:用户是否属于文件所有组
  • -N:文件从上次读取到现在为止是否被修改
  • f1 -nt f2:文件f1f2
  • f1 -ot f2:文件f1f2
  • f1 -ef f2:文件f1f2指向相似文件实体(相同的硬链接)

Vim 配置

打印信息

  • :echo:打印信息,但是信息不会保存
  • :echom:打印信息会保存在:messages
  • :messages:查看:echom保存的信息

设置选项

  • 设置选项方式

    • 命令行一次设置多个选项::set number numberwidth=6
    • 本地缓冲区设置::setlocal nonumber
  • 设置 bool 选项

    • :set <name>:打开选项
    • :set no<name>:关闭选项
    • :set <name>!:切换选项
    • :set <name>?:查看选项值(返回或no
  • 设置键值选项

    • :set <name>=value:设置选项值
    • :set <name>?:查看选项值
    • 键值选项支持运算:+=

statusline 状态栏设置

  • 状态栏代码通用格式:%-0{minwid}.{maxwid}{item}

    • -:左对齐
    • 0:使用”0”填充
    • %=:切换到状态栏右侧

    • %f:文件名

    • %F:完整路径文件名
    • %y:文件类型([text],[python])
    • %Y:文件类型(TEXT,PYTHON)

    • %l:当前行号

    • %L:总行数
    • %v/%V:列号
    • %c:字符列号
    • %p:文件位置百分比

    • %n:buffer number

    • %{&ff}:文件格式(DOS、UNIX)
    • %b:当前字符ACSII码
    • %B:当前字符16进制值
    • %m:modified flag([+],[-]表示不可修改)
  • 设置状态栏显式格式,既可以一行完成配置,也可以分开配置

    1
    2
    3
    4
    5
    6
    set statusline=%f\ -\ filetype:\ %y
    set statusline=%f
    set statusline+=%=
    set statusline+=%l
    set statusline+=/
    set statusline+=%L
    • 中间空格需要用“\“转义,“%%”转义“%”
  • laststatus:设置状态栏显示模式

    • 1:默认值,两个以上窗口才显示
    • 2:一直显示

折叠设置

  • foldmethod
    • manual:手动折叠选中的行(默认 zf 触发)
    • marker{{{` 到 `}}} 标记待折叠行(默认 za 触发)
    • indent:折叠缩进
    • syntax:语法折叠
  • foldlevel=<num>:设置 indent 折叠起始水平(zm 触发),即从 <num> 水平开始尝试折叠
  • foldlevelstart=<num>:设置文件打开时默认折叠水平
    • -1:初始不折叠
  • foldcolumn=<num>:用 <num> 行表示可可折叠状态

一些常用关键字

  • iskeyword=@,_,48-57,192_255:指定关键字

    • 下划线
    • ASCII 码位在 48-57 之间的字符(0-9)、192-255之间的字符
  • conceallevel=0:隐藏等级

    • 1
    • 2

键盘映射

1
:<mode>map <mark> {lhs} {rhs}
  • 注意:映射后不能跟注释,vim会认为整行都是命令

映射工作模式

  • 映射的工作模式可区分 6 种
    • normal 模式:输入命令时
    • visual 模式:可视区域高亮并输入命令时
    • select 模式:类似可视模式,但键入的字符对选择区替换
    • operator-pending 模式:操作符等待中
    • insert 模式:包括替换模式
    • command-line 模式:输入 :/ 命令时

映射命令设置

  • 映射命令设置的模式如下,对应都有如下非递归、取消命令

    • <mode>noremap[!]:非递归映射,即不会在其他映射中再次被展开
    • <mode>unmap[!]:取消映射
    • <mode>mapclear[!]

    |命令|模式| |——-|——-| |:map|normalvisualselectoperator-pending| |:nmap|normal| |:vmap|visualselect| |:smap|selection| |:xmap|visual| |:omap|operator-pending| |:map!|insertcommand-line| |:imap|insert| |:lmap|insertcommand-lineLang-Arg| |:cmap|command-line| |:tmap|终端作业|

特殊参数

  • 映射特殊参数

    • <buffer>:映射将局限于当前缓冲区
      • 优先级比全局映射高
      • 清除映射时同样需要添加参数
      • 可使用 <leader> 替代 <localleader> 可工作,但是不推荐
    • <nowait>:存在较短映射时,失效以其作为前缀的较长映射
    • <silent>:映射不在命令行上回显
    • <special>:特殊键可以使用<>记法
    • <script>:映射只使用通过以<SID>开头来定义的脚本局部映射来重映射优右值中的字符
    • <unique>:若存在相同命令、缩写则定义失败
      • 定义局部映射时,同样会检查全局映射
    • <expr>:映射的右值将被作为表达式被计算
  • 特殊参数说明

    • 特殊参数的尖括号<>是本身具有的,必须紧跟命令后面
    • 有些特殊参数在取消映射时同样需注明

omap

  • 应用方法:operator (操作) + operator-pending (移动、范围选择)
  • 预定义的 operator-pending 映射如 wawi(t,

    |按键 |操作 |移动 | |———-|———————-|———————-| |dw |删除(delete) |到下一个单词 | |ci( |修改(change) |在括号内 | |yt, |复制 |到逗号前 |

  • 自定义的 operator-pending 映射则需要

    • 选取一定范围:可同时指定开头、结尾(一般通过进入 visual 模式下选择范围)

      1
      2
      3
      4
      5
      6
      7
      " 下个括号内内容
      onoremap in( :<c-u>normal! f(vi(<cr>
      " 当前括号内容
      onoremap il( :<c-u>normal! f)vi(<cr>`
      " 选取使用 `===` 标记 markdown 标题
      onoremap ih :<c-u>execute "normal! ?^==\\+$\r:nohlsearch\rkvg_"<cr>
      onoremap ah :<c-u>execute "normal! ?^==\\+$\r:nohlsearch\rg_vk0"<cr>
    • 指定光标位置:光标当前位置为开头、指定位置为结尾

      1
      2
      " 移动至 `return` 前一行
      onoremap b /return<cr>

leaderslocalleader

leaderlocalleader:作为“前缀”的不常用的按键,后接其他字符作为整体映射

  • 用途

    • 避免覆盖太多按键原始功能
    • 约定俗成的规范,容易理解
    • 方便更改 <leader><localleader> 作为前缀设置
      • <leader>:对全局映射而设置的映射的前缀
      • <localleader>:只对某类(个)文件而设置的映射的前缀
    • <leader><localleader> 除了设置不同以外,没有太大区别,应用场合时约定规范,不是强制性的
  • <leader><localleader> 设置

    1
    2
    3
    4
    :let mapleader = "-"
    :nnoremap <leader>d dd
    :let maplocalleader = "\\"
    :nnoremap <buffer> <localleader>c I#<esc>
    • vim 会对 mapleadermaplocalleader 进行特殊的处理,不是简单的声明

Abbreviations 缩写

  • iabbrev:紧跟缩写输入非关键字后,缩写会替换为相应的完整字符串
    • 相较于映射
      • iabbrev 用于 insertreplacecommand-line 模式
      • iabbrev 会注意缩写前后的字符,只在需要的时候替换
    • iabbrev 同样支持特殊参数
      • <buffer>:仅限本地缓冲区
1
2
3
4
5
6
7
8
" 纠错
iabbrev waht what
" 简化输入
iabbrev @@ xyy15926@gmail.com
" 替换 `----` 为前个单词
iabbrev <buffer> ---- &mdash
" 替换 `return` 为 null
iabbrev <buffer> return nopenopenope
  • :set iskeyword? 即可查看关键字字符

Autocmd 自动命令

autocmd 使用

  • autocmd 注意事项

    • 同时监听多个事件,使用 , 分隔,中间不能有空格
    • 一般同时监听 bufnewfilebufread,这样打开文件时无论文件是否存在都会执行命令
    • 所有事件后面都需要注明适用场景,可用*表示全部场景,中间也不能有空格
    • autocmd 是定义命令,不是执行命令
      • 每次执行都会定义命令,而vim 不会忽略重复定义
      • 如::autocmd bufwrite * :sleep 200m,每次执行时都会重复定义命令
  • 缓冲区事件

    1
    2
    3
    4
    autocmd bufnewfile * :write
    autocmd bufnewfile *.txt :write
    autocmd bufwritepre *.html :normal gg=g
    autocdm bufnewfile,bufread *.html setlocal nowrap
  • filetype 事件(vim 设置缓冲区 filetype 时触发)

    1
    2
    3
    4
    autocmd filetype javascript nnoremap <buffer> <localleader>c i//<esc>
    autocmd filetype python nnoremap <buffer> <localleader>c i#<esc>
    autocmd filetype javascript :iabbrev <buffer> iff if ()<left>
    autocmd filetype python :iabbrev <buffer> iff if:<left>

augroup 自动命令组

  • 自动命令组

    1
    2
    3
    4
    augroup cmdgroup
    autocmd bufwrite * :echom "foo"
    autocmd bufwrite * :echom "bar"
    augroup end
  • 注意事项

    • 类似 autocmdvim 不会忽略重复定义,但是可以通过 :autocmd! 清除一个组

      :augroup cmdgroup : autocmd! : autocmd bufwrite :echom “foo” : autocmd bufwrite :echom “bar” :augroup end

Vim安装

安装选项

1
2
3
4
5
6
7
8
$ ./configure --with-features=huge\
--enable-multibyte \
--enable-python3interp \
--with-python3-config-dir=/usr/lib64/python3.4/config-3.4m/ \
--enable-pythoninterp \
--with-python-config-dir=/usr/lib64/python2.7/config/ \
--prefix=/usr/local
--enable-cscope
  • 按照以上命令配置,编译出的Vim版本中是动态支持 +python/dyn+python3/dyn

  • 此时Vim看似有python支持,但是在Vim内部 :echo has("python"):echo has("python3")都返回0

  • 之后无意中尝试去掉对python的支持,编译出来的Vim就是 可用的python3,不直到为啥