Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

Linux进程间通信

3
Sep
2009

Linux进程间通信

By Alex
/ in C,Linux
/ tags Linux知识, Linux编程, 系统编程
0 Comments
管道

当从一个进程连接数据流到另外一个进程时,使用术语“管道”。通常是把一个进程的输出通过管道连接到另外一个进程的输入。Shell命令通过管道字符可以实现命令的连接:

Shell
1
2
3
4
5
cmd1 | cmd2
 
#cmd1的标准输入来自终端键盘
#cmd1的标准输出传递给cmd2,作为它的标准输入
#cmd2的标准输出连接到终端屏幕
popen函数

Linux提供了类似的API,允许通过编程的方式,利用管道在两个程序之间传递数据。在两个程序之间进行数据传递的最简单方式是使用popen/pclose函数:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
/**
* 允许将另外一个程序作为新进程启动,并可以传递数据或者接收数据
* @param command 需要运行的程序和参数
* @param open_mode 打开模式,必须是r或者w
*     如果是r,被调用程序的输出可以被当前程序使用,通过返回的文件指针进行fread读取
*     如果是w,当前程序可以通过fwrite向被调用程序发送数据,后者可以在stdin上读取这些数据
*/
FILE *popen( const char *command, const char *open_mode );
/**
* 关闭文件指针,该函数只有在新进程结束后才会返回,否则会一直阻塞
*/
int pclose( FILE *stream_to_close );

下面是一个简单的示例,执行uname命令并获取其输出:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
    char buffer[BUFSIZ + 1];
    int chars_read;
    memset( buffer, 0, sizeof ( buffer ) );
    FILE *read_fp = popen( "uname -a", "r" ); //创建新进程并读取其标准输出
    if ( read_fp != NULL )
    {
        //像读取文件一样,将新进程的标准输出读取到缓冲区
        chars_read = fread( buffer, sizeof(char), BUFSIZ, read_fp );
        if ( chars_read > 0 )
        {
            printf( "Output: %s\n", buffer );
        }
        pclose( read_fp );
        exit( EXIT_SUCCESS );
    }
    exit( EXIT_FAILURE );
}
pipe调用

在底层,Linux提供了pipe函数,通过该函数可以在两个进程之间传递数据,不需要启动Shell,该函数提供了对读写数据的更多控制:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <unistd.h>
/**
* 创建一个管道,该函数对入参数组填上两个新的文件描述符,然后返回0
* 返回的两个文件描述符通过一种特殊的方式连接:依据FIFO原则,写入
* file_descriptor[1]的数据,都可以从file_descriptor[0]中读取回来
*
* @param file_descriptor 长度为2的文件描述符数组
* @return 如果成功返回0否则返回-1并设置errno:
*     EMFILE:进程使用的文件描述符过多
*     ENFILE:系统的文件表已满
*     EFAULT:文件描述符无效
*/
int pipe( int file_descriptor[2] );

初看,pipe函数没有什么价值,但由于fork调用创建新进程时,默认原先打开的文件描述符仍然保持打开状态,因此,管道可以被父子进程共享,从而用来在其间进行数据传递,下面是pipe在父子进程之间使用的例子:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
    int data_processed;
    int file_pipes[2];
    const char some_data[] = "MSG";
    char buffer[BUFSIZ + 1];
    pid_t fork_result;
    memset( buffer, '\0', sizeof ( buffer ) );
    if ( pipe( file_pipes ) == 0 ) //创建一个共享的管道
    {
        fork_result = fork(); //创建子进程
        if ( fork_result == -1 ) exit( EXIT_FAILURE );
        if ( fork_result == 0 )
        {
            //这里是子进程,从文件描述符0中读取数据
            data_processed = read( file_pipes[0], buffer, BUFSIZ );
            printf( "Read %d bytes: %s\n", data_processed, buffer );
            exit( EXIT_SUCCESS );
        }
        else
        {
            //这里是父进程,向文件描述符1中写入数据
            data_processed = write( file_pipes[1], some_data, strlen( some_data ) );
            printf( "Wrote %d bytes\n", data_processed );
        }
    }
    exit( EXIT_SUCCESS );
}

上面的例子中,父子进程运行的是相同的程序,如果两个进程是完全不同的程序呢?通过exec()调用后,只需要将文件描述符传递给新进程就可以继续使用管道了,因为文件描述符本质上只是一个数字而已:

C
1
2
3
4
5
6
7
8
//将管道文件描述符保存到字符串中
sprintf(buffer, "%d", file_pipes[0]);
//传递给子进程
execl("command", buffer, (char *)0);
//读取参数为文件描述符
sscanf(argv[0], "%d", &file_descriptor);
//从文件描述符中读取数据
data_processed = read( file_descriptor, buffer, BUFSIZ );

关于管道的使用,应当注意:

  1. 对于已经关闭写端的管道,对其指向read()调用不会阻塞,而会立即返回0。这与读取无效文件描述符不同,后者会返回-1
  2. 如果通过fork()调用使用管道,就会存在两个不同的文件描述符可以用来向管道写数据,一个在父进程中,一个在子进程中。只有在父子文件中均把针对管道的写描述符关闭,管道才认为是关闭的,对其进行read才会立即返回
