zoukankan      html  css  js  c++  java
  • Linux 系统的总管 Systemd

    1. init的进化,全功能的Systemd

    本文是学习Systemd过程的整理笔记,读者要学习可以按如下顺序查看几篇写的非常好的文章。

    https://www.ibm.com/developerworks/cn/linux/1407_liuming_init3/index.html
    浅析Linux初始化init系统,第3部分[Systemd]刘明(2014-7-16)
    http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html
    Systemd 入门教程:命令篇  阮一峰(2016-3-7)
    http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html
    Systemd 入门教程:实战篇 阮一峰(2016-3-8)
    看完这3篇,基本就能比较全面的了解Systemd了。如果还不过瘾,还可以看下面这些:
    https://www.freedesktop.org/wiki/Software/systemd/
    https://www.freedesktop.org/software/systemd/man/
    https://en.wikipedia.org/wiki/Systemd
    https://zh.wikipedia.org/wiki/Systemd
    http://www.jinbuguo.com/systemd/systemd-analyze.html
    https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units
    http://0pointer.de/public/systemd-ebook-psankar.pdf
    一些主流分发版本的Systemd文档:
    https://wiki.archlinux.org/index.php/Systemd
    https://wiki.gentoo.org/wiki/Systemd
    https://wiki.debian.org/systemd
    https://wiki.debian.org/zh_CN/Systemd
    https://fedoraproject.org/wiki/How_to_debug_Systemd_problems
    https://fedoraproject.org/wiki/SysVinit_to_Systemd_Cheatsheet
    https://docs.fedoraproject.org/en-US/quick-docs/understanding-and-administering-systemd/index.html
    

    1.1 Linux系统中,init主要有3个版本

    内核启动的第一个用户空间进程是由init开始的,init主要有如下3个版本,

    > System V init, (Sys V 传统的顺序启动init,已过时)包含目录: /etc/inittab
    > Upstart, (Ubuntu使用过)包含目录: /etc/init, 包含.conf文件
    > systemd, (并行按需启动,主流)包含目录: /usr/lib/systemd/; /etc/systemd/
    
    根据 Linux 惯例,字母d是守护进程(daemon)的缩写。 Systemd 这个名字的含义,就是它要守护整个系统。
    systemd 是linux的系统和服务管理器。 systemd 和 SysV、LSB init scripts兼容. 它可以作为sysvinit的一个替代品. 
    Systemd从开机初始化,运行过程各种服务的管理,到系统休眠,睡眠,关机。内核之上管理一切,有人形容这就是一个操作系统了。已成为大多数发行版的标配。
    

    1.2 比较传统的init程序,Systemd的特点有:

    * 更快的启动速度:采用了 socket / D-Bus activation 等技术启动服务
    * 能够按需启动守护进程
    * 采用 Linux 的 Cgroup 特性跟踪和管理进程的生命周期。准确干净的关闭服务,回收资源。
    * 启动挂载点和自动挂载的管理:Systemd 内建了自动挂载服务实现 autofs 的功能,无需另外安装 autofs 服务。Maintains mount and automount points
    * 实现事务性依赖关系管理:
    * 能够对系统进行快照和恢复(这个快照功能目前在 systemd 中并不完善2014)
    * 日志服务:systemd 自带日志服务 journald,克服现有的 syslog 服务的缺点
    * Systemd Journal 用二进制格式保存所有日志信息(内核日志和应用日志),用户只用journalctl一个命令来查看所有日志信息。日志的配置文件是/etc/systemd/journald.conf。
    

    1.3 Systemd Journal 的优点如下:

    * 简单性:代码少,依赖少,抽象开销最小。
    * 零维护:日志是除错和监控系统的核心功能,因此它自己不能再产生问题。举例说,自动管理磁盘空间,避免由于日志的不断产生而将磁盘空间耗尽。
    * 移植性:日志 文件应该在所有类型的 Linux 系统上可用,无论它使用的何种 CPU 或者字节序。
    * 性能:添加和浏览 日志 非常快。
    * 最小资源占用:日志 数据文件需要较小。
    * 统一化:各种不同的日志存储技术应该统一起来,将所有的可记录事件保存在同一个数据存储中。所以日志内容的全局上下文都会被保存并且可供日后查询。例如一条固件记录后通常会跟随一条内核记录,最终还会有一条用户态记录。重要的是当保存到硬盘上时这三者之间的关系不会丢失。Syslog 将不同的信息保存到不同的文件中,分析的时候很难确定哪些条目是相关的。
    * 扩展性:日志的适用范围很广,从嵌入式设备到超级计算机集群都可以满足需求。
    * 安全性:日志 文件是可以验证的,让无法检测的修改不再可能。
    
    

    2. Systemd 的基本概念

    2.1 单元的概念(12个类型)

    启动过程中的每一步都被 systemd 抽象为一个配置单元,即 unit。
    可以认为一个服务是一个配置单元;一个挂载点是一个配置单元;一个交换分区的配置是一个配置单元;等等。
    systemd 将配置单元Unit归纳为以下12种。
    > Service unit:系统服务。代表一个后台服务进程,比如 mysqld。这是最常用的一类。
    > Socket Unit:进程间通信的 socket。此类配置单元封装系统和互联网中的一个 套接字 。当下,systemd 支持流式、数据报和连续包的 AF_INET、AF_INET6、AF_UNIX socket 。每一个套接字配置单元都有一个相应的服务配置单元 。相应的服务在第一个"连接"进入套接字时就会启动(例如:nscd.socket 在有新连接后便启动 nscd.service)。
    > Device Unit:硬件设备。此类配置单元封装一个存在于 Linux 设备树中的设备。每一个使用 udev 规则标记的设备都将会在 systemd 中作为一个设备配置单元出现。
    > Mount Unit:文件系统的挂载点。此类配置单元封装文件系统结构层次中的一个挂载点。Systemd 将对这个挂载点进行监控和管理。比如可以在启动时自动将其挂载;可以在某些条件下自动卸载。Systemd 会将/etc/fstab 中的条目都转换为挂载点,并在开机时处理。
    > Automount Unit:自动挂载点。此类配置单元封装系统结构层次中的一个自挂载点。每一个自挂载配置单元对应一个挂载配置单元 ,当该自动挂载点被访问时,systemd 执行挂载点中定义的挂载行为。
    > Swap Unit:swap 文件。和挂载配置单元类似,交换配置单元用来管理交换分区。用户可以用交换配置单元来定义系统中的交换分区,可以让这些交换分区在启动时被激活。
    > Timer Unit:定时器。用来定时触发用户定义的操作,这类配置单元取代了 atd、crond 等传统的定时服务。
    > Target unit:多个Unit构成的一个组。它们本身实际上并不做什么,只是引用其他配置单元而已。这样便可以对配置单元做一个统一的控制。这样就可以实现大家都已经非常熟悉的运行级别概念。比如想让系统进入图形化模式,需要运行许多服务和配置命令,这些操作都由一个个的配置单元表示,将所有这些配置单元组合为一个目标(target),就表示需要将这些配置单元全部执行一遍以便进入目标所代表的系统运行状态。 (例如:multi-user.target 相当于在传统使用 SysV 的系统中运行级别 5)
    > Snapshot Unit:Systemd 快照,可以切回某个快照
    > Path Unit:文件或路径
    > Scope Unit:不是由 Systemd 启动的外部进程,来自systemd总线接口的信息。通常用于管理外部系统进程。
    > Slice Unit:进程组,通过Linux控制组节点(cgroup)重新激活资源。
    

    2.1.1 Unit 单元查看

    # 列出正在运行的 Unit
    $ systemctl list-units
    
    # 列出所有Unit,包括没有找到配置文件的或者启动失败的
    $ systemctl list-units --all
    列表字段有:UNIT	LOAD	ACTIVE	SUB	DESCRIPTION
    > LOAD =反映单元定义是否已正确加载。(loaded,not-found)
    > ACTIVE =高级单元激活状态,即SUB的泛化。(active,failed,inactive)
    > SUB =低级单元激活状态,值取决于单元类型。(active,dead,exited,failed,listening,mounted,plugged,running,waiting)
    

    查询统计当前系统的所有配置单元(>文件后,使用excel分类统计)

    LOAD	合计(314)
    loaded	308
    not-found	6
    
    ACTIVE	合计(314)
    active	265
    failed	2
    inactive	47
    
    SUB	合计(314)
    active	32
    dead	47
    exited	25
    failed	2
    listening	5
    mounted	11
    plugged	166
    running	20
    waiting	6
    
    # 列出所有没有运行的 Unit
    $ systemctl list-units --all --state=inactive
    
    # 列出所有加载失败的 Unit
    $ systemctl list-units --failed
    
    # 列出所有正在运行的、类型为 service 的 Unit
    $ systemctl list-units --type=service
    

    2.1.2 Unit 的状态

    # 显示系统状态
    $ systemctl status
    
    # 显示单个 Unit 的状态
    $ sysystemctl status bluetooth.service
    
    除了status命令,systemctl还提供了三个查询状态的简单方法,主要供脚本内部的判断语句使用。
    # 显示某个 Unit 是否正在运行
    $ systemctl is-active application.service
    
    # 显示某个 Unit 是否处于启动失败状态
    $ systemctl is-failed application.service
    
    # 显示某个 Unit 服务是否建立了启动链接
    $ systemctl is-enabled application.service
    

    2.1.3 Unit 管理操作

    对于用户来说,最常用的是下面这些命令,用于启动和停止 Unit(主要是 service)。
    
    # 立即启动一个服务
    $ sudo systemctl start apache.service
    
    # 立即停止一个服务
    $ sudo systemctl stop apache.service
    
    # 重启一个服务
    $ sudo systemctl restart apache.service
    
    # 杀死一个服务的所有子进程
    $ sudo systemctl kill apache.service
    
    # 重新加载一个服务的配置文件
    $ sudo systemctl reload apache.service
    
    # 重载所有修改过的配置文件
    $ sudo systemctl daemon-reload
    
    # 显示某个 Unit 的所有底层参数
    $ systemctl show httpd.service
    
    # 显示某个 Unit 的指定属性的值
    $ systemctl show -p CPUShares httpd.service
    
    # 设置某个 Unit 的指定属性
    $ sudo systemctl set-property httpd.service CPUShares=500
    
    

    2.1.4 Unit 依赖关系

    systemd 的配置单元之间可以彼此定义依赖关系。
    比如:unit A 依赖 unit B,可以在 unit B 的定义中用"require A"来表示。这样 systemd 就会保证先启动 A 再启动 B。
    
    systemctl list-dependencies命令列出一个 Unit 的所有依赖。
    $ systemctl list-dependencies nginx.service
    
    上面命令的输出结果之中,有些依赖是 Target 类型(详见下文),默认不会展开显示。如果要展开 Target,就需要使用--all参数。
    $ systemctl list-dependencies --all nginx.service
    
    

    2.2 Unit 的配置文件

    每个配置单元都有一个对应的配置文件,告诉 Systemd 怎么启动这个 Unit 。
    Systemd 默认从目录/etc/systemd/system/读取配置文件。但是,里面存放的大部分文件都是符号链接,(开机启动的部分)
    指向目录/usr/lib/systemd/system/,真正的配置文件存放在那个目录。
    systemctl enable命令用于在上面两个目录之间,建立符号链接关系。disable则是相反,删除符号链接,撤销开机启动。
    配置文件的后缀名,就是该 Unit 的种类,比如sshd.socket。如果省略,Systemd 默认后缀名为.service,所以sshd会被理解成sshd.service。
    

    2.2.1 配置文件的状态

    # 列出所有配置文件
    $ systemctl list-unit-files
    1. UNIT FILE	STATE
    
    

    2.2.2 统计本机配置文件数量

    STATE	合计(299)(8种状态)
    disabled	98	没建立启动链接,开机不启动
    enabled	8	已建立启动链接,即开机启动项目;
    enabled-runtime	1	启用运行时
    generated	3	产生
    indirect	4	间接
    static	184	静态,该配置文件没有[Install]部分(无法执行),只能作为其他配置文件的依赖
    transient	1	短暂的?
    masked		该配置文件被禁止建立启动链接
    
    type.配置文件后缀	合计(299)(12种类型)
    automount	1	自动挂载
    mount	10	挂载
    path	2	路径
    scope	1	范围,transient
    service	183	服务
    slice	2	切片?
    socket	27	套接字,端口
    swap	1	交换区
    target	63	目标
    timer	9	计时器
    device	0	设备
    snapshot	0	快照
    

    2.2.3 查看配置文件

    # 列出指定类型的配置文件
    $ systemctl list-unit-files --type=service
    
    一旦修改配置文件,就要让 SystemD 重新加载配置文件,然后重新启动,否则修改不会生效。
    $ sudo systemctl daemon-reload
    $ sudo systemctl restart httpd.service
    
    配置文件的格式,注意,配置文件的区块名和字段名,都是大小写敏感的。
    Unit 文件的编写,文件分为三个小节。
    具体内容参见:http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-commands.html
    http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html
    https://docs.fedoraproject.org/en-US/quick-docs/understanding-and-administering-systemd/index.html
    详细内容参见官方:https://www.freedesktop.org/software/systemd/man/systemd.unit.html
    

    2.3 Target

    启动计算机的时候,需要启动大量的 Unit。如果每一次启动,都要一一写明本次启动需要哪些 Unit,显然非常不方便。Systemd 的解决方案就是 Target。
    简单说,Target 就是一个 Unit 组,包含许多相关的 Unit 。
    传统的init启动模式里面,有 RunLevel 的概念,跟 Target 的作用很类似。不同的是,RunLevel 是互斥的,不可能多个 RunLevel 同时启动,但是多个 Target 可以同时启动。
    

    2.3.1 查看Target命令

    # 查看当前系统的所有 Target
    $ systemctl list-unit-files --type=target
    
    # 查看一个 Target 包含的所有 Unit
    $ systemctl list-dependencies multi-user.target
    
    # 查看启动时的默认 Target
    $ systemctl get-default
    
    # 设置启动时的默认 Target
    $ sudo systemctl set-default multi-user.target
    
    # 切换 Target 时,默认不关闭前一个 Target 启动的进程,
    # systemctl isolate 命令改变这种行为,
    # 关闭前一个 Target 里面所有不属于后一个 Target 的进程
    $ sudo systemctl isolate multi-user.target
    

    2.3.2 Target 与 传统 RunLevel 的对应关系如下。

    Traditional runleve  l      New target name     Symbolically linked to...
    > Runlevel 0           |    runlevel0.target -> poweroff.target	关闭系统。
    > Runlevel 1           |    runlevel1.target -> rescue.target	单用户模式。
    
    > Runlevel 2           |    runlevel2.target -> multi-user.target	用户定义/域特定运行级别。默认等同于 3。
    > Runlevel 4           |    runlevel4.target -> multi-user.target
    > Runlevel 3           |    runlevel3.target -> multi-user.target	多用户,非图形化。用户可以通过多个控制台或网络登录。
    
    > Runlevel 5           |    runlevel5.target -> graphical.target	多用户,图形化。通常为所有运行级别 3 的服务外加图形化登录。
    > Runlevel 6           |    runlevel6.target -> reboot.target	重启
    > emergency		  emergency.target			紧急 Shell
    
    

    2.4 Systemd 事务

    Systemd 能保证事务完整性。Systemd 的事务概念和数据库中的有所不同,主要是为了保证多个依赖的配置单元之间没有环形引用。
    若存在循环依赖,那么 systemd 将无法启动任意一个服务。
    此时 systemd 将会尝试解决这个问题,因为配置单元之间的依赖关系有两种:required 是强依赖;want 则是弱依赖,systemd 将去掉 wants 关键字指定的依赖看看是否能打破循环。
    如果无法修复,systemd 会报错。
    Systemd 能够自动检测和修复这类配置错误,极大地减轻了管理员的排错负担。
    

    2.5 Systemd 的并发启动原理

    依赖可以分为三个具体的类型,而每一个类型实际上都可以通过相应的技术解除依赖关系。
    一:解决 socket 依赖
    二:解决 D-Bus 依赖
    D-Bus 是 desktop-bus 的简称,是一个低延迟、低开销、高可用性的进程间通信机制。
    它越来越多地用于应用程序之间通信,也用于应用程序和操作系统内核之间的通信。
    很多现代的服务进程都使用D-Bus 取代套接字作为进程间通信机制,对外提供服务。
    三:解决文件系统依赖
    详见:https://www.ibm.com/developerworks/cn/linux/1407_liuming_init3/index.html
    

    2.6 Systemd 的使用

    开发人员需要了解 systemd 的更多细节。比如您打算开发一个新的系统服务,就必须了解如何让这个服务能够被 systemd 管理。这需要您注意以下这些要点:
    后台服务进程代码不需要执行两次派生来实现后台精灵进程,只需要实现服务本身的主循环即可。
    不要调用 setsid(),交给 systemd 处理
    不再需要维护 pid 文件。
    Systemd 提供了日志功能,服务进程只需要输出到 stderr 即可,无需使用 syslog。
    处理信号 SIGTERM,这个信号的唯一正确作用就是停止当前服务,不要做其他的事情。
    SIGHUP 信号的作用是重启服务。
    需要套接字的服务,不要自己创建套接字,让 systemd 传入套接字。
    使用 sd_notify()函数通知 systemd 服务自己的状态改变。一般地,当服务初始化结束,进入服务就绪状态时,可以调用它。
    
    

    2.7 系统管理

    Systemd 并不是一个命令,而是一组命令,涉及到系统管理的方方面面。systemctl是最主要的命令。

    2.7.1 关机,重启,休眠...

    # 重启系统
    $ sudo systemctl reboot
    
    # 关闭系统,切断电源
    $ sudo systemctl poweroff
    
    # CPU停止工作
    $ sudo systemctl halt
    
    # 暂停系统(挂起),设备通电,内容保存在内存中。
    $ sudo systemctl suspend
    
    # 让系统进入冬眠状态,设备断电,内存保存在硬盘中。
    $ sudo systemctl hibernate
    
    # 让系统进入交互式休眠状态(混合睡眠),内容保持在内存及硬盘中。
    $ sudo systemctl hybrid-sleep
    
    # 启动进入救援状态(单用户状态)
    $ sudo systemctl rescue
    
    Systemd的其他命令

    2.7.2 systemd-analyze命令用于查看启动耗时。

    # 查看启动耗时
    $ systemd-analyze
    
    # 查看每个服务的启动耗时
    $ systemd-analyze blame
    
    # 显示瀑布状的启动过程流
    $ systemd-analyze critical-chain
    
    # 显示指定服务的启动流
    $ systemd-analyze critical-chain atd.service
    

    2.7.3 hostnamectl命令用于查看当前主机的信息。

    # 显示当前主机的信息
    $ hostnamectl
    
    # 设置主机名。
    $ sudo hostnamectl set-hostname tompc
    

    2.7.4 localectl命令用于查看本地化设置。

    # 查看本地化设置
    $ localectl
    
    # 设置本地化参数。
    $ sudo localectl set-locale LANG=en_GB.utf8
    $ sudo localectl set-keymap en_GB
    
    

    2.7.5 timedatectl命令用于查看当前时区设置。

    # 查看当前时区设置
    $ timedatectl
    
    # 显示所有可用的时区
    $ timedatectl list-timezones
    
    # 设置当前时区
    $ sudo timedatectl set-timezone America/New_York
    $ sudo timedatectl set-time YYYY-MM-DD
    $ sudo timedatectl set-time HH:MM:SS
    
    

    2.7.6 loginctl命令用于查看当前登录的用户。

    # 列出当前session
    $ loginctl list-sessions
    
    # 列出当前登录用户
    $ loginctl list-users
    
    # 列出显示指定用户的信息
    $ loginctl show-user tom
    
    

    2.7.7更多查看命令

    2.7.7.1 查看状态,树状列表

    $ systemctl status
       CGroup: /
               ├─user.slice
               │ └─user-1001.slice
               │   ├─session-2.scope
               │   │ ├─  507 lightdm --session-child 14 21
               │   │ ├─  518 /bin/sh /etc/xdg/xfce4/xinitrc -- /etc/X11/xinit/xserv>
               │   │ ├─  529 xfce4-session
    
    List running units:运行列表:$ systemctl list-units 或者$ systemctl
    

    2.7.7.2 UNIT 列表显示

    $ systemctl
      UNIT                        LOAD   ACTIVE SUB       DESCRIPTION
      proc-sys-fs-binfmt_misc.automount loaded active running   Arbitrary Executabl>
      sys-devices-pci0000:00-0000:00:02.0-backlight-acpi_video0.device loaded activ>
      sys-devices-pci0000:00-0000:00:02.0-drm-card0-card0x2dLVDSx2d1-intel_backli>
    
      sys-subsystem-net-devices-wlp16s0.device loaded active plugged   PRO/Wireless>
      -.mount                     loaded active mounted   /
      dev-hugepages.mount         loaded active mounted   Huge Pages File System
    
    

    2.7.7.3 列出依赖关系

    $ systemctl list-dependencies
    default.target
    ● ├─lightdm.service
    ● └─multi-user.target
    ●   ├─dbus.service
    ●   ├─lm_sensors.servic
    

    2.7.7.4 树形显示进程

    alias psc='ps xawf -eo pid,user,cgroup,args'
    $ ps xawf -eo pid,user,cgroup,args
      PID USER     CGROUP                      COMMAND
        2 root     -                           [kthreadd]
        3 root     -                            \_ [rcu_gp]
        4 root     -                            \_ [rcu_par_gp]
        6 root     -                            \_ [kworker/0:0H-kblockd]
    
    

    2.7.7.5 树装显示

    $ systemd-cgls
    Control group /:
    -.slice
    ├─user.slice
    │ └─user-1001.slice
    │   ├─session-2.scope
    │   │ ├─  507 lightdm --session-child 14 21
    │   │ ├─  518 /bin/sh /etc/xdg/xfce4/xinitrc -- /etc/X11/xinit/xserverrc
    │   │ ├─  529 xfce4-session
    
    ===============
    服务类型
    编写自定义服务文件时,需要考虑几种不同的启动类型。这是使用Type=以下[Service]部分中的参数设置的:
    
    Type=simple(默认值):systemd认为服务立即启动。这个过程一定不能分叉。如果需要在此服务上订购其他服务,请不要使用此类型,除非它已被套接字激活。
    Type=forking:systemd认为一旦进程分叉和父进程退出,服务就会启动。对于经典守护进程,请使用此类型,除非您知道没有必要。您还应该指定PIDFile=,以便systemd可以跟踪主进程。
    Type=oneshot:这对执行单个作业然后退出的脚本很有用。您可能还需要进行设置RemainAfterExit=yes,以便systemd在进程退出后仍将该服务视为活动状态。
    Type=notify:Type=simple与之相同,但规定守护进程在准备就绪时会向systemd发送信号。此通知的参考实现由libsystemd-daemon.so提供。
    Type=dbus:当指定BusName出现在DBus的系统总线上时,该服务被视为就绪。
    Type=idle:systemd将延迟服务二进制文件的执行,直到调度所有作业。除此之外的行为非常相似Type=simple。
    ----------------------
    Service types
    There are several different start-up types to consider when writing a custom service file. This is set with the Type= parameter in the [Service] section:
    
    Type=simple (default): systemd considers the service to be started up immediately. The process must not fork. Do not use this type if other services need to be ordered on this service, unless it is socket activated.
    Type=forking: systemd considers the service started up once the process forks and the parent has exited. For classic daemons use this type unless you know that it is not necessary. You should specify PIDFile= as well so systemd can keep track of the main process.
    Type=oneshot: this is useful for scripts that do a single job and then exit. You may want to set RemainAfterExit=yes as well so that systemd still considers the service as active after the process has exited.
    Type=notify: identical to Type=simple, but with the stipulation that the daemon will send a signal to systemd when it is ready. The reference implementation for this notification is provided by libsystemd-daemon.so.
    Type=dbus: the service is considered ready when the specified BusName appears on DBus's system bus.
    Type=idle: systemd will delay execution of the service binary until all jobs are dispatched. Other than that behavior is very similar to Type=simple.
    
    =================
    https://bbs.archlinux.org/viewtopic.php?id=163286
    $ sudo pacman -S graphviz
    要从系统创建图表,请使用:
    $ systemd-analyze dot --order | dot -Tsvg > systemd-user.svg
    要从用户会话中创建图表,请使用:
    $ systemd-analyze dot --user --order | dot -Tsvg > systemd-user.svg
    
    ===================
    https://wiki.archlinux.org/index.php/Systemd
    调查systemd错误
    诊断启动问题
    诊断服务
    =============
    
    ===============
  • 相关阅读:
    mysql 统计数据库基本资源sql
    java ffmpeg (Linux)截取视频做封面
    shutil模块
    json模块与pickle模块
    hashlib模块
    sys模块
    os模块
    paramiko模块
    Python reduce() 函数
    瀑布流展示图片
  • 原文地址:https://www.cnblogs.com/sztom/p/10660459.html
Copyright © 2011-2022 走看看