【systemctl 学习总结】

一、为什么要系统学习 systemctl?

  • systemctl 经常遇到的真实场景
    • 服务起不来,只会 systemctl status xxx
    • 需要写一个开机启动脚本,不想每次都要手动操作
    • 想控制某程序在后台自动重启
  • 过去的方案(init.d、rc.local)为什么不够用了?(引入并行启动 的概念)
    • systemdsystemctl 是其管理工具)普及之前,Linux 主要使用 SysVinit (即 /etc/init.d 下的脚本)来管理服务。传统的 SysV init 是串行的(一个接一个启动)效率极低,导致系统启动缓慢。
    • 而 systemd 是并行的,systemd 会分析服务之间的依赖关系,没有依赖冲突的服务会同时启动。这大大缩短了 Linux 的启动时间。
    • 可以提到 systemd 不仅管理服务,还管理挂载点、Socket 等资源,是系统的"大管家"(没有理解啥意思)。有管理接口统一,日志集中,启动快可控等优势。

二、systemctl 与 systemd 的关系(核心概念扫盲)

很多新手(甚至老手)经常把 systemctl 与 systemd 混为一谈,这很正常,如果你想真正搞懂,先要理解三个核心概念:systemdsystemctlUnit。简单来说:

  • systemd是后台核心。
  • systemctlsystemd的控制接口。
  • Unit是被管理的对象。

2.1 什么是 systemd?

  • systemd 是:

    Linux系统启动后运行的第一个进程,内核加载完后,第一个运行的就是它。PID永远是1(可通过ps -p 1 -o pid,comm,cmd查看),systemd 是第一个用户态进程,作为老祖宗进程,他肩负着巨大责任:

    • 负责启动后续所有的系统服务

    • 服务管理:时刻监控着系统的各种服务(如 Nginx、MySQL),确保它们按需运行。

    • 资源编排:它不仅管服务(service),还管挂载点(mount)、网络设备(socket)、定时任务(timer)等。

    • 服务管理器

    • 资源与依赖编排工具,负责回收孤儿进程。

  • systemd 的设计哲学:

    并行启动,按需激活。不像老前辈SysVinit那样死板的按顺序执行脚本,而是通过依赖关系智能调度。

2.2 systemctl 是什么?

搞清楚了systemd后,systemctl 就很好理解了:

  • systemctl ≠ systemd
  • systemctl = systemd 的控制接口**(CLI 工具)**

systemd是一个运行在后台的守护进程,你无法直接跟守护进程对话,你需要通过systemctl这个命令行工具向守护进程发送指令。

2.3 Unit 是什么?