将管道用作标准输入输出

通过管道连接两个进程,具有更加简洁的方法:

C
1
2
3
4
5
6
7
8
9
10
if ( fork_result == ( pid_t ) 0 ) // 子进程
{
    close( 0 ); //关闭标准输入
    //复制管道读,根据dup的特点,它将复制为最小数值的文件描述符,即作为标准输入
    dup( file_pipes[0] );
    //关闭管道中两个文件描述符
    close( file_pipes[0] );
    close( file_pipes[1] );
    execlp( "od", "od", "-c", ( char * ) 0 );
}
命名管道

要在两个不相关(不具备共同祖先)的进程之间传递数据,可以使用命名管道(Named pipe),命名管道又被称为FIFO文件。命名管道是一种特殊类型的文件,在文件系统中以文件名的形式存在,其行为却与前一节所述的管道类似。可以通过Shell命令 mknode 或者 mkfifo 来创建命名管道:

Shell
1
2
3
4
5
6
7
mkfifo /tmp/my_fifo
#尝试读取这个空白的命名管道
cat < /tmp/my_fifo  #阻塞
 
#在另外一个终端尝试写入这个空白的命名管道
echo "Hello World" > /tmp/my_fifo
#第一个终端读取到内容并输出在屏幕上

在程序中,可以使用以下两个调用:

C
1
2
3
4
5
6
7
8
9
10
11
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo( const char *filename, mode_t mode );
int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t) 0);
 
//创建命名管道的例子:
int main()
{
    int res = mkfifo( "/tmp/my_fifo", 0777 ); //尝试创建777权限的文件,当然需要受到用户掩码umask的制约
    if ( res == 0 ) exit( EXIT_SUCCESS );
}

通过open命令访问FIFO文件时,需要注意一个限制:不能以 O_RDWR 模式打开,因为FIFO只是为了单向的传递数据。如果以读写方式打开,进程将从管道中读取到自己写入的数据。如果确实需要双向的数据传输,可以使用一对命名管道。此外选项 O_NONBLOCK 也会影响对管道的读写请求的处理方式:

C
1
2
3
4
5
6
7
8
//调用一直阻塞,除非另外一个进程以写方式打开同一命名管道
open( const char *path, O_RDONLY );
//即使没有其它进程以写模式打开同一命名管道,调用也会成功并立即返回
open(const char *path, O_RDONLY | O_NONBLOCK);
//调用一直阻塞,除非另外一个进程以读方式打开同一命名管道
open( const char *path, O_WRONLY );
//调用会立即返回,但是如果没有另外一个进程以读模式打开命名管道,调用将返回-1,并且FIFO也不会被打开
open(const char *path, O_WRONLY | O_NONBLOCK);
信号量

与线程之间通信的信号量类似,Linux还提供了更通用的,可以在不同进程之间进行通信的信号量机制,这些信号量接口都是针对成组的通用信号量进行操作,而不是针对一个二进制信号量。

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <sys/sem.h>
/**
* 创建一个新的信号量,或者获取一个已经存在的信号量
* @param key  一个整数,不相关的进程可以通过同一key来访问同一信号量
*             特殊值IPC_PRIVATE表示创建一个只有当前进程才能看见的信号量
* @param num_sems 需要的信号量的数目,一般为1
* @param sem_flags 位或标记。低9位类似于文件权限;IPC_CREAT表示创建一个新的信号量;
*                  IPC_EXCL | IPC_CREAT 表示确保获得一个新的、唯一的信号量,如果
*                  信号量已经存在,会返回错误
* @return 成功返回正整数,表示信号量的唯一标识(sem_id);失败返回-1
*/
int semget( key_t key, int num_sems, int sem_flags );
/**
* 用于改变信号量的值,这是一个原子操作
* @param sem_id 信号量的唯一标识
* @param sem_ops 指向一个结构的指针
* @param num_sem_ops
*
*/
struct sembuf
{
    short sem_num; //信号量的数量,除非需要使用一组信号量,否则取值0
    short sem_op;  //信号量在一次操作中需要改变的值,可以使用非1值来改变信号量
                   //通常只会用到两个值:-1表示P操作,表示等待信号量可用;+1表示V操作,表示发送信号量可用的信息
    short sem_flg; //通常设置为SEM_UNDO,它使得操作系统跟踪该信号量的修改情况
                   //如果进程没有释放持有的信号量就终止,操作系统会代为释放
};
int semop( int sem_id, struct sembuf *sem_ops, size_t num_sem_ops );
/**
* 直接控制信号量信息
* @param sem_id 信号量的唯一标识
* @param sem_num 信号量的数量,除非需要使用一组信号量,否则取值0
* @param command 需要指向的操作
* @param semun 提供命令参数的联合体
*/
union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};
int semctl( int sem_id, int sem_num, int command, union semun semun );
共享内存

