【Linux进程通信】二、匿名管道

Ⅰ. 管道一、管道的概念

​ 管道是 unix 中最古老的进程间基于文件系统通信的形式。我们把从一个进程连接到另一个进程的一个数据流称为一个 “管道”。注意管道是单向连通的,不存在说双向管道,就像生活中水往低处流而不会往高处流一样!

【Linux进程通信】二、匿名管道

​ 进程 A 通过管道将数据写入到 “公共内存” 中,并且进程 B 可以从该段 “公共内存” 中读取这些数据,这样子的话就达到了两个进程之间的交互!

​ 那么有人可能会有问题:既然这段 “公共内存” 是共享的并且都是基于文件系统的,那这个管道文件是不是在磁盘上面呢,然后进程A通过写入到磁盘中的管道文件,进程B再去读取这样子的方式 ❓❓❓

​ 其实不是的,因为我们都知道,文件 IO 的效率是相当的低的,所以操作系统肯定不笨,操作系统会将这个管道文件 load 到内存中,也就是内存级别的文件,而我们两个进程之间只需要和这个内存级文件打交道即可,这样子大大的提高了效率!

​ 任何一个文件包括两套资源:

struct file 的操作方法有属于自己的内核缓冲区,所以父进程和子进程有一份公共的资源:文件系统提供的内核缓冲区,父进程可以向对应的文件的文件缓冲区写入,子进程可以通过文件缓冲区读取,此时就完成了进程间通信,这种方式提供的文件称为管道文件。管道文件本质就是内存级文件,不需要 IO

​ 管道的本质是内核中的缓冲区,通过内核缓冲区实现通信,管道文件只是一个标识符,用于让多个进程能够访问同一块缓冲区,并非通信介质。

​ 并且通过这个内存级别文件的不同形式,我们分为匿名管道和命名管道,后面我们来一一介绍!

二、管道通信原理

​ 通过我们上面所说的,管道是个 内存级别的文件(struct file 中特有的,就像内核缓冲区一样),我们要知道为什么它会叫做 “管道” 呢,是因为它一开始就叫做 “管道” 吗,那肯定不是,是因为它的原理和 “管道” 是类似的,人们后期才会将其命名为 “管道”,而管道是单通向的,不存在双向管道!

​ 并且我们生活中常见的管道比如说水管、油等等它们的作用就是来传输水和油,那么我们计算机中的管道就是传输数据,那么这个流向肯定要是一条单向的,我们就不能想象出其原理结构:

【Linux进程通信】二、匿名管道

​ 除此之外,我们可以再说说普通文件,我们父进程创建子进程,子进程拷贝父进程的文件描述符表,所以都指向 log 文件,而每次我们写入完毕之后,可能会存在写满等情况就会刷新,那么会访问磁盘,每次读取时候又将 log 加载进内存,不断的来回,这样子 IO 次数非常的多,效率也就非常的低,所以说为什么存在管道文件,其实就是为了避开普通文件通信的效率底下问题!

【Linux进程通信】二、匿名管道

Ⅱ. 匿名管道一、匿名管道的原理与创建方法

​ 在讲匿名管道之前呢,我们必须知道管道它的通信方法,而对于匿名管道来说,其实就是 通过 fork 创建子进程!(而命名管道的方法不需要创建子进程,后面会讲)

​ 为什么对于匿名管道来说需要创建子进程呢 ❓❓❓

​ 匿名管道的名称由来也是因为这个原因,我们需要通过 fork 创建子进程,我们都知道,子进程会继承父进程的大部分属性和内容包括文件描述符(若发生写时拷贝则会改变),也就是说父进程指向的文件 file,子进程也会指向同一个文件 file(父子进程文件描述符表是独立的,但是指向的文件是同一个),而就像我们下图,父子进程可以都指向该管道文件,这样子的话我们就无需说让子进程和父进程去专门创建一个带名称的管道并且指向它,也就是说我们可以 利用父子进程的继承性让子进程继承这个管道文件达到共同指向它的目的,所以这个管道文件没必要带名称,所以叫做匿名管道!

