# 内核

# 版本

  • 官网 (opens new window)
  • 目前 Linus Torvalds 依然在主管开发 Linux 内核,源代码托管在 GitHUb (opens new window) 上。
  • Linux 内核(kernel)的版本号命名规则为 主版本.次版本.发布版本-修订版本 ,例如 3.10.0-11
    • 2011 年发布 v3.0 版本。
    • 2015 年发布 v4.0 版本。
    • 2019 年发布 v5.0 版本。

# 架构

Linux 系统架构从上到下分为三层:

  • User Space :运行用户态进程。
    • 用户启动的进程一般运行在用户态,只使用虚拟内存空间中的用户内存空间。
    • 当用户态进程调用内核 API、遇到中断或异常时,就会下沉到内核态,有权限执行内核内存空间中的代码、访问底层硬件。
  • Kernel Space :运行内核态进程。
    • System Call Interface(SCI):向上对用户空间提供内核接口。
    • Virtual File System(VFS):一个抽象的文件系统层,向下管理不同类型的文件系统,向上提供统一的文件系统接口。
    • File System :实际的文件系统层,包括多种文件系统。
    • General Block Device Layer :一个抽象的块设备层,向下管理不同类型的硬件设备,向上提供统一的 IO 接口。
    • Device Driver :有很多种,分别管理不同设备。
  • Hardware Layer :计算机底层的硬件层。

# 中断

Linux 系统处理中断的过程通常分为两步:

  1. 处理硬件中断请求,立即处理其中不能延误的任务。
  2. 以软件中断的形式执行剩下的任务,比如创建成内核线程。
  • 这样既可以及时执行中断中重要的任务,又可以延迟执行耗时久的任务。
  • 例如:当网卡收到数据帧时,会发出一个硬中断,让网卡驱动程序将数据帧拷贝到内核缓冲区中。然后网卡驱动程序发出一个软中断,通知某个内核进程来解析数据帧,转换成 IP、TCP 等协议包,最后交给给应用层的进程。

# 内核进程

常见的内核进程如下:

  • kthreadd

    • 系统开机时创建的第三个进程,PID 为 2 ,负责管理所有内核态进程。
  • kworker

    • 在 CPU 每个核上运行一个实例,用于并行执行内核 Workqueue 中的任务,比如定时器、中断、IO 。
  • migration

    • 用于在多个 CPU 核之间迁移运行中的进程,从而避免单核故障、实现负载均衡。
    • 在 CPU 每个核上运行一个实例。
  • watchdog

    • 用于避免系统死机。进程启动之后会打开 /dev/watchdog 文件,不断对它进行写操作。如果在一定时间内(默认为 1 分钟)没有进行写操作,就会通过硬件或软件重启系统。
    • 在 CPU 每个核上运行一个实例。
  • kblockd

    • 用于管理块设备。
    • 在系统中运行一个实例。

# 内核参数

# sysctl

:一个内置命令,用于读取、配置系统运行时的内核参数。

  • 用法:
    sysctl
            <var>           # 读取指定参数的值
            <var>=<value>   # 配置指定参数的值
            -a              # 显示所有可修改的内核参数
            -p <file>       # 加载指定文件中的配置参数(默认是 /etc/sysctl.conf 文件)
    
    • 执行 sysctl -a 会从 /proc/sys/ 目录下的各个文件读取内核参数(超过一千个),然后显示给用户看。
      • 例如,参数 net.ipv4.tcp_keepalive_time 对应的文件是 /proc/sys/net/ipv4/tcp_keepalive_time
  • sysctl 的配置在系统重启之后就会丢失,将配置保存到 /etc/sysctl.conf 文件中才能永久生效。
    • 系统每次重启时会自动加载该文件,用户也可以执行 sysctl -p 主动加载。