共享内存允许不相关的进程访问同一块逻辑内存。这是一种在进程之间传递数据的非常有效的方式,大部分实现都把共享内存安排为同一段物理内存。共享内存是进程地址空间中的一个特殊的范围。共享内存没有通过同步机制,因此需要使用其它同步机制来对共享内存的访问进行同步。

与共享内存相关的函数有:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <cygwin/types.h>
#include <stddef.h>
 
//#include <sys/shm.h>
 
/**
* 创建共享内存
* @param key 共享内存段的命名,特殊键IPC_PRIVATE表示创建进程私有的共享内存
* @param size 共享内存的容量
* @param shmflg 位或,包含9个代表访问权限的位,IPC_CREAT用于创建一个新的共享内存
*               给此函数传递已经存在的key并不是错误,此时的IPC_CREAT会被忽略
*
* @return 如果成功,返回一个正整数,作为共享内存的标识符;否则返回-1
*/
int shmget( key_t key, size_t size, int shmflg );
/**
* 第一次创建共享内存段时,它不能被任何进程访问。要启用对共享内存的访问,必须
* 将其连接到一个进程的地址空间中,这通过shmat函数完成
* @param shm_id 共享内存标识符
* @param shm_addr 连接到当前进程的地址位置,通常设置为空指针,表示让系统选择,否则硬件依赖性太高
* @param shmflg 标记位:SHM_RND与shm_addr联用,来控制连接地址;SHM_RDONLY 是共享内存对当前进程只读
*
* @return 如果成功,返回指向共享内存第一个字节的指针;否则返回-1
*/
void *shmat( int shm_id, const void *shm_addr, int shmflg );
/**
* 将共享内存段从当前进程分离,该函数不会删除共享内存,只是使当前进程不再能访问它
* @param shm_addr shmat的返回值
*/
int shmdt( const void *shm_addr );
/**
* 控制共享内存
* @param shm_id 共享内存标识符
* @param cmd 采取的动作:
*            IPC_STAT 将shmid_ds中的数据设置为共享内存的当前关联值
*            IPC_SET 如果有足够权限,则把shmid_ds中的值设置到共享内存
*            IPC_RMID 删除共享内存段
* @param buf 指针,指向共享内存模式和访问权限的结构
*/
struct shmid_ds
{
    uid_t uid;
    uid_t gid;
    mode_t mode;
};
int shmctl( int shm_id, int cmd, struct shmid_ds *buf );
消息队列

消息队列类似于命名管道,但是不具有打开、关闭管道以及阻塞通信方面的复杂性。消息队列提供了一种从一个进程向另外一个进程发送数据块的机制,每个数据块被认为含有一个类型。下面是消息队列相关API:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
MSGMAX
//单个消息的最大字节数
MSGMNB
//队列最大深度
 
#include <sys/msg.h>
/**
* 创建和访问一个消息队列
* @param key 队列的名字,IPC_PRIVATE用于创建私有队列
* @param msgflg 标记位,包含9个权限位,IPC_CREAT必须与这些位或才能创建新的队列
* @return 如果成功返回消息队列的标识符,否则返回-1
*/
int msgget( key_t key, int msgflg );
/**
* 把消息放入到队列中
* @param msqid 消息队列标识符
* @param msg_ptr 待发送消息的指针,目标应当是一个结构,且第一个成员变量是long型,用于表示消息类型
* @param msg_sz msg_ptr指向的消息的长度,不包括long型的消息类型的长度
* @param msgflg 控制当队列满或者队列消息到达系统范围限制时的行为,位或
*               IPC_NOWAIT:立即返回-1,不发送消息;如果该标记被清除,则发送进程挂起直到队列有空闲
*/
int msgsnd( int msqid, const void *msg_ptr, size_t msg_sz, int msgflg );
/**
* 从队列里接收一个消息
* @param msqid 消息队列标识符
* @param msg_ptr 准备接收消息的指针
* @param msg_sz msg_ptr指向消息的长度,不包括long型消息类型的长度
* @param msgtype 消息类型,用于实现简单的优先级机制:如果小于0获取消息类型小于等于其绝对值的第一个消息
*                如果为0获取第一个消息;如果大于0获取对应类型的第一个消息
* @param msgflg 控制当队列为空时的行为,位或。IPC_NOWAIT类似msgsnd
*/
int msgrcv( int msqid, void *msg_ptr, size_t msg_sz, long int msgtype, int msgflg );
/**
* 控制消息队列
* @param msqid 消息队列标识符
* @param cmd 命令。IPC_STAT,将消息队列关联值设置到buf;IPC_SET,将buf中的值设置到消息队列
*            IPC_RMID,删除消息队列
* @param buf 存放命令参数
*/
struct msqid_ds
{
    uid_t uid;
    uid_t gid;
    mode_t mode;
};
int msgctl( int msqid, int cmd, struct msqid_ds *buf );

 

← 贝多芬.第14钢琴奏鸣曲
原型模式 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • Linux信号、进程和会话
  • Linux内核编程知识集锦
  • Linux编程知识集锦
  • Linux网络知识集锦
  • Linux网络编程

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2