Unit 是systemd世界里最重要的概念,在systemd眼中,一切被管理的资源都被抽象成了一个Unit。Systemd 使用单位文件(Unit Files)来描述和管理系统服务、挂载点、套接字、计时器等系统资源。每个单位文件(Unit Files)包括了一个服务的启动,停止和重启的定义,以及一类关系和执行条件等信息。这些单位文件通常存储在 /etc/systemd/system//lib/systemd/system/ 目录下。下面第四节会详细介绍 Unit files 文件的书写格式。

  1. /usr/lib/systemd/system/(或 /lib/systemd/system/

    上面这俩目录的内容是一样的。

  • 身份:系统默认目录
  • 谁在管理:软件包管理器 (如 yum, apt, rpm),当你安装 Nginx、MySQL 等软件时,官方自带的 .service 文件会被包管理器自动安装到这里。
  • 注意:这些软件是软件维护者提供的出厂设置,禁止修改 这里的文件(一旦你升级了软件,比如 apt update,系统会重新覆盖这个目录下的文件,你辛苦修改的配置就全部丢失了。)
  1. /etc/systemd/system/(高优先级)
  • 身份:管理员自定义目录(用户特权区)。

  • 谁在管理:你(系统管理员)

  • 特点:你可以在此处放自己写的 Unit 文件,该目录下的单位文件不会因为apt update软件升级而被覆盖。如果该目录下出现了与/usr/lib/systemd/system/目录同名的 Unit 文件,那么systemd会无条件使用/etc目录下的 Unit 单位文件。


三、最常用的 systemctl 基本命令(入门必会)

掌握了概念,接下来就是"真刀真枪"的实战了。这一节我们只讲最高频、最实用的命令。

3.1 查看服务状态

当你想知道一个服务怎么样时,第一反应肯定是:

shell 复制代码
systemctl status xxx #(注:xxx的.service 后缀通常可以省略,systemctl 会自动补全)

如果你觉得上面的命令输出太多不好处理,可以用:

shell 复制代码
systemctl is-active xxx # 自动化运维常用,适合配合 if 语句做判断。

我们拿systemctl status sshd举个例子介绍一下怎么看输出:

  • Active:
    • active (running):服务活得好好的。
    • inactive (dead):服务挂了或者没启动。
    • failed:出错了!这是排障的第一步。
  • Loaded :
    • 这里会告诉你配置文件是从哪里加载的(通常是 /usr/lib.../etc...),以及是否 enabled(开机自启动)。
  • Main PID
    • 服务的主进程ID,如果你想用kill命令强杀该进程,这就是目标。
  • 底部的日志
    • 这里显示了最近的几行日志。如果服务启动失败,红色的报错信息通常就在这里。

3.2 启停与重启(start / stop)

shell 复制代码
systemctl start xxx     # 启动
systemctl stop xxx      # 停止
systemctl restart xxx   # 重启(先停后启)
systemctl reload xxx    # 卸载(不中断服务)
systemctl enable --now xxx  # 同时实现"设置开机自启"和"立即启动",一条顶两条

!NOTE

这里有一个非常经典 的知识点:restartreload 有什么区别?

  • systemctl restart xxx:先杀后启,服务会暂停中断。需要 彻底更新必须用此命令
  • systemctl reload xxx:平滑重载,主进程不退出,有时为了业务连续性需要用该命令,比如只是重新读取配置文件。

3.3 开机启动管理(enable/disable)

怎么保证重启后服务还能自动跑起来呢?

shell 复制代码
systemctl enable xxx     # 设置开机自启动(创建软连接,在启动名单文件夹里建立一个指向服务的快捷方式) 
systemctl disable xxx    # 取消开机自启
systemctl is-enabled xxx # 坚持是否开启开机自启动(返回 enabled 或 disabled)

!NOTE

systemctl enable xxx只是标记下一次开机自启动,并不会立即激活服务,这也是上面为啥推荐大家使用systemctl enable --now <service>的原因。

3.4 列出系统中所有服务

当你忘记某个服务的名字或者想看看系统跑着哪些服务,可以使用如下命令:

  • 查看当前已经激活(Active)的服务:

    shell 复制代码
    systemctl list-units --type=service  # 只想看服务(.service 文件)
  • 查看所有已安装的服务(包括没激活的)

    shell 复制代码
    systemctl list-unit-files --type=service  # 只想看服务(.service 文件)

    该命令可以用来找服务名:systemctl list-unit-files | grep ssh

掌握了上面的命令,你已经能应付 90% 的日常服务管理工作了。接下来,我们要进入深水区------自己写一个服务文件。


四、Service Unit 文件详解(核心重点,博客亮点)

前面我们学会了怎么用,这一节要学习写 Unit file 文件的规则,你将学会自定义任何程序的启动规则。

4.1 一个最简单的 service 示例

为了让你感受更直观,直接上手写一个简单的服务。场景:假设你有个 Python 脚本,你想让它变成一个系统服务。

  1. 准备脚本(随便写个死循环脚本):

    创建一个文件 /opt/myapp.py

    python 复制代码
    import time
    while True:
        print("我是后台服务,我还在运行...")
        time.sleep(5)
  2. 编写Uint文件:

    /etc/systemd/system/目录下创建一个名为myapp.service的文件,内容如下:

    ini 复制代码
    [Unit]
    Description=My first Demo Service
    After=network.target
    
    [Service]
    Type=simple
    ExecStart=/usr/bin/python3 -u /opt/myapp.py
    Restart=always
    
    [Install]
    WantedBy=multi-user.target

    提示-u 的作用是关闭 stdout/stderr 缓冲,使print() 立刻进入 journal。

  3. 立即生效:

    shell 复制代码
    # 通知 systemd 管理员更新了单元文件(Unit file),刷新一下配置
    sudo systemctl daemon-reload
    # 启动并设置开机自启
    sudo systemctl enable --now myapp
    # 查看状态
    sudo systemctl status myapp

如果一切正常,你会看到服务变成了 active (running),他的日志被 systemd 统一收进了 journald (可通过sudo journalctl -u myapp.service命令查看打印),后面会讲到。

4.2 Unit 文件怎么写

上门文件虽然简单,但是已经包含了systemd配置文件的三大核心板块,我们可以把它想象成一份员工入职说明书。

  • [Unit] :介绍我是谁,我依赖谁
    • Description :对服务的简短描述,当你输入systemctl status myapp时打印的就是它。
    • After / Requires:依赖关系,需要在被依赖的服务后启动,比如有些服务需要依赖web,就得写After=network.target
  • [Service]:介绍怎么干活
    • Type=:服务类型,坑最多,后面4.3小节讲解。
    • ExecStart=:启动命令,因为systemd启动时环境变量很少,所以**必须要写绝对路径**。
    • ExecStop=:定义服务停止命令,systemd 停服务时,用你指定的方式"收尾",比如ExecStop=/usr/bin/kill -9 myapp,当你的程序本来就可以优雅推出时(不需要特殊清理逻辑),可以不写。
    • Restart=:重启策略
      • always:只要不是管理员systemctl stop手动叫停的,无论程序是正常退出的(exit code 0)、还是异常崩溃的(exit code 1)、还是被信号杀掉的,它都会重启。
      • on-failure:只有当程序崩溃时才自动重启,如果程序自己跑完任务正常退出后不自动重启。
      • no:不自动重启,即使进程异常退出也不管。
    • User / Group=:指定运行的用户,建议不要用root用户而是指定一个普通用户。
    • Environment=:可以向 ExecStart / ExecStop 注入环境变量,后面会提到用法。
  • [Install]:介绍怎么安装,决定了服务在什么场景下被启用
    • WantedBy:依附目标
      • multi-user.target:此处与systemctl enable myapp有配合,当你执行该命令时,systemctl 会去读取 myapp.service 文件,然后寻找 [Install] 段。意思是"当系统进入多用户模式(也就是正常的命令行模式)时,把我加载进去"。命令执行后systemctl 会在 /etc/systemd/system/multi-user.target.wants/ 这个目录下,创建一个指向 myapp.service 的软连接。如果[Install]被删了,则执行enable时就会报错。

4.3 Unit 文件中 [Service] 字段 Type 的区别(很多坑)

Type告诉systemd如何判断"服务已经启动成功了"。

  • Type=simple默认最推荐:

    • 行为:systemd 认为 ExecStart 启动的那个进程就是主进程,它会一直霸占前台运行。
    • 适用:现代程序、Docker 容器、或者不会自动后台化的脚本。
    • 坑点:如果你的程序启动后自动后台化了(比如nohup&),systemd会认为主进程退出了,从而认为服务启动失败,甚至把他杀掉。
  • Type=forking传统守护进程:

    • 行为:systemd认为程序启动时会分身(fork),父进程退出了,子进程还在后台运行,systemd回去追踪那个子进程。
    • 适用:老式的软件,他们习惯启动后把自己扔到后台。
    • 使用这种类型时,最好配合 PIDFile= 参数,告诉 systemd 子进程的 PID 写在哪里,否则它可能找不到进程。
  • Type=oneshot一次性任务:

    • 行为:程序执行完就退出了。systemd 不会认为它"挂了",而是认为它"完成任务了"。

    • 适用:初始化脚本,任务备份等。

    • 技巧:配合 RemainAfterExit=yes 使用,这样即使脚本跑完了,systemctl status 依然会显示它是 active (exited) 的。当你想跑初始化脚本时这样写:

      ini 复制代码
      Type=oneshot
      ExecStart=/opt/setup.sh
      RemainAfterExit=yes

👉 避坑总结:

  1. 如果你写的脚本是死循环,或默认在前台跑,则用simple
  2. 如果你的脚本里用了nohup ... &让程序后台跑,要么去掉 nohup 改用 simple,要么把类型改为 forking
  3. 如果你只是想开机跑个初始化脚本 ,跑完就结束,需要用 oneshot

五、写一个真实可用的开机服务(实战)

待补充,我还没合适的应用场景。


六、systemctl + journald:日志debug

写好了服务文件,也启动了,但是服务有bug刚启动就退出了,咋办?新手会狂敲systemctl status xxx然后盯着三行命令发呆,老手都知道,线索藏在journalctl里。systemd自带强大日志系统journalctl,他把所有服务的标准输出(stdout/stderr)都收集起来,学会这个你就能调试服务了。(老版本sysVinit中使用syslogd)

6.1 journalctl 基本用法

假设你的服务叫 myapp.service,下面介绍其基本用法:

shell 复制代码
journalctl -u  # 看所有服务揉在一起的日志
journalctl -u myapp  # 只看 myapp 服务的日志,显式所有log,很多很多。
journalctl -u myapp -f # -f:follow 模式,日志会像直播一样实时显示在终端里。显式最近10条。
journalctl -u myapp -b # 代表 boot,只看当前这次启动周期内的日志。但我看还是很多log啊
journalctl -u myapp -b --no-pager # 一次性把当前启动所有日志打印处理(说实话我没理解这个当前启动)

!NOTE

如果你是通过sudo权限才让编辑/etc/systemd/system/文件的单位文件,那么上面的命令需要加上sudo权限。

6.2 常见debug套路

  • 服务启动失败或秒退

    • 现象:systemctl status 显示 failed
    • 排查步骤:systemctl status myapp看退出码,然后再journalctl看日志找原因。
  • environment 变量缺失

    • 现象:你在终端里手动运行脚本好好的,但放到 systemd 里就报错说"command not found"或"配置加载失败"。

    • 原因:systemd 启动服务时,环境变量非常干净(甚至可以说是简陋),没有你熟悉的 $PATH

    • 解决:在 Unit 文件的 [Service] 段里显式指定环境变量(用全局路径可能可以减少此类问题):

      ini 复制代码
      [Service]
      Environment="PATH=/usr/local/bin:/usr/bin:/bin"
      Environment="MY_APP_CONFIG=/etc/myapp/config.yaml"

七、进阶:targets / timers / 依赖关系(选读)

到这,你已经掌握了90%的systemctl的用法,下面可能用不到,但一但遇到复杂场景(比如服务器开机黑屏、定时任务不执行、服务启动顺序错乱),它们就是你的救命稻草。

7.1 target 是什么?

在老版本的 Linux(SysVinit)中,有一个概念叫"运行级别"(Runlevel),比如 Runlevel 3 是多用户文本模式,Runlevel 5 是图形界面模式。systemdTarget 取代了 Runlevel。你可以把 Target 理解为 "系统状态里程碑"

  • Target 不是一个具体的服务,而是一个服务的集合
  • 当系统启动到某个 Target 时,意味着这个 Target 包含的所有服务(以及依赖的服务)都应该已经启动了。

最常用的两个Target:

multi-user.target(对应 Runlevel 3)

  • 含义:多用户命令行模式。
  • 场景 :这是大多数服务器的标准状态。系统启动,网络通了,SSH 能连了,但没有图形桌面界面

graphical.target(对应 Runlevel 5)

  • 含义:图形界面模式。
  • 场景 :它不仅包含了 multi-user.target 的所有功能,还额外启动了显卡驱动、显示管理器(GDM/KDM)等。这是你个人电脑(Ubuntu Desktop/CentOS GUI)的默认状态。

常用命令:

  • 查看当前默认启动目标:

    shell 复制代码
    systemctl get-default
  • 修改默认启动目标(比如服务器想省资源,不想进图形界面):

    shell 复制代码
    sudo systemctl set-default multi-user.target

7.2 timer:替代 crontab 的现代方案

cron 大家都很熟悉(熟悉个der,运维才经常用这个命令,crontab 是 Linux 里最传统的"定时执行任务"的机制),但它在 systemd 时代显得有些"原始"。systemd 提供了 Timer 单元,用来替代定时任务。

systemd timer 可以精确到秒甚至毫秒。timer 可以直接用 journalctl 看日志。systemd timer 支持 Persistent=true,开机后会自动补跑错过的任务。总结起来就是比cron更精确,更便捷。

怎么使用?

Timer 需要成对出现:一个 .service(干什么)和一个 .timer(什么时候干)。

示例:每 5 分钟执行一次备份脚本

  1. 定义任务 (backup.service)

    ini 复制代码
    [Uint]
    Description=Run Backup Script
    
    [Service]
    Type=oneshot
    ExecStart=/opt/scripts/backup.sh
  2. 定义时间 (backup.timer)('.'前名字要一样哦):

    ini 复制代码
    [Unit]
    Description=Run Backup Every 5 Mins
    
    [Timer]
    OnBootSec=5min
    OnUnitActiveSec=5min
    Persistent=true
    
    [Install]
    WantedBy=timers.target
  3. 启动

    shell 复制代码
    sudo systemctl enable --now backup.timer

7.3 服务依赖与启动顺序控制

排序:在 init.d 时代,服务启动顺序是靠脚本文件名前面的数字(S01, S02...)决定的,非常僵化,而在systemd 中,我们通过 Unit 文件的 [Unit] 字段中的AfterBefore来排序。

依赖:在systemd 中,我们通过 Unit 文件的 [Unit] 字中的RequiresWants来精确控制依赖。

排序(Ordering):谁先谁后?

  • After=network.target:意思是" 网络好了,启动我"。
  • Before=xxx.service:意思是"我必须在 xxx 之前启动"。
  • 注意After 仅仅管顺序,不管死活。如果 network.target 启动失败了,配置了 After 的服务依然会尝试启动。

依赖(Dependency):没你不行?

  • Requires=postgresql.service强依赖 。如果数据库启动失败,我的服务也别想启动(直接报错退出)。
  • Wants=redis.service弱依赖 。我会尝试启动 Redis,但如果 Redis 挂了,我的服务依然会继续启动

简单例子:对于 Web 应用来说,最稳妥的配置通常是:

ini 复制代码
[Unit]
Description=My Web App
# 强依赖数据库,如果数据库挂了我也挂死
Requires=postgresql.service
# 弱依赖缓存,缓存挂了我也能跑
Wants=redis.service
# 必须等网络和数据库都起来了,我才能动
After=network.target postgresql.service

有了该配置,你就不需要再写复杂的Shell脚本在ExecStart 里用 while 循环去检查数据侧端口是否正常了,------systemd 帮你搞定了一切。


八、我在使用 systemctl 中踩过的坑(个人经验总结)

讲完理论,最后讲一些实战容易翻车的细节,希望这些血泪史能帮你避坑。

8.1 忘了 daemon-reload:最经典的"假死"现场

场景 :新手最常犯的错误,你兴冲冲地修改了 /etc/systemd/system/myapp.service,把端口从 8080 改到了 9090。然后执行 systemctl restart myapp。发现改没改都一样,为啥?

原因systemd 为了性能,会把 Unit 文件配置缓存到内存,你修改了硬盘上的文件,但是内存的配置还是旧的。

解决办法 :养成肌肉记忆,修改文件后,必须先执行:sudo systemctl daemon-reload

8.2 ExecStart 写 Shell 脚本但没加 Type=oneshot

场景 :你想启动一个复杂的 Java 应用,于是写了个 start.sh 脚本来组装命令。

复制代码
[Service]
ExecStart=/opt/app/start.sh

结果服务启动后瞬间变成 inactive (dead)

原因 :Shell 脚本的执行机制是:启动 -> 执行命令 -> 退出。对于 Type=simple(默认值为simple),systemd 认为 ExecStart 指定的进程必须一直活着。脚本执行完就退出了,systemd 以为服务挂了,于是把它标记为停止。

解决 :如果必须用脚本,且脚本只是初始化一下就跑完了,记得加上 Type=oneshotRemainAfterExit=yes

8.3 环境变量和 PATH 问题("明明手动能跑,服务就跑不了")

场景 :你在终端里 ./myapp 跑得飞起,但放到 systemd 里就报错 command not found 或者 file not found

原因

  • PATH 不同systemd 启动的环境非常纯净,它的 $PATH 变量通常只有 /usr/local/bin:/usr/bin:/bin。如果你脚本里用了相对路径,或者命令在 /opt/local/bin 下,它就找不到。
  • 环境变量缺失 :你在 .bashrc 里配置了 JAVA_HOMELD_LIBRARY_PATH,但 systemd 并不会加载 .bashrc

解决

  • 绝对路径 :在 ExecStart 里永远写绝对路径(如 /usr/bin/python3 而不是 python3)。
  • 显式声明 :在 Unit 文件里通过Environment手动指定环境变量如:Environment="PATH=/usr/local/bin:/usr/bin:/bin:/opt/myapp/bin"Environment="JAVA_HOME=/usr/lib/jvm/java-11"

8.4 U-Boot / Embedded Linux 等场景下没用 systemd

场景 :如果你是在做嵌入式开发(比如基于 ARM 的板子),可能会遇到 systemctl 命令报错:
System has not been booted with systemd as init system (PID 1).

原因

  • 很多精简版的 Linux(如 Alpine, OpenWrt)或者嵌入式系统,为了省资源,默认使用 SysVinitOpenRCBusyBox init,而不是 systemd
  • 即使安装了 systemd 包,如果内核启动参数(cmdline)里指定了 init=/bin/shsystemd 也不会作为 PID 1 启动。

解决

  • 确认 PID 1 是谁:ps -p 1 -o comm=
  • 如果不是 systemd,那就别折腾 systemctl 了,老老实实写 /etc/init.d/ 脚本吧。

九、什么时候得用 systemd ?啥时候不适合用?

虽然 systemd 现在是 Linux 发行版的主流,但它并不是万能的。作为一个专业的系统工程师,要知道它的边界在哪里。什么时候必须要用,什么时候不适合使用。

适合

  • 长期运行的守护进程 :比如 Web 服务器(Nginx)、数据库(MySQL)、应用服务。systemd 的自动重启、资源限制和日志收集功能,是管理这类服务的最佳拍档。
  • 有复杂依赖关系的场景 :如果你的服务启动依赖于网络、磁盘挂载或者其他服务,systemdAfterRequires 等依赖管理功能,能帮你省去写复杂 Shell 脚本的麻烦

不适合

  • 纯一次性任务 :虽然可以用 Type=oneshot,但如果你的脚本只是开机跑一次就完事,完全不需要守护和重启,那么传统的 /etc/rc.local 或者简单的 init 脚本可能更轻量、更直接。
  • 极简或容器化系统
    • 嵌入式/精简版 Linux :像 Alpine Linux、OpenWrt 或者基于 BusyBox 的系统,它们的设计哲学是"小而美",通常使用 OpenRCBusyBox init。在这些环境下强行安装 systemd 既困难又没必要。
    • Docker 容器 :容器的最佳实践是"一个容器一个进程"。在 Docker 容器里跑 systemd 会增加不必要的复杂度和资源开销(虽然技术上可行),通常我们更推荐直接在前台启动主进程。

总结 :工具没有好坏,只有适不适合。systemctl 是现代服务器的利器,但在极简环境下,别强求它。


十、总结及 systemctl 推荐阅读

虽然这篇博客覆盖了核心用法,但 systemd 的博大精深远超于此。如果你想继续深入,建议按照以下路径探索:

  • 入门 :熟练使用 systemctl 管理现有服务,习惯看 journalctl 日志。
  • 进阶 :尝试编写自定义 .service 文件,理解 Type=simpleoneshot 的区别。
  • 高阶 :研究 .timer 替代 Crontab,使用 .socket 实现按需启动,或者配置 cgroup 限制服务的 CPU/内存使用。

推荐进一步阅读

最好的文档永远是官方文档(Man Pages)。当你遇到不懂的参数时,直接查阅手册:

  • man systemd.service :这是最核心的手册,详细解释了 [Service] 字段里每一个参数的含义(如 RestartSec, Environment 等)。
  • man systemd.unit :了解 Unit 文件的通用格式和依赖逻辑,(如 讲解了BeforeAfter 等的用法)。
  • man journalctl:探索更多日志过滤和导出的技巧。

希望这篇博客能成为你手边的一本小册子,在下次面对各种service时,让你更有底气。祝你的服务永远 active (running)

相关推荐
嵌入式×边缘AI:打怪升级日志2 小时前
100ASK-T113 Pro 开发板 Bootloader 完全开发指南
linux·ubuntu·bootloader
charlie1145141914 小时前
Linux 字符设备驱动:cdev、设备号与设备模型
linux·开发语言·驱动开发·c
handler014 小时前
Linux 内核剖析:进程优先级、上下文切换与 O(1) 调度算法
linux·运维·c语言·开发语言·c++·笔记·算法
zhouwy1134 小时前
Linux进程与线程编程详解
linux·c++
我星期八休息4 小时前
IT疑难杂症诊疗室:AI时代工程师Superpowers进化论
linux·开发语言·数据结构·人工智能·python·散列表
切糕师学AI4 小时前
深入解析 Zsh 与 Oh-My-Zsh:打造高效现代化终端
linux·终端·zsh
切糕师学AI5 小时前
Ubuntu 下 Git 完全使用指南
linux·git·ubuntu
浪客灿心6 小时前
Linux网络传输层协议
linux·运维·网络
舟遥遥娓飘飘6 小时前
Nexus4CC 手机电脑同步claude code对话部署教程(基于linux系统)
linux·智能手机·电脑