​ 所以,看待管道,就如同看待文件一样!管道的使用和文件一致,迎合了 “Linux 一切皆文件思想“!

【Linux进程通信】二、匿名管道

​ 那么我们如何创建这个匿名管道文件呢 ❓❓❓

​ 下面就得调用我们的系统函数 pipe()

代码语言:javascript代码运行次数:0运行复制

#include int pipe(int pipefd[2]);// 功能:创建一无名管道// 参数:pipefd为文件描述符数组,其中fd[0]表示读端,fd[1]表示写端// 返回值:成功返回0,失败返回-1且设置错误代码

​ 大家是不是就会很疑惑这个 pipefd[2] 数组里面放的是固定的值吗,其实不是的,它会根据当前进程打开文件的个数也就是文件描述符表中 fd_array[] 的有效个数进行返回,但是我们能确定的就是其中 fd[0] 表示的是读,fd[1] 表示的是写,而不需要我们关心具体的 fd 是多少!

​ 假设我们的进程没有打开文件,也就是说只打开了默认的三个标准输入输出流,那么自然我们的 fd[0] = 3fd[1] = 4,但如果我们开了一个文件,那么 fd[0] = 4fd[1] = 5,所以说我们关系的是 fd[0]fd[1] 而不是具体的 3 还是 4

​ 知道了如何创建匿名管道文件,下面我们来看看如何建立起父子进程之间的通信,也就是让他们看到同一份资源!下面假设我们让父进程进行写入,而子进程进行读取来模拟过程:

首先我们要创建一个父进程,并让这个父进程创建并指向这个管道文件接着父进程 fork 创建子进程, 父子进程同时指向管道文件(子进程拷贝父进程大部分内容属性),并且 fd[0]fd[1] 都是打开的然后父进程关闭读端也就是 fd[0],子进程关闭写端也就是 fd[1],这样子的话就形成了一条单向的管道!

【Linux进程通信】二、匿名管道

​ 有人会问能不能不关父进程的读端等等,答案肯定是不行的,因为不关的话可能在读写的时候会发生一些问题,所以 不是我们要的管道通向是必须关的!

​ 至于在通信完毕之后,要不要将父子进程中对应的管道关闭呢,这个是建议关闭的,因为不关闭的话,怕会误操作等等!

二、管道读写特征

​ 首先我们先打印一个没有手动打开文件的进程,看看它对应的 fd[0]fd[1] 是多少:

代码语言:javascript代码运行次数:0运行复制

#include #include #include using namespace std;int main(){    int fds[2];     int n = pipe(fds);    assert(n != -1);    // fds[0]:读    // fds[1]:写    cout << "fds[0]: " << fds[0] << endl;    cout << "fds[1]: " << fds[1] << endl;    return 0;}
【Linux进程通信】二、匿名管道

​ 和我们上面的判断一样!

​ 接下来我们来根据上面讲的匿名管道原理,根据通信的步骤,完成父子进程的通信!(下面以子进程写入,父进程读取为例)

① 读取快,写入慢的情况

​ 这里所谓的读取快,写入慢其实就是子进程写入的时候让他先 sleep 一会再写入:

代码语言:javascript代码运行次数:0运行复制

#include #include #include #include #include #include #include #include using namespace std;int main(){    // 1、创建管道文件,打开读写端    int fds[2];     int n = pipe(fds);    assert(n == 0);    // 2、创建子进程    pid_t id = fork();    assert(id >= 0);    if(id == 0)    {        // 子进程的通信代码        close(fds[0]); // 子进程关掉读端        int cnt = 0;        const char* msg = "i am child, sending msg now!"; // 子进程要发的信息        while(true)        {            char buffer[1024]; // 只有子进程能看到                        // snprintf只是sprintf加上了写入个数,将格式化内容转化为字符串            snprintf(buffer, sizeof(buffer), "child msg: %s[%d][%d]", msg, cnt++, getpid());            write(fds[1], buffer, strlen(buffer)); // 不需要算入''               sleep(1); // 每隔一秒写一次,这是这个情况的关键点          }        close(fds[1]); // 建议最后关闭子进程写端        cout << "子进程关闭自己的写端" < 0)        {            buffer[n] = '';            cout << "Get msg# " << buffer << " parent_pid: " << getpid() << endl;        }    }    // 回收子进程    n = waitpid(id, NULL, 0);    assert(n == id);    close(fds[0]); // 建议最后关掉读端    return 0;}