# 配置参数

  • 参考文档:

  • 配置文件采用 INI 格式。

    • 键值对用 = 分隔,中间的空格会被忽略。
    • 值通常是字符串型、整型或布尔型。
      • 其中,布尔型用 1、0 分别表示 True、False 。
  • 关于进程:

    kernel.pid_max        = 32768       # 整个系统中,同时运行进程的最大数量
    kernel.threads-max    = 126935      # 整个系统中,同时运行线程的最大数量
    
  • 关于内存:

    vm.max_map_count      = 65530       # 每个进程创建的内存映射区域(memory map areas)的最大数量,它们用于 malloc、shared librarie
    vm.swappiness         = 1           # 使用 swap 内存的推荐度,取值范围为 0~100 ,0 表示禁用
    
    vm.overcommit_memory  = 0           # 是否允许内核分配出去的内存超过物理内存的总量,因为分配的内存不一定会被进程实际使用
        # 0 :根据启发式算法适量允许
        # 1 :始终允许,此时可能因为物理内存不足而触发 OOM
        # 2 :始终不允许超过物理内存的 vm.overcommit_ratio 百分比,此时不会触发 OOM ,但可能因为内存不足而不能创建新进程
    vm.overcommit_ratio   = 50
    vm.oom_kill_allocating_task = 0     # 执行 OOM 的策略。取值如下:
        # 0 :扫描全部进程,选出 oom_score 最大的进程然后杀死
        # 1 :杀死当前申请内存而导致内存不足的进程
    vm.panic_on_oom       = 0           # 是否不执行 OOM ,而是让系统重启
    
  • 关于文件:

    fs.file-max=791098                  # 整个系统中,允许同时打开文件的最大数量
        # 每个文件的 inode、dcache 大概占用 1k 内存。默认设置的 fs.file-max 会允许文件占用可用内存的 10%
    
  • 关于网络:

    net.ipv4.icmp_echo_ignore_all = 1       # 忽略所有 icmp 包,这样本机就不能被 ping 到
    net.ipv4.ip_forward           = 0       # 如果一个网口收到数据包时,不匹配目标 IP ,是否转发给其它匹配目标 IP 的网口,相当于路由转发功能
    net.ipv4.ip_local_port_range  = 10000  65535   # 允许 Socket 绑定的本地端口范围
    
    # 关于建立 TCP 连接
    net.core.somaxconn            = 4096    # 限制了调用 listen() 函数时,backlog 的最大值
    net.ipv4.tcp_max_syn_backlog  = 128     # SYN 队列的最大长度,默认值与内存成正比
    net.ipv4.tcp_syncookies       = 1       # 是否启用 SYN Cookies 功能。可取值:
        # 0 :不启用
        # 1 :默认值,当 SYN 队列满了时启用
        # 2 :总是启用
    net.ipv4.tcp_abort_on_overflow= 0       # 当 accept 队列满了时,server 怎么处理新的 ESTABLISHED 连接。可取值:
        # 0 :默认值,丢弃 client 发来的 ACK 包
        # 1 :回复一个 RST 包给 client ,表示拒绝连接
    
    # 关于保持 TCP 连接
    net.ipv4.tcp_keepalive_time   = 180
    net.ipv4.tcp_keepalive_intvl  = 10
    net.ipv4.tcp_keepalive_probes = 3
        # 如果本机的一个 TCP 连接长达 tcp_keepalive_time 秒没有用于数据通信,则探测一下对方主机是否仍然在线
        # 每隔 tcp_keepalive_intvl 秒探测一次,最多探测 tcp_keepalive_probes 次
        # 减小该值可以尽早关闭无用的 Socket ,比如 CLOSE_WAIT 状态的 Socket
    
    # 关于断开 TCP 连接
    net.ipv4.tcp_tw_reuse         = 1       # 允许将 TIME-WAIT 状态的 Socket 用于新的 TCP 连接
    net.ipv4.tcp_tw_recycle       = 1       # 允许快速回收 TIME-WAIT 状态的 Socket
    net.ipv4.tcp_fin_timeout      = 30      # FIN-WAIT-2 状态的超时时间
    net.ipv4.tcp_max_orphans      = 65536   # 限制无主的 Socket 的最大数量,超过限制则自动回收
    
    # 关于 Socket 缓冲区
    net.core.rmem_default = 212992                    # 单个 Socket 接收缓冲区的默认容量,默认为 208 KB
    net.core.wmem_default = 212992                    # 单个 Socket 发送缓冲区的最大容量
    net.core.rmem_max     = 212992                    # 最大容量
    net.core.wmem_max     = 212992
    net.ipv4.tcp_rmem     = 4096    87380   6291456   # 单个 TCP Socket 接收缓冲区的容量,分别为最小值、默认值、最大值,单位 bytes
    net.ipv4.tcp_wmem     = 4096    16384   4194304   # 单个 TCP Socket 发送缓冲区的容量
    net.ipv4.tcp_mem      = 572268  763025  1144536   # 系统全部 TCP Socket 缓冲区的容量的最小值、压力值、最大值,单位为内存页 page
        # 低于最小值时,说明内存无压力
        # 超过最小值时,开始节省给 Socket 分配的内存
        # 超过最大值时,禁止给 Socket 分配新的 page
    net.ipv4.udp_mem      = 574224  765634  1148448
    net.ipv4.udp_rmem_min = 4096
    net.ipv4.udp_wmem_min = 4096
    

# ulimit

:一个内置命令,用于设置 shell 终端占用的系统资源上限。

  • 命令:

    $ ulimit
            -a        # 显示当前全部的资源限制信息
              -H      # 选择硬限制
              -S      # 选择软限制
            -u        # 显示 -u 参数的值
            -u <n>    # nproc ,限制同时运行的进程数最多为 n 个(不能限制 root 用户)
            -m <n>    # rss   ,限制占用的常驻内存最多为 n kb(大部分 Linux 发行版不支持该限制)
            -v <n>    # 限制占用的虚拟内存最多为 n kb
            -s <n>    # stack ,限制堆栈的大小为 n kb
            -t <n>    # cpu   ,限制单个进程累计占用的 CPU 时长,超过限制时则发送 SIGKILL 信号终止该进程。进程的运行状态为 Sleeping 时不计入时长
            -n <n>    # nofile ,限制同时打开的文件描述符数量最多为 n
            -f <n>    # fsize ,限制创建单个文件的大小最大为 n blocks
    
  • 例:

    ulimit -a               # 显示限制(默认是软限制)
    ulimit -aH              # 显示硬限制
    ulimit -nH unlimited    # 设置硬限制
    ulimit -nS 1024         # 设置软限制
    ulimit -n  unlimited    # 同时设置硬限制和软限制
    
    • 当进程占用的资源超过限制时,会被立即杀死。
    • 软限制的值不能超过硬限制。
    • 非 root 用户无权调大硬限制,只能调小硬限制、改变软限制。
  • ulimit 的配置只能作用于当前 shell 终端。而 /etc/security/limits.conf 文件中的配置会对指定用户永久生效,如下:

    *    hard    nofile       unlimited
    *    soft    nofile       1024
    *    hard    maxlogins    3
    *    soft    maxlogins    3
    

    每行的格式如下:

    <domain> <type> <item> <value>
    
    • domain :用户名或组名,也可用 * 匹配所有。
    • type :取值为 hard 或 soft ,表示硬限制或软限制。
    • item、value :限制项及其值。比如:
      • maxlogins :当前用户同时登录的最大数量
      • maxsyslogins :系统同时登录的最大数量