Linux/Uinx 系统编程:进程管理(3)

Linux/Uinx 系统编程:进程管理(3)

本章来讲解进程管理的最后一部分内容。

文章目录

  • Linux/Uinx 系统编程:进程管理(3)
    • I/O重定向
      • 原理
        • FILE结构体的内部结构
        • 重定向的实现过程
      • scanf 与 printf
        • scanf
        • printf
      • 重定向标准输入
        • 重定向示例代码
    • 管道
      • 管道的使用方式
        • 管道命令处理
      • 命名管道
        • 命名管道终端示例
        • 命名管道C程序示例

I/O重定向

shell中,我们可以通过:>或者<来执行重定向

将文件中的内容,输入到运行的程序中或者将程序的输出输入到文件中。

但是他们的原理是怎么样的呢?

原理

实际上,sh进程有三个用于终端I/O的文件流:stdin,stdout,stderr,每一个流本质上都是指向执行映像区FILE结构体的一个指针

下面给出FILE结构体的内容:

FILE结构体的内部结构

下面给出FILE结构体的组成,这里只提出一点,不多做介绍,感兴趣的读者可以自行查询相关资料。

#ifndef _FILE_DEFINED
struct _iobuf {
        char *_ptr;  //文件输入的下一个位置
        int   _cnt;   //当前缓冲区的相对位置
        char *_base;  //文件的起始位置
        int   _flag;  //文件标志
        int   _file;  //文件的有效性验证
        int   _charbuf;//检查缓冲区状况,若无缓冲区则不读取
        int   _bufsiz; //文件的大小
        char *_tmpfname;//临时文件名
        };
typedef struct _iobuf FILE;
#define _FILE_DEFINED
#endif  /* _FILE_DEFINED */
重定向的实现过程

上文已经说到,在每个C程序中,有三个用于输入输出的IO流。事实上,每个IO流都对应着Linux内核中的一个打开文件,用**文件描述符(数字)**表示。

stdinstdoutstderr的文件描述符号分别为0、1、2

当某个进程复刻出一个子进程时,该子进程会继承父进程的所有打开文件。因此,子进程也具有与父进程相同的文件流和文件描述符号。

最后一句话说明:Linux内核中的IO文件只有三个,所有进程共用这些文件

scanf 与 printf

scanf和printf函数本质上都是借用上面的三个文件来进行输入输出。下面来详细介绍一下具体原理:

scanf

scanf函数的工作原理是这样的:

  1. scanf首先会检查stdin(标准输入)这个流指向的FILE结构体中的缓冲数组是否有数据。
  2. 如果缓冲数组为空,scanf会执行read系统调用,通过FILE结构体中的文件描述符从0号文件(也就是标准输入)读取内容。
  3. 读取到的内容会被存放到缓冲数组中,然后scanf会从这个数组中读取信息。

因此他们之间的联系可以看成这样:

scanf与FILE和输入文件的关系

printf

printf函数的工作原理与scanf类似,但方向相反。以下是printf函数的基本工作原理:

  1. printf首先将你提供的格式化字符串和参数处理成一个完整的字符串。
  2. 这个字符串首先被放入stdout(标准输出)这个流指向的FILE结构体中的缓冲数组。
  3. 如果缓冲数组满了,或者遇到了换行符,或者你调用了fflush函数,printf会执行write系统调用,通过FILE结构体中的文件描述符将缓冲数组的内容写入1号文件(也就是标准输出)。

如下图:
printf与FILE和输出文件的关系

重定向标准输入

如何做到重定向呢?

由上面的原理可知:printf函数和scanf都是通过文件描述符在文件中读取内容的,虽然中间有缓冲区,但是本质上,就是在文件中的读取,因此我们能不能尝试更改FILE中的文件描述符呢?

答案是可以的,Linux下的C语言提供了一个函数dup将fd复制到数值最小的未使用的文件描述符号中。具体使用方法如下:

#include <unistd.h>
dup(fd);

其中fd是文件描述符。

重定向示例代码

我们先创建一个文件input表示输入的内容,以替换标准输入文件(fd = 0):

1 2 3 4 5

结尾没有换行和空格。

接下来写出重定向文件的内容:

/*************************************************************************
	> File Name: io.c
	> Author:Royi 
	> Mail:royi990001@gmail.com 
	> Created Time: Thu 01 Feb 2024 05:45:49 PM CST
	> Describe: 
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>


int main() {
    int fd = open("./input", O_RDONLY);
    char ch;
    close(0);
    dup(fd);
    while ((ch = getchar()) != '\n' && ch != EOF) {
        putchar(ch);
    }
    return 0;
}

这个程序先使用open函数以只读的形式打开了input文件,返回文件描述符号fd。

接下来使用close函数关闭文件描述符0,也就是标准输入文件,此时最小的未打开的文件描述符就是0

最后使用dup(fd),将创建的fd符号复制到最小的未打开的文件描述符的上。

经过这样的一番操作,scanf函数对应的stdin指向的FILE结构体中的fd不再是0,而是新的fd,也就是open函数返回的值。

运行程序时,会直接看到输出:

1 2 3 4 5

这样就完成了重定向。

当然C语言还有别的函数dup2,它具有两个参数:

#include <unistd.h>

dup2(fd1, fd2);

这个函数的作用是将fd1复制到fd2中,如果fd2是打开的状态,那么就先关闭它,然后进行复制,这样不需要我们调用close函数去关闭想要重定向的文件描述符,同时也可以重定向非最小未使用的文件描述符,大大提高了程序的灵活性,这里不再做解释。

管道

管道是用于进程交换数据的单项进程间通信通道。有一个读入端和写入端。

管道有两类:

  • 普通管道(匿名管道):用于相关进程(父子进程)。
  • 命名管道:用于不相关进程(非父子进程)。

管道的读、写进程按照以下的方式进行同步:

  • 管道上有数据时,读进程会根据需要读取(不会超过管道大小)
  • 管道上没有数据时,但仍有写进程读进程等待数据
  • 写进程将数据写入管道时,会唤醒读进程,使他们继续读取
  • 如果管道没有数据也没有写进程,读进程返回0,并停止读取
  • 如果管道仍然有写进程读进程等待数据
  • 写进程写入管道,如果管道有空间,会根据需要尽可能多的写入
  • 如果管道没有空间但是有读进程写进程会等待空间
  • 读进程读出管道时,会唤醒等待的写进程
  • 如果管道不再有读进程写进程会将这种情况视为管道中断错误,并终止写入

总结:0返回值意味着管道没有数据也没有写进程。只要有写进程,读进程就不会消失。如果读进程消失但是写进程没有消失的话就报错。

管道的使用方式

进程不能通过管道给自己传输数据,原因如下:

  • 如果进程先从管道中读取,那么将无法从读取的系统调用中返回,因为管道中没有内容,读进程会等待写进程写入,但是写进程是谁呢?是自己,这样就将自己锁死了
  • 相反,如果写入管道的话,需要读进程接收(在4KB以内没有问题,没有超出管道大小,可以成功,但是大部分数据超过4KB),在管道满了之后,写进程会等待读进程读出,但是读进程是谁呢?是自己,也相当于把自己锁死了

使用管道时,必须有两个进程,一个作为管道的输入进程,另一个作为管道的输出进程

接下来以一个程序示例:

/*************************************************************************
	> File Name: pipe.c
	> Author:Royi 
	> Mail:royi990001@gmail.com 
	> Created Time: Thu 01 Feb 2024 09:36:16 PM CST
	> Describe: parent to child with pipe 
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>

int pd[2], n, i;
pid_t pid;
char line[256];


int main() {
    pipe(pd);
    printf("pd = [%d, %d]\n", pd[0], pd[1]);
    if ((pid = fork()) == -1) {
        perror("fork");
        exit(1);
    }

    // parent 
    if (pid) {
        close(pd[0]);
        printf("I'm parent %d, and I closed pd[0]\n", getpid());
        while (i++ < 10) {
            printf("Parent %d is wrinting to pipe\n", getpid());
            n = write(pd[1], "I'm your PAPA", 16);
            printf("Parent %d wrote %d bytes to pipe\n", getpid(), n);
            sleep(1);
        }
        printf("Parent %d exited.", getpid());
    } else {
        // child
        close(pd[1]);
        printf("I'm child %d, and I closed pd[1]\n", getpid());
        while (1) {
            printf("child %d is reading from pipe\n", getpid());
            if ((n = read(pd[0], line, 128))) {
                line[n] = 0;
                printf("child read %d bytes from pipe: %s\n", n, line);
            } else {
                // pipe has no data and to writer.
                exit(0);
            }
            sleep(1);
        }
    }
    return 0;
}
  • 函数pipe()创建了一管道并且在pd[2]中返回了两个文件描述符。其中pd[0]用于从管道读取,pd[1]用于向管道中写入。
  • 为了让两个进程交替运行,在每个循环中加入了sleep函数。
  • 多运行几次发现,读进程在管道中没有数据时,会持续等待

运行示例结果:

pd = [3, 4]
I'm parent 3030, and I closed pd[0]
Parent 3030 is wrinting to pipe
Parent 3030 wrote 16 bytes to pipe
I'm child 3031, and I closed pd[1]
child 3031 is reading from pipe
child read 16 bytes from pipe: I'm your PAPA
Parent 3030 is wrinting to pipe
Parent 3030 wrote 16 bytes to pipe
child 3031 is reading from pipe
child read 16 bytes from pipe: I'm your PAPA
Parent 3030 is wrinting to pipe
Parent 3030 wrote 16 bytes to pipe
child 3031 is reading from pipe
child read 16 bytes from pipe: I'm your PAPA
Parent 3030 is wrinting to pipe
Parent 3030 wrote 16 bytes to pipe
child 3031 is reading from pipe
child read 16 bytes from pipe: I'm your PAPA
Parent 3030 is wrinting to pipe
Parent 3030 wrote 16 bytes to pipe
child 3031 is reading from pipe
child read 16 bytes from pipe: I'm your PAPA
Parent 3030 is wrinting to pipe
child 3031 is reading from pipe
Parent 3030 wrote 16 bytes to pipe
child read 16 bytes from pipe: I'm your PAPA
Parent 3030 is wrinting to pipe
child 3031 is reading from pipe
Parent 3030 wrote 16 bytes to pipe
child read 16 bytes from pipe: I'm your PAPA
child 3031 is reading from pipe
Parent 3030 is wrinting to pipe
Parent 3030 wrote 16 bytes to pipe
child read 16 bytes from pipe: I'm your PAPA
Parent 3030 is wrinting to pipe
child 3031 is reading from pipe
Parent 3030 wrote 16 bytes to pipe
child read 16 bytes from pipe: I'm your PAPA
Parent 3030 is wrinting to pipe
child 3031 is reading from pipe
Parent 3030 wrote 16 bytes to pipe
child read 16 bytes from pipe: I'm your PAPA
child 3031 is reading from pipe
Parent 3030 exited.%  

通过对程序修改可以实现先让父进程死亡(输入进程消失),会发现接收进程返回,但是如果接收进程只读几次的话,会出现141号(BROKEN_PIPE)报错,这个报错就是因为读取进程死亡而导致管道文件失效造成的。

下面是修改后的示例代码:

/*************************************************************************
	> File Name: pipe.c
	> Author:Royi 
	> Mail:royi990001@gmail.com 
	> Created Time: Thu 01 Feb 2024 09:36:16 PM CST
	> Describe: parent to child with pipe 
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <signal.h>

int pd[2], n, i;
pid_t pid;
char line[256];


int main() {
    pipe(pd);
    printf("pd = [%d, %d]\n", pd[0], pd[1]);
    if ((pid = fork()) == -1) {
        perror("fork");
        exit(1);
    }

    // parent 
    if (pid) {
        signal(SIGPIPE, SIG_IGN);
        close(pd[0]);
        printf("I'm parent %d, and I closed pd[0]\n", getpid());
        while (i++ < 10) {
            printf("Parent %d is wrinting to pipe\n", getpid());
            n = write(pd[1], "I'm your PAPA", 16);
            if (n == -1) {
                perror("write");
                exit(1);
            }
            printf("Parent %d wrote %d bytes to pipe\n", getpid(), n);
            sleep(1);
        }
        printf("Parent %d exited.", getpid());
    } else {
        // child
        close(pd[1]);
        printf("I'm child %d, and I closed pd[1]\n", getpid());
        i = 0;
        while (i++ < 3) {
            printf("child %d is reading from pipe\n", getpid());
            if ((n = read(pd[0], line, 128))) {
                line[n] = 0;
                printf("child read %d bytes from pipe: %s\n", n, line);
            } else {
                // pipe has no data and to writer.
                printf("Child had not find bytes of needing to read.");
                exit(0);
            }
        }
        printf("Child is exiting...\n");
    }
    return 0;
}


BROKEN_PIPE报错
在修改中的代码上,由于write函数为系统调用,而在执行write函数时出现了管道中断的错误,因此内核会发出一个信号“BROKEN_PIPE”,导致程序直接停止,因此无法看到报错信息,在程序开头添加signal(SIGPIPE, SIG_IGN);可以忽略掉信号继续执行程序,并通过程序看到报错信息。

管道命令处理

在Linux中,管道是如何使用的呢?

命令行:

cmd1 | cmd2

包含一个管道符号“ | ”。

sh将通过一个进程运行cmd1,另一个进程运行cmd2,他们通过一个管道连接在一起。因此cmd1的输出变成cmd2的输入。下文展示了管道命令的使用方法:

当sh获取命令行cmd1 | cmd2时,会复刻出一个子进程sh,并且等待子进程sh照常终止。

子进程sh:浏览命令行中是否有|符号。在这种情况下,cmd1 | cmd2有一个管道符号。将命令函划分为头部=cmd1尾部=cmd2

然后子进程sh执行以下类似的代码片段:

int pd[2];
pipe(pd);
pid = fork();
if (pid) {
	close(pd[0]);
	close(1);
	dup(pd[1]);
	close(pd[1]);
	exec(head);
} else {
	close(pd[1]);
	close(0);
	dup(pd[0]);
	close(pd[0]);
	exec(tail);
}

管道写进程重定向其 fd = 1pd[1],管道读进程重定向其 fd = 0pd[0]。这样一来就可以通过管道连接起来了。

命名管道

命名管道又叫FIFO,它们有”名称“,并且在文件系统中以特殊文件的形式存在。

它们会一直存在下去,直到使用rm和unlink将其删除。它们可与非相关进程一起使用,并不局限于管道创建进程的子进程。

命名管道终端示例

在sh中,通过mknod命令创建一个命名管道:

mknod mypipe p

或者在C程序中发出mknod()系统调用:

int r = mknod("mypipe", S_IFIFO, 0);

两种方式都会在当前目录中创建一个名为mypipe的管道文件。

使用:

ls -l mypipe

可以查看文件属性。

创建mypipe
其中,数字1代表连接数,0代表大小。

进程可以像访问普通文件一样访问命名管道。

对命名管道的写入和读取是由Linux内核同步的。

如何使用这个管道呢?我们来做一个最基本的演示:

我们需要两个sh,这里我以我的服务器为例,打开两个sh终端:

在第一个终端上的该目录下执行:

echo "hello" > mypipe

将”hello“重定向到mypipe中,如下图:

终端1

此时会发现陷入了阻塞状态。

这是因为管道中存在内容还没有读出,sh进程正在等待。

接下来在第二个终端上,执行:

cat mypipe

读出管道中的文件,如下图:

终端2

读出管道中的内容之后,第一个终端也退出了阻塞状态:

终端1

读者可以自行尝试一下。

命名管道C程序示例

如何在C程序中使用命名管道呢?

接下来展示示例代码:

/*************************************************************************
	> File Name: named_pipe.c
	> Author:Royi 
	> Mail:royi990001@gmail.com 
	> Created Time: Fri 02 Feb 2024 03:20:10 PM CST
	> Describe: 
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

char *line = "testing named pipe";

int main() {
    int fd;
    mknod("mypipe", S_IFIFO | 0677, 0);
    fd = open("./mypipe", O_WRONLY);
    write(fd, line, strlen(line));
    close(fd);
    return 0;
}

/*************************************************************************
	> File Name: named_pipe.c
	> Author:Royi 
	> Mail:royi990001@gmail.com 
	> Created Time: Fri 02 Feb 2024 03:20:10 PM CST
	> Describe: 
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

char buf[128];

int main() {
    int fd = open("./mypipe", O_RDONLY);
    read(fd, buf, 128);
    printf("%s\n", buf);
    close(fd);
    return 0;
}

编译成对应名称的文件,结果如下:

编译后结果

测试方法与直接的shell命令行测试一模一样,先执行write程序,然后在另一个终端上的执行read程序:

执行write程序:

write程序执行

进入阻塞态。

在另一个终端上执行read程序:

read程序

此时返回第一个终端发现已经退出了阻塞态:

退出阻塞态


以上就是Linux\Uinx系统编程中进程管理的所有内容啦!!!创作不易,希望读者给个关注给个点赞收藏支持一下!!!!下一章将更新多进程编程的内容,敬请期待!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/367618.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux 组建和卸载raid1、raid0详细操作

组raid的最好是相同容量和型号的硬盘&#xff0c;否则会有木桶效应 linux下组raid有很多细节 一、安装raid软件 deb包 apt-get install mdadm或dnf包 dnf install mdadm二、组raid1-镜像&#xff0c;组raid0-并列 raid1和raid0只有在madam命令时一点点不同&#xff0c;其他…

了解野指针与assert断言 拿捏指针的使用!

目录 1.野指针 野指针的成因&#xff1a; 2.规避野指针 3.assert断言 创作不易&#xff0c;宝子们&#xff01;如果这篇文章对你们有帮助的话&#xff0c;别忘了给个免费的赞哟~ 1.野指针 概念&#xff1a;野指针就是指针指向的位置是不可知的&#xff08;随机的、不正确的…

重写Sylar基于协程的服务器(5、IO协程调度模块的设计)

重写Sylar基于协程的服务器&#xff08;5、IO协程调度模块的设计&#xff09; 重写Sylar基于协程的服务器系列&#xff1a; 重写Sylar基于协程的服务器&#xff08;0、搭建开发环境以及项目框架 || 下载编译简化版Sylar&#xff09; 重写Sylar基于协程的服务器&#xff08;1、…

【论文阅读笔记】Taming Transformers for High-Resolution Image Synthesis

Taming Transformers for High-Resolution Image Synthesis 记录前置知识AbstractIntroductionRelated WorkMethodLearning an Effective Codebook of Image Constituents for Use in TransformersLearning the Composition of Images with Transformers条件合成合成高分辨率图…

阿狸与小兔子的奇幻之旅

在很久很久以前&#xff0c;有一个遥远的国度&#xff0c;这个国度里生活着各种各样的动物&#xff0c;它们和谐共处&#xff0c;幸福快乐。在这个国度里&#xff0c;有一只聪明伶俐的小狐狸&#xff0c;名叫阿狸。 一天&#xff0c;阿狸在森林里散步时&#xff0c;遇到了一只正…

【BIAI】Lecture10 - Motor System2

Motor System2 专业术语 descending spinal tracts 下行脊髓束 corticospinal tract 锥体束 reticulospinal tract 脊髓脑干束 vestibulospinal tract 脊髓脑干侧脊束 precentral gyrus 前中央回 population coding 群体编码 basal ganglia 基底节 thalamus 丘脑 Posterior pa…

Python中容器类型的数据

目录 序列 序列的索引操作 加和乘操作 切片操作 成员测试 列表 创建列表 追加元素 插入元素 替换元素 删除元素 元组 创建元组 元组拆包 集合 创建集合 修改集合 字典 创建字典 修改字典 访问字典视图 遍历字典 若我们想将多个数据打包并且统一管理&…

计算斐波那契数

前提需备知识&#xff1a; 斐波那契数列是第一项和第二项为1&#xff0c;第三项为前两项之和&#xff0c;然后以此类推的一个数列&#xff0c;即1&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;5&#xff0c;8&#xff0c;13&#xff0c;21&#xff0c;34&#xff0c;5…

【开源】JAVA+Vue.js实现学生日常行为评分管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、系统设计2.1 功能模块设计2.2.1 登录注册模块2.2.2 用户管理模块2.2.3 评分项目模块2.2.4 评分数据模块2.2.5 数据字典模块 2.3 可行性设计2.4 用例设计2.5 数据库设计2.5.1 整体 E-R 图2.5.2 用户2.5.3 评分项目2.5.4 评分数据2.5.…

STM32MP135开发板助力电力行业,IEC61850协议移植笔记

1.概述 IEC61850是变电站自动化系统&#xff08;SAS&#xff09;中通信系统和分散能源&#xff08;DER&#xff09;管理的国际标准。它通过标准的实现&#xff0c;实现了智能变电站的工程运作标准化。使得智能变电站的工程实施变得规范、统一和透明&#xff0c;在电力和储能系…

评论区功能的简单实现思路

评论区功能是社交类项目中的核心组成部分&#xff0c;它涉及到前端的交云和后端的数据处理。基于你的技术栈&#xff08;前端 Vue3&#xff0c;后端 Java&#xff09;&#xff0c;下面是一个具体的实现思路和数据库设计建议&#xff0c;并探索一下知乎的评论系统。 数据库设计…

基于SpringBoot Vue学生信息管理

大家好✌&#xff01;我是Dwzun。很高兴你能来阅读我&#xff0c;我会陆续更新Java后端、前端、数据库、项目案例等相关知识点总结&#xff0c;还为大家分享优质的实战项目&#xff0c;本人在Java项目开发领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#x…

蓝桥杯---垒骰子

赌圣atm晚年迷恋上了垒骰子&#xff0c;就是把骰子一个垒在另一个上边&#xff0c;不能歪歪扭扭&#xff0c;要垒成方柱体。经过长期观察&#xff0c;atm 发现了稳定骰子的奥秘&#xff1a;有些数字的面贴着会互相排斥&#xff01;我们先来规范一下骰子&#xff1a;1的对面是4&…

Vue2+ElementUI 弹窗全局拖拽 支持放大缩小

拖拽组件 dialogDrag.vue <template><div></div> </template> <script>export default {name: dialogDrag,data() {return {originalWidth: null,originalHeight: null}},created() {this.$nextTick(()>{this.dialogDrag()})},mounted() {}…

vue3 之 组合式API—computed

computed计算属性函数 计算属性基本思想和Vue2的完全一致&#xff0c;组合式API下的计算属性只是修改了写法 核心步骤&#xff1a; 导入computed函数执行函数 在回调参数中return基于响应式数据做计算的值&#xff0c;用变量接收 vue <script setup> // 1.导入compute…

【数据结构】链表OJ面试题(题库+解析)

前言 还不清楚链表的码喵们可以看看前篇关于链表的详解 http://t.csdnimg.cn/X6t6P 1.链表面试题 既然已经懂得了链表该如何实现&#xff0c;那么现在就趁热打铁开始练习&#xff01;这里给码喵们整理了相对不错的一些OJ题来练习 1. 删除链表中等于给定值 val 的所有结点。 力…

代码随想录 Leetcode78. 子集

题目&#xff1a; 代码(首刷看解析 2024年2月3日&#xff09;&#xff1a; class Solution { private:vector<vector<int>> res;vector<int> path; public:void backtracking(vector<int>& nums, int startIndex) {res.push_back(path);for (int …

RocketMQ问题篇01 | NameServer告警异常分析

RocketMQ问题篇01 | NameServer告警异常分析 1、问题描述2、初步分析2.1 mqcloud源代码分析2.2 NameServer源码分析2.3 NameServer源码分析2&#xff08;源码出错概率太低&#xff09;2.4 大流量分析 3、堆栈分析3.1 wait response on the channel3.2 connect to failed3.3 sen…

Catalan数

文章目录 Catalan数Leecode96 不同的二叉搜索树题目描述解题思路代码 Leecode22 括号生成题目描述代码 Catalan数 Catalan数是一种组合数学的计数方法&#xff0c;常用于解决一些计数问题&#xff0c;例如括号匹配问题、二叉树的节点问题等。Catalan数的计算公式如下&#xff1…

20240203在Ubuntu20.04.6下配置stable-diffusion-webui.git

20240203在Ubuntu20.04.6下配置stable-diffusion-webui.git 2024/2/3 11:55 【结论&#xff1a;在Ubuntu20.04.6下&#xff0c;生成512x512分辨率的图像&#xff0c;大概需要11秒钟&#xff01;】 前提条件&#xff0c;可以通过技术手段上外网&#xff01;^_首先你要有一张NVID…
最新文章