​ 下面写个监控脚本:

代码语言:javascript代码运行次数:0运行复制

while :; do ps ajx | head -1 && ps ajx | grep a.out ; sleep 1 ; done          
【Linux进程通信】二、匿名管道

​ 可以看到每 sleep 一秒后父进程接收一次信息,如果我们将 sleep 变成五秒,那么其实父进程会一直处于阻塞状态(监控脚本中看到的 S+ 状态),等待着子进程写入数据,才会响应!也就是说,如果管道中没有写入数据,那么读取的那一端默认会进入阻塞状态!

​ 为了验证一下,我们在父进程读取数据那行代码前后打印一些信息来看看:

代码语言:javascript代码运行次数:0运行复制

cout << "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" << endl;ssize_t n = read(fds[0], buffer, sizeof(buffer) - 1); // 多留一个位置放cout << "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" << endl;
【Linux进程通信】二、匿名管道

② 读取慢,写入快的情况

​ 这个情况和情况①是相反的,也就是让父子进程睡眠的顺序交换一下,当父进程在读取的时候让它 sleep 上一会,然后再读取,而子进程不停的写入,整体代码是不变的:

【Linux进程通信】二、匿名管道
【Linux进程通信】二、匿名管道

​ 可以看到,在父进程睡眠的五秒期间,子进程其实在不断的写入,所以每隔五秒接收子进程的信息的时候,就是一堆的数据!

​ 并且我们可以将父进程的睡眠时间调久一点,不让父进程去接收,看看让子进程不断的写入数据,我们可以在子进程中打印一下 cnt 的大小看看是怎么变化的,最后会不会这段管道文件被填满:

【Linux进程通信】二、匿名管道
【Linux进程通信】二、匿名管道

​ 可以看出来,管道文件是有固定大小的,是会被写满的!侧面说明 写入端写满的时候,写入端会阻塞,等待读取端读取!

③ 写入端提前关闭的情况

​ 这里让写入端提前关闭,也就是这里子进程我们让它打印一遍后就直接 break,接着它就会关掉自己的写入端,此时父进程也就是读取端,调用 read 接收的时候,返回值为 0,所以我们可以加以判断后退出。(注意,如果写入端没有被关闭,即使没有写入,那么读取端用 read 的时候也不会返回 0 的!)

【Linux进程通信】二、匿名管道
【Linux进程通信】二、匿名管道

​ 也就是说,写入端提前退出,读取端会读到返回值0!

④ 读取端提前关闭的情况

​ 操作系统很聪明,对于这种读取端提前关闭的情况,操作系统会给写入端进程发送信号,终止写入端,因为没人在接收,那么这只是在浪费系统资源,下面我们用 status 来接受一下子进程的终止信号看看效果:

【Linux进程通信】二、匿名管道

三、总结读写规则

通过上面的几种情况,我们可以看到管道读写的几种规则(额外补充一些):

当没有数据可读时 O_NONBLOCK disable:read 调用阻塞,即进程暂停执行,一直等到有数据来到为止O_NONBLOCK enable:read 调用返回 -1errno 值为 EAGAIN当管道满的时候 O_NONBLOCK disable: write 调用阻塞,直到有进程读走数据O_NONBLOCK enable:调用返回 -1errno 值为 EAGAIN

如果所有管道写入端对应的文件描述符被关闭,则 读取端 read 返回 0 如果所有管道读取端对应的文件描述符被关闭,则 write 操作会产生信号 SIGPIPE ,进而 终止写入端 write 当要写入的数据量不大于 PIPE_BUF 时,linux 将保证写入的原子性。 当要写入的数据量大于 PIPE_BUF 时,linux 将不再保证写入的原子性。 Ⅲ. 管道的特征

1、一般而言,进程退出,管道释放,所以 管道的生命周期随进程。

2、管道用来进行具有血缘关系的进程之间进行通信,常用于父子通信。

3、管道是 面向字节流 的。(网络)

4、管道是 半双工 的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道。

5、一般而言,内核会对管道操作进行同步与互斥,这是一种对共享资源进行保护的方案。

6、管道本质是内核中的缓冲区,命名管道文件只是标识,用于让多个进程找到同一块缓冲区,删除后,之前已经打开管道的进程依然可以通信

【Linux进程通信】二、匿名管道

Ⅳ. 自己写一个匿名管道池

​ 所谓的匿名管道池,其实就是我们的父进程也就是当前进程,有一组子进程等待父进程调度,读取父进程写入的信号进行某些工作的完成,这个就是匿名管道池!池化技术应用也是非常广泛的,这里我们试着写一个小demo!

​ 大概的设计思路就是如下,一个父进程通过创建各自的管道文件对各自的子进程分别管理起来!

【Linux进程通信】二、匿名管道

​ 首先我们需要先创建一个管道进程池,其实就是一个数组,其中数组每个元素都是一个结构体,结构体中需要包括每个对应管道文件的写入端文件描述符、子进程的 pid 等等。

​ 可能会有人想为什么不加上那个管道文件的读取端文件描述符呢 ❓❓❓

​ 其实是因为我们在到时候在子进程执行命令那部分代码上面,会调用 pipe() 产生匿名管道,而下面的子进程是能轻易的拿到这个读取端的文件描述符,而对于父进程,它需要管理多个子进程,所以我们需要将每个写端文件描述符记录起来,以便管理!

​ 具体的实现看下面的代码(其中细节挺多,主要从 main 函数切入,并且在 CreateProcessPool 函数也就是创建管道池中存在 bug,需要我们修复,就是每次产生的子进程会拷贝父进程的读写端,如果有多个子进程的话,那么就会造成多个写入端同时指向一个子进程的情况,所以我们必须采取一些措施,具体看代码!):

完整代码:代码语言:javascript代码运行次数:0运行复制

#include #include #include #include #include #include #include #include #include #include using namespace std;#define MakeSeed() srand((unsigned long)time(nullptr) ^ getpid() ^ 0x171237 ^ rand() % 1234)///中间部分为任务功能部分/typedef void(* func_t)(); // 函数指针void IOTask(){    cout << getpid() << " : IO Task runningn" << endl;    sleep(1);}void DownloadTask(){    cout << getpid() << " : Download Task runningn" << endl;    sleep(1);}void flushTask(){    cout << getpid() << " : flush Task runningn" << endl;    sleep(1);}void LoadFunction(vector* funcMap){    assert(funcMap);    funcMap->push_back(IOTask);    funcMap->push_back(DownloadTask);    funcMap->push_back(flushTask);}///以下为管道池的管理const int PROCESS_NUM = 5; // 子进程池的个数// 描述每对子进程和管道文件的结构体class childProcess{public:    childProcess(pid_t pid, int writeFd)        :_pid(pid), _writeFd(writeFd)    {        char buffer[1024];        snprintf(buffer, sizeof(buffer), "process-%d[pid(%d)|writeFd(%d)]", num++, _pid, _writeFd);        _name = buffer;    }public:    string _name; // 以统一的规则命名的名称    pid_t _pid; // 子进程pid    int _writeFd; // 管道文件的写入端    static int num; // 当前子进程为第几个进程编号};int childProcess::num = 0;void SendTask(const childProcess& cp, int index_func){    cout << "Send task num: " << index_func < " << cp._name << endl;    // 发送任务就是向管道里写入数据    int n = write(cp._writeFd, &index_func, sizeof(index_func));    assert(n == sizeof(int)); // 断言一下,发送的字节大小必须为int类型大小    (void)n;}int ReceiveTask(int readFd){    int code = 0;    ssize_t n = read(readFd, &code, sizeof(code));    if(n == 4)         return code;     else if(n <= 0)        return -1;    else         return 0;}void CreateProcessPool(vector* pipePool, vector& funcMap){    vector deleteFd; // 记录每次要关闭的前面的子进程的写端    for(int i = 0; i = 0); // 断言一下是否fork成功        // 子进程的执行部分        if(id == 0)        {            // 每次删掉子进程拷贝父进程的前n个写入端指向            for(int i = 0; i = 0 && commandCode push_back(move(cp)); // 将该对象调用move移动构造到管道池        deleteFd.push_back(pipefd[1]); // 记录每个子进程的前n个写入端fd    }}void loadBlanceContrl(vector& cp, vector& fmp, int taskCnt){    int pipe_size = cp.size(); // 管道池个数    int func_size = fmp.size(); // 任务个数    bool forever = (taskCnt == 0 ? true : false); // 判断是否为永远    while(true)    {        // 1、选择一个子进程        int index_process = rand() % pipe_size;        // 2、选择一个任务        int index_func = rand() % func_size;        // 3、任务发送给选择的进程        SendTask(cp[index_process], index_func);        sleep(1);        if(!forever)        {            taskCnt--;            if(taskCnt == 0) break;           }    }    // 关闭写入端    for(int i = 0; i < PROCESS_NUM; ++i)        close(cp[i]._writeFd); // 类似堆栈的方式的原理关闭的写入端}void waitProcess(vector& cp){    for(int i = 0; i < PROCESS_NUM; ++i)    {        // 这里不做退出码等处理        waitpid(cp[i]._pid, nullptr, 0);        cout < " << cp[i]._pid << endl;    }}int main(){    MakeSeed(); // 随机种子    // 1、创建管道进程池和任务列表    vector funcMap;    LoadFunction(&funcMap);    vector pipePool;    CreateProcessPool(&pipePool, funcMap);    // 2、父进程控制子进程完成任务,负载均衡的向子进程发送命令码,若父进程退出则关闭子进程    int taskCnt = 3; // 0: 永远进行,其它则表示计数    loadBlanceContrl(pipePool, funcMap, taskCnt);        // 3、回收子进程信息    waitProcess(pipePool);    return 0;}

以上就是【Linux进程通信】二、匿名管道的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/74039.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月14日 01:19:13
下一篇 2025年11月14日 01:55:57

相关推荐

  • 怎样用免费工具美化PPT_免费美化PPT的实用方法分享

    利用KIMI智能助手可免费将PPT美化为科技感风格,但需核对文字准确性;2. 天工AI擅长优化内容结构,提升逻辑性,适合高质量内容需求;3. SlidesAI支持语音输入与自动排版,操作便捷,利于紧急场景;4. Prezo提供多种模板,自动生成图文并茂幻灯片,适合学生与初创团队。 如果您有一份内容完…

    2025年12月6日 软件教程
    000
  • Pages怎么协作编辑同一文档 Pages多人实时协作的流程

    首先启用Pages共享功能,点击右上角共享按钮并选择“添加协作者”,设置为可编辑并生成链接;接着复制链接通过邮件或社交软件发送给成员,确保其使用Apple ID登录iCloud后即可加入编辑;也可直接在共享菜单中输入邮箱地址定向邀请,设定编辑权限后发送;最后在共享面板中管理协作者权限,查看实时在线状…

    2025年12月6日 软件教程
    100
  • REDMI K90系列正式发布,售价2599元起!

    10月23日,redmi k90系列正式亮相,推出redmi k90与redmi k90 pro max两款新机。其中,redmi k90搭载骁龙8至尊版处理器、7100mah大电池及100w有线快充等多项旗舰配置,起售价为2599元,官方称其为k系列迄今为止最完整的标准版本。 图源:REDMI红米…

    2025年12月6日 行业动态
    200
  • Linux中如何安装Nginx服务_Linux安装Nginx服务的完整指南

    首先更新系统软件包,然后通过对应包管理器安装Nginx,启动并启用服务,开放防火墙端口,最后验证欢迎页显示以确认安装成功。 在Linux系统中安装Nginx服务是搭建Web服务器的第一步。Nginx以高性能、低资源消耗和良好的并发处理能力著称,广泛用于静态内容服务、反向代理和负载均衡。以下是在主流L…

    2025年12月6日 运维
    000
  • Linux journalctl与systemctl status结合分析

    先看 systemctl status 确认服务状态,再用 journalctl 查看详细日志。例如 nginx 启动失败时,systemctl status 显示 Active: failed,journalctl -u nginx 发现端口 80 被占用,结合两者可快速定位问题根源。 在 Lin…

    2025年12月6日 运维
    100
  • 华为新机发布计划曝光:Pura 90系列或明年4月登场

    近日,有数码博主透露了华为2025年至2026年的新品规划,其中pura 90系列预计在2026年4月发布,有望成为华为新一代影像旗舰。根据路线图,华为将在2025年底至2026年陆续推出mate 80系列、折叠屏新机mate x7系列以及nova 15系列,而pura 90系列则将成为2026年上…

    2025年12月6日 行业动态
    100
  • Linux如何防止缓冲区溢出_Linux防止缓冲区溢出的安全措施

    缓冲区溢出可通过栈保护、ASLR、NX bit、安全编译选项和良好编码实践来防范。1. 使用-fstack-protector-strong插入canary检测栈破坏;2. 启用ASLR(kernel.randomize_va_space=2)随机化内存布局;3. 利用NX bit标记不可执行内存页…

    2025年12月6日 运维
    000
  • Linux如何优化系统性能_Linux系统性能优化的实用方法

    优化Linux性能需先监控资源使用,通过top、vmstat等命令分析负载,再调整内核参数如TCP优化与内存交换,结合关闭无用服务、选用合适文件系统与I/O调度器,持续按需调优以提升系统效率。 Linux系统性能优化的核心在于合理配置资源、监控系统状态并及时调整瓶颈环节。通过一系列实用手段,可以显著…

    2025年12月6日 运维
    000
  • Pboot插件数据库连接的配置教程_Pboot插件数据库备份的自动化脚本

    首先配置PbootCMS数据库连接参数,确保插件正常访问;接着创建auto_backup.php脚本实现备份功能;然后通过Windows任务计划程序或Linux Cron定时执行该脚本,完成自动化备份流程。 如果您正在开发或维护一个基于PbootCMS的网站,并希望实现插件对数据库的连接配置以及自动…

    2025年12月6日 软件教程
    000
  • Linux命令行中wc命令的实用技巧

    wc命令可统计文件的行数、单词数、字符数和字节数,常用-l统计行数,如wc -l /etc/passwd查看用户数量;结合grep可分析日志,如grep “error” logfile.txt | wc -l统计错误行数;-w统计单词数,-m统计字符数(含空格换行),-c统计…

    2025年12月6日 运维
    000
  • Linux命令行中fc命令的使用方法

    fc 是 Linux 中用于管理命令历史的工具,可查看、编辑并重新执行历史命令。输入 fc 直接编辑最近一条命令,默认调用 $EDITOR 打开编辑器修改后自动执行;通过 fc 100 110 或 fc -5 -1 可批量编辑指定范围的历史命令,保存后按序重跑;使用 fc -l 列出命令历史,支持起…

    2025年12月6日 运维
    000
  • 曝小米17 Air正在筹备 超薄机身+2亿像素+eSIM技术?

    近日,手机行业再度掀起超薄机型热潮,三星与苹果已相继推出s25 edge与iphone air等轻薄旗舰,引发市场高度关注。在此趋势下,多家国产厂商被曝正积极布局相关技术,加速抢占这一细分赛道。据业内人士消息,小米的超薄旗舰机型小米17 air已进入筹备阶段。 小米17 Pro 爆料显示,小米正在评…

    2025年12月6日 行业动态
    000
  • 荣耀手表5Pro 10月23日正式开启首销国补优惠价1359.2元起售

    荣耀手表5pro自9月25日开启全渠道预售以来,市场热度持续攀升,上市初期便迎来抢购热潮,一度出现全线售罄、供不应求的局面。10月23日,荣耀手表5pro正式迎来首销,提供蓝牙版与esim版两种选择。其中,蓝牙版本的攀登者(橙色)、开拓者(黑色)和远航者(灰色)首销期间享受国补优惠价,到手价为135…

    2025年12月6日 行业动态
    000
  • Vue.js应用中配置环境变量:灵活管理后端通信地址

    在%ignore_a_1%应用中,灵活配置后端api地址等参数是开发与部署的关键。本文将详细介绍两种主要的环境变量配置方法:推荐使用的`.env`文件,以及通过`cross-env`库在命令行中设置环境变量。通过这些方法,开发者可以轻松实现开发、测试、生产等不同环境下配置的动态切换,提高应用的可维护…

    2025年12月6日 web前端
    000
  • VSCode终端美化:功率线字体配置

    首先需安装Powerline字体如Nerd Fonts,再在VSCode设置中将terminal.integrated.fontFamily设为’FiraCode Nerd Font’等支持字体,最后配合oh-my-zsh的powerlevel10k等Shell主题启用完整美…

    2025年12月6日 开发工具
    000
  • Linux命令行中locate命令的快速查找方法

    locate命令通过查询数据库快速查找文件,使用-i可忽略大小写,-n限制结果数量,-c统计匹配项,-r支持正则表达式精确匹配,刚创建的文件需运行sudo updatedb更新数据库才能查到。 在Linux命令行中,locate 命令是快速查找文件和目录路径的高效工具。它不直接扫描整个文件系统,而是…

    2025年12月6日 运维
    000
  • 环境搭建docker环境下如何快速部署mysql集群

    使用Docker Compose部署MySQL主从集群,通过配置文件设置server-id和binlog,编写docker-compose.yml定义主从服务并组网,启动后创建复制用户并配置主从连接,最后验证数据同步是否正常。 在Docker环境下快速部署MySQL集群,关键在于合理使用Docker…

    2025年12月6日 数据库
    000
  • Linux文件系统rsync命令详解

    rsync通过增量同步高效复制文件,支持本地及远程同步,常用选项包括-a、-v、-z和–delete,结合SSH可安全传输数据,配合cron可实现定时备份。 rsync 是 Linux 系统中一个非常强大且常用的文件同步工具,能够高效地在本地或远程系统之间复制和同步文件与目录。它以“增量…

    2025年12月6日 运维
    000
  • Linux systemctl list-dependencies命令详解

    systemctl list-dependencies 用于查看 systemd 单元的依赖关系,帮助排查启动问题和优化启动流程。1. 基本语法为 systemctl list-dependencies [选项] [单元名称],默认显示 default.target 的依赖。2. 常见单元类型包括 …

    2025年12月6日 运维
    100
  • Xbox删忍龙美女角色 斯宾塞致敬板垣伴信被喷太虚伪

    近日,海外游戏推主@HaileyEira公开发表言论,批评Xbox负责人菲尔·斯宾塞不配向已故的《死或生》与《忍者龙剑传》系列之父板垣伴信致敬。她指出,Xbox并未真正尊重这位传奇制作人的创作遗产,反而在宣传相关作品时对内容进行了审查和删减。 所涉游戏为年初推出的《忍者龙剑传2:黑之章》,该作采用虚…

    2025年12月6日 游戏教程
    000

发表回复

登录后才能评论
关注微信