【Linux从入门到精通】通信 | 共享内存(System V)

 

  本篇文章接着上篇文章通信 | 管道通信(匿名管道 & 命名管道)进行讲解。本篇文章的中点内容是共享内存

文章目录

 一、初识与创建共享内存

1、1 什么是共享内存

1、2 共享内存函数

1、2、1 创建共享内存 shmget

1、2、2 ftok 生成 key

1、2、3 获取共享内存 shmget

1、3 demo 代码

二、对共享内存进行相关操作 

2、1 查看/删除 共享内存资源

2、2 共享内存挂接和访问

2、2、1 共享内存的挂接 shmat()

2、2、2 共享内存的访问

2、3 删除共享内存 shmctl 

三、完整共享内存通信 demo 代码

3、1 Log.hpp 日志

3、2 comm.hpp

3、3 shmClient.cpp

3、4 shmServer.cpp


🙋‍♂️ 作者:@Ggggggtm 🙋‍♂️

👀 专栏:Linux从入门到精通  👀

💥 标题:共享内存 💥

 ❣️ 寄语:与其忙着诉苦,不如低头赶路,奋路前行,终将遇到一番好风景 ❣️

 一、初识与创建共享内存

1、1 什么是共享内存

  我们在之前学管道通信时,是怎么实现通信的呢?匿名管道通信的方式是子进程继承父进程的内核数据结构,使得父子进程能够看到同一块空间命名管道信是让不同进程打开同一份文件。我们发现通信的前提就是让不同的进程看到同一份“资源”。当然,共享内存也不例外。

   每个进程都有自己独立的地址空间,所以它们彼此之间不能直接访问对方的内存。而共享内存则提供了一种特殊的内存区域,允许多个进程可以同时访问和操作同一块内存。具体如下图:

  这里再次解释一下上图。我们创建共享内存的过程:一个进程在内核空间申请一个共享内存对象,让后通过页表建立与物理内存的映射。让后另一个进程通过特殊的方法和算法来找到该共享内存并且与其建立映射。下面我们会对上述过程进行详细解释。 

1、2 共享内存函数

1、2、1 创建共享内存 shmget

  shmget函数用于创建一个新的共享内存段或者获取现有的共享内存段的标识符

函数原型为:

int shmget(key_t key, size_t size, int shmflg);

参数说明:

  • key:用于标识共享内存段的键值。可以使用ftok函数生成
  • size:指定共享内存段的大小,以字节为单位。
  • shmflg:用于指定共享内存段的访问权限和标志位,可以使用IPC_CREAT、IPC_EXCL等宏进行设置。

返回值:

  • 如果成功,返回共享内存段的标识符(即共享内存ID)。
  • 如果失败,返回-1,并设置errno。

  更加详细的如下图:

  我们在这里具体解释一下第三个参数 shmflg。这个参数是可以有多个选择的。底层是利用了位图的思想。主要是IPC_CREAT和IPC_EXCL两个选项。

  IPC_CREAT和IPC_EXCL是在shmget函数中使用的标志位,用于指定共享内存段的访问权限和标志。它们在shmget函数的第三个参数shmflg中使用。

  • IPC_CREAT:该标志用于创建一个新的共享内存段。如果指定的key对应的共享内存段不存在,则创建一个新的共享内存段。如果共享内存段已经存在,则返回该共享内存段的标识符(即共享内存ID)。

  • IPC_EXCL:该标志与IPC_CREAT一起使用,在创建共享内存段时起作用。如果指定的key对应的共享内存段已经存在,则shmget函数会失败,并返回-1,并且置errno为EEXIST(资源已存在)。

   我们接下来再看一下 shmget 的具体使用例子。

int shmid = shmget(key, size, IPC_CREAT | permission_flags);

  上述代码中,IPC_CREAT标志位用于创建共享内存段。如果指定的key对应的共享内存段已经存在,那么shmget函数会返回该共享内存段的标识符;如果共享内存段不存在,则会创建一个新的共享内存段,并返回新创建的共享内存段的标识符。

1、2、2 ftok 生成 key

  在函数shmget中,key值是用于标识或检索共享内存段的关键值。它在创建或访问共享内存时起到重要作用。具体来说,key值用于以下两个目的:

  • 当多个进程需要访问同一个共享内存段时,它们可以使用相同的key值来标识这个共享内存段。
  • 如果一个共享内存段已经存在,并且其他进程想要访问它,那么只需要提供相同的key值即可找到该共享内存段。

  那在使用 shmget 函数之前,我们应该使用 ftok 函数生成key值,来表示这个共享内存段。由于是标示共享空间,所以应该确定唯一性。至于key的值是多少并不关键。那我们看一下ftok 函数的用法。具体如下:

参数说明:

  • pathname:一个包含一个现有文件的路径名,用于生成k值。最好是有权访问这个文件。
  • proj_id:不同的proj_id可以被用作区分不同类型的通信方式或不同的ipc资源,来生成不同的k值。其实就是一个任意整型值。

  我们再看一下其具体的例子:

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>int main()
{key_t key;char *path = "./example.txt";int proj_id = 0x66;// 使用ftok函数生成k值key = ftok(path, proj_id);printf("Generated key: %d\n", key);return 0;
}

  另一个进程也可以用相同的方法生成相同的key值。ftok函数根据给定的路径名和proj_id生成k值。当路径名和proj_id相同时,生成的k值也相同。这是因为ftok函数内部使用了哈希运算,将路径名和proj_id转化为一个唯一的整数。尽管可能存在哈希冲突(即不同的路径名和proj_id生成相同的k值),但概率非常低。通常情况下,不同的进程可以使用相同的路径名和proj_id生成相同的k值是非常罕见的。即使出现相同的k值,由于进程间通信中还有其他参数的限制(如消息队列标识、共享内存标识等),不同进程之间的IPC通信仍然可以正常进行。  

  生成 key 值后,我们就可以用key值创建共享内存,或者来获取共享内存。下面我们看一下获取使用key值来获取共享内存的方法。

1、2、3 获取共享内存 shmget

  shmget 函数还可用来获取共享内存。当生成的key值已经有对应的共享内存时,shmget 函数就会返回这段共享内存的标识码。我么不只需要将第三个参数修改为0,就是来获取对应key值的共享内存。

1、3 demo 代码

  我们接下来写一段代码测试和总结一下我们上面所学到的函数。下面为实例:

// Log.hpp
#include <iostream>
#include <ctime>#define Debug   0
#define Notice  1
#define Warning 2
#define Error   3const std::string msg[] = {"Debug","Notice","Warning","Error"
};std::ostream &Log(std::string message, int level)
{std::cout << " | " << (unsigned)time(nullptr) << " | " << msg[level] << " | " << message;return std::cout;
}// comm.hpp
#pragma once#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cassert>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "Log.hpp"using namespace std; #define PATH_NAME "/home/gtm"
#define PROJ_ID 0x66
#define SHM_SIZE 4096 //共享内存的大小,最好是页(PAGE: 4096)的整数倍#define FIFO_NAME "./fifo"// shmClient.cc
#include"comm.hpp"int main()
{Log("child pid is : ", Debug) << getpid() << endl;key_t k = ftok(PATH_NAME, PROJ_ID);if (k < 0){Log("create key failed", Error) << " client key : " << k << endl;exit(1);}Log("create key done", Debug) << " client key : " << k << endl;// 获取共享内存int shmid = shmget(k, SHM_SIZE, 0);if(shmid < 0){Log("create shm failed", Error) << " client key : " << k << endl;exit(2);}Log("create shm success", Error) << " client shmid : " << shmid << endl;return 0;
}// shmServer.cc
#include "comm.hpp"string TransToHex(key_t k)
{char buffer[32];snprintf(buffer, sizeof buffer, "0x%x", k);return buffer;
}int main()
{// 我们之前为了通信,所做的所有的工作,属于什么工作呢:让不同的进程看到了同一份资源(内存)// 1. 创建公共的Key值key_t k = ftok(PATH_NAME, PROJ_ID);assert(k != -1);Log("create key done", Debug) << " server key : " << TransToHex(k) << endl;// 2. 创建共享内存 -- 建议要创建一个全新的共享内存 -- 通信的发起者int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666); //if (shmid == -1){perror("shmget");exit(1);}Log("create shm done", Debug) << " shmid : " << shmid << endl;return 0;
}

  shmServer.cc文件中的代码是服务端代码。首先调用ftok函数生成一个唯一的键值,并将其转换为十六进制字符串表示。然后使用shmget函数创建一个共享内存段,创建时指定了IPC_CREAT标志,用于新建共享内存段。如果创建成功,返回一个共享内存标识符shmid。

  shmClient.cc文件中的代码是客户端代码。首先也是调用ftok函数生成一个唯一的键值。然后通过shmget函数打开已存在的共享内存段,打开时不需要指定IPC_CREAT标志,而是提供即将打开的共享内存段的键值和大小。如果打开成功,返回一个共享内存标识符shmid。

  Log.hpp文件定义了一个宏和一个Log函数。宏定义了四个日志级别,分别对应Debug、Notice、Warning和Error四个字符串。Log函数负责输出日志信息,接受一个字符串信息和一个日志级别参数。Log函数将时间戳、日志级别和消息内容输出到标准输出流中。

  上述代码就是完成了创建共享内存的功能,并且在其中打印了一些日志信息。我们不妨来看一下运行结果。具体如下图:

  我们发现对应的shmid是相同的,说明Server和Client确实获得了相同的共享内存块。他们所生成的key值相同吗?其实是相同的,如下图:

二、对共享内存进行相关操作 

2、1 查看/删除 共享内存资源

  当我们再次运行时,就会发生错误。具体如下图:

  为什么呢?原因是我们刚刚创建的共享内存依然存在。当进程结束时,共享内存并不会自动释放。为什么呢?我们可以认为共享内存是属于操作系统。所以共享内存的生命周期随操作系统!这时我们可以手动关闭共享内存。在关闭前首先要查看共享内存,指令:ipcs -m。具体如下图:

  当然,查到共享内存后,可以用指令进行删除。那么问题来了,使用key值删除呢?还是用shmid 进行删除呢?我们这里需要注意:共享内存中的shmid和key值是两个不同的概念

  1. shmid(Shared Memory ID)是共享内存的标识符,由操作系统分配,并作为一个非负整数对共享内存进行引用。在使用共享内存时,我们需要通过shmid来进行操作,如创建、附加、访问和删除等。shmid可以看作是内核用于标识某个特定共享内存段的一个唯一值

  2. key值是用户定义的一个标识符,通常是一个整数值。在创建共享内存时,我们可以使用ftok函数生成一个key值,以便其他进程可以通过这个key值来获取相同的共享内存区域。key值是用于标识共享内存的用户级别的标识符,不同于shmid,其值不受内核控制

  所以删除共享内存,我们使用的shmid。具体指令:ipcrm -m shmid。如下图:

  当我们删除共享内存后,再次进行查找发现就没有了,且程序能够正常运行。

2、2 共享内存挂接和访问

2、2、1 共享内存的挂接 shmat()

  在调用shmget()函数时,内核会在内部维护一个共享内存表格,其中包含了共享内存的相关信息,包括共享内存的大小、权限等。当调用成功后,将返回一个唯一的共享内存标识符,该标识符可以用于后续的共享内存操作。

  那么正常来说,我们访问共享内存是需要通过系统调用的。但是我们这里可以将内核级别的共享内存挂接到进程的地址空间。然后用户就可以直接进行访问

  进程可以使用系统提供的函数(如shmat())将自己的地址空间映射到共享内存。也可以理解为shmat()函数将共享内存附加到进程的虚拟地址空间中,使得进程可以访问该共享内存所指向的物理内存区域。具体用法如下:

  参数说明:

  • shm_id:共享内存标识符,通过调用 shmget 获取。
  • shm_addr:内存段的地址,通常传入 NULL,表示由系统自动选择一个适合的地址。
  • shmflg:控制共享内存段的附加方式和权限,可以使用 IPC_CREAT 标志创建新的共享内存段。通常传入0。

  返回值:

  • 如果成功,返回指向共享内存段第一个字节的指针;
  • 如果失败,返回 void * 类型的错误值 -1

  其实我们看完其使用方法后,有没有发现与 malloc 很相似。malloc 申请空间成功后,会返回所申请空间的起始地址。否则就会返回NULL。shmat 与其确实有些相似。我们可结合如下例子一起理解:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>int main() {int shm_id;key_t key;int *shared_memory;// 获取共享内存标识符key = ftok("。/file", 0x66);shm_id = shmget(key, sizeof(int), IPC_CREAT | 0666);// 将共享内存段附加到进程的地址空间中shared_memory = shmat(shm_id, NULL, 0);// 访问共享内存printf("共享内存中的值为:%d\n", *shared_memory);// 分离共享内存段shmdt(shared_memory);return 0;
}

  我们也看到了最后是有一个去关联的 shmdt 函数。参数就是我们所获取的共享内存的起始地址,这里就不再过多解释此函数

  这里有会有一个问题:将内核级别的共享内存挂接到进程地址空间的哪里了呢?我们看如下图:

   我们之前学进程地址空间时,知道堆和栈的中间有大量的镂空,而这段位置就是内核级别的共享内存所挂接到的位置!

2、2、2 共享内存的访问

  到这里,我们已经学习了共享内存的大部分内容。只差对共享内存的访问了。当我们对共享内存进行挂接后, 就可以得到共享内存挂接后的起始地址。我们用户可以对其进行直接访问(写入/读取)。我们给出如下伪代码:

    // shmServer.cppchar *shmaddr = (char *)shmat(shmid, nullptr, 0);Log("attach shm done", Debug) << " shmid : " << shmid << endl;for(;;){printf("%s\n", shmaddr);if(strcmp(shmaddr, "quit") == 0) break;sleep(1);}// shmClient.cpp  // 挂接并获得共享内存起始地址char *shmaddr = (char *)shmat(shmid, nullptr, 0);if(shmaddr == nullptr){Log("attach shm failed", Error) << " client key : " << k << endl;exit(3);}char a = 'a';for(; a <= 'c'; a++){// 我们是每一次都向shmaddr[共享内存的起始地址]写入snprintf(shmaddr, SHM_SIZE - 1,\"hello server, 我是其他进程,我的pid: %d, inc: %c\n",\getpid(), a);sleep(3);}

  对上述代码是一个使用共享内存进行进程间通信的伪代码。下面对代码进行详解:

  1. 首先,在服务端(shmServer.cpp)中,通过shmat函数将共享内存连接到当前进程的地址空间。shmat函数的第一个参数是共享内存的标识符shmid,第二个参数为NULL表示让系统自动选择合适的地址分配给共享内存,第三个参数为0表示以默认权限进行操作。连接完成后,返回共享内存的起始地址,并赋值给shmaddr指针。

  2. 服务器端的for循环中,通过printf函数将shmaddr指向的共享内存内容输出到标准输出(读取)。然后通过strcmp函数判断共享内存中的内容是否为"quit",如果是,则跳出循环,结束程序。否则,通过sleep函数暂停1秒钟。

  3. 在客户端(shmClient.cpp)中,同样通过shmat函数连接到共享内存,并将共享内存的起始地址赋给shmaddr指针。若连接失败(shmaddr为nullptr),则输出错误信息并退出程序。

  4. 客户端的for循环中,使用snprintf函数将格式化的字符串写入shmaddr指向的共享内存中(写入)。该字符串包含了客户端进程的PID(进程标识符)和一个递增的字符,以展示多次写入的内容。然后通过sleep函数暂停3秒钟。

  运行结果如下:

  我们通过运行结果发现:在客户端没有写入的情况下,服务端进行读取时也会读到内容。读到的是空字符串(共享内存默认会初始化为0)。我们发现共享内存的读写并没有访问控制。我们知道命名管道通信是由访问控制的。但是当一个进程写入时,另一个就能够马上看到写入的内容。所以共享内存是所有进程间通信(IPC),速度最快的!不需要过多的拷贝!!(不需要将数据给操作系统)。如果我想一定程度的访问控制呢?可以在共享内存读写的过程中加入命名管道来控制。

2、3 删除共享内存 shmctl 

  上面我们了解了可以使用Linux指令对共享内存进行删除,我们也可以使用系统调用 shmctl()函数 对其进行删除。具体使用如下:

 参数说明:

  • shmid:共享内存标识符,通过shmget函数获取得到。
  • cmd:表示对共享内存进行的操作类型,可以选择的参数有:
    • IPC_STAT:获取共享内存的状态信息,将共享内存的属性保存在buf所指向的结构体中。
    • IPC_SET:设置共享内存的属性,使用buf所指向的结构体中的值进行设置。
    • IPC_RMID:删除共享内存。
  • buf:指向一个struct shmid_ds结构体的指针,用于存储共享内存的属性信息。通常使用nullptr。

  shmctl函数可以用于对共享内存段进行控制操作。它能够实现共享内存的创建、删除、以及获取和修改共享内存的属性。但是我们该函数最常用删除共享内存。使用IPC_RMID操作可以删除指定的共享内存段,并释放系统资源。这个操作会立即删除共享内存段,以及与它关联的任何进程中的键和id

三、完整共享内存通信 demo 代码

3、1 Log.hpp 日志

  

#include <iostream>
#include <ctime>#define Debug   0
#define Notice  1
#define Warning 2
#define Error   3const std::string msg[] = {"Debug","Notice","Warning","Error"
};std::ostream &Log(std::string message, int level)
{std::cout << " | " << (unsigned)time(nullptr) << " | " << msg[level] << " | " << message;return std::cout;
}

3、2 comm.hpp

#pragma once#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <cassert>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "Log.hpp"using namespace std; //不推荐#define PATH_NAME "/home/whb"
#define PROJ_ID 0x66
#define SHM_SIZE 4096 //共享内存的大小,最好是页(PAGE: 4096)的整数倍#define FIFO_NAME "./fifo"class Init
{
public:Init(){umask(0);int n = mkfifo(FIFO_NAME, 0666);assert(n == 0);(void)n;Log("create fifo success",Notice) << "\n";}~Init(){unlink(FIFO_NAME);Log("remove fifo success",Notice) << "\n";}
};#define READ O_RDONLY
#define WRITE O_WRONLYint OpenFIFO(std::string pathname, int flags)
{int fd = open(pathname.c_str(), flags);assert(fd >= 0);return fd;
}void Wait(int fd)
{Log("等待中....", Notice) << "\n";uint32_t temp = 0;ssize_t s = read(fd, &temp, sizeof(uint32_t));assert(s == sizeof(uint32_t));(void)s;
}void Signal(int fd)
{uint32_t temp = 1;ssize_t s = write(fd, &temp, sizeof(uint32_t));assert(s == sizeof(uint32_t));(void)s;Log("唤醒中....", Notice) << "\n";
}void CloseFifo(int fd)
{close(fd);
}

3、3 shmClient.cpp

#include "comm.hpp"int main()
{Log("child pid is : ", Debug) << getpid() << endl;key_t k = ftok(PATH_NAME, PROJ_ID);if (k < 0){Log("create key failed", Error) << " client key : " << k << endl;exit(1);}Log("create key done", Debug) << " client key : " << k << endl;// 获取共享内存int shmid = shmget(k, SHM_SIZE, 0);if(shmid < 0){Log("create shm failed", Error) << " client key : " << k << endl;exit(2);}Log("create shm success", Error) << " client key : " << k << endl;// sleep(10);char *shmaddr = (char *)shmat(shmid, nullptr, 0);if(shmaddr == nullptr){Log("attach shm failed", Error) << " client key : " << k << endl;exit(3);}Log("attach shm success", Error) << " client key : " << k << endl;int fd = OpenFIFO(FIFO_NAME, WRITE);// client将共享内存看做一个char 类型的bufferwhile(true){ssize_t s = read(0, shmaddr, SHM_SIZE-1);if(s > 0){shmaddr[s-1] = 0;Signal(fd);if(strcmp(shmaddr,"quit") == 0) break;}}CloseFifo(fd);// 去关联int n = shmdt(shmaddr);assert(n != -1);Log("detach shm success", Error) << " client key : " << k << endl;return 0;
}

3、4 shmServer.cpp

#include "comm.hpp"Init init; string TransToHex(key_t k)
{char buffer[32];snprintf(buffer, sizeof buffer, "0x%x", k);return buffer;
}int main()
{// 1. 创建公共的Key值key_t k = ftok(PATH_NAME, PROJ_ID);assert(k != -1);Log("create key done", Debug) << " server key : " << TransToHex(k) << endl;// 2. 创建共享内存 -- 建议要创建一个全新的共享内存 -- 通信的发起者int shmid = shmget(k, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0666); //if (shmid == -1){perror("shmget");exit(1);}Log("create shm done", Debug) << " shmid : " << shmid << endl;// 3. 将指定的共享内存,挂接到自己的地址空间char *shmaddr = (char *)shmat(shmid, nullptr, 0);Log("attach shm done", Debug) << " shmid : " << shmid << endl;int fd = OpenFIFO(FIFO_NAME, READ);for(;;){Wait(fd);// 临界区printf("%s\n", shmaddr);if(strcmp(shmaddr, "quit") == 0) break;// sleep(1);}// 4. 将指定的共享内存,从自己的地址空间中去关联int n = shmdt(shmaddr);assert(n != -1);(void)n;Log("detach shm done", Debug) << " shmid : " << shmid << endl;// 5. 删除共享内存,IPC_RMID即便是有进程和当下的shm挂接,依旧删除共享内存n = shmctl(shmid, IPC_RMID, nullptr);assert(n != -1);(void)n;Log("delete shm done", Debug) << " shmid : " << shmid << endl;CloseFifo(fd);return 0;
}

  上述共享内存代码是结合了命名管道通信进行了访问控制

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

YOLO目标检测——红火蚂蚁识别数据集+已标注yolo格式标签下载分享

实际项目应用&#xff1a;目标检测红火蚂蚁识别数据集在农业、生态学、环境保护、城市管理和学术研究等领域都有着广泛的应用。通过准确识别和定位红火蚂蚁&#xff0c;可以帮助我们更好地了解和管理这一入侵物种&#xff0c;从而减少其对环境和经济的负面影响。数据集说明&…

一文看懂微信小程序新版隐私协议(附带弹窗组件)

一、前言 微信小程序近期又迎来了一次改革–9月15日之后如果小程序涉及调用微信的隐私接口获取用户的信息的&#xff0c;需要用户手动同意协议后才可正常调用接口&#xff0c;否则会返回报错信息。 隐私接口目前常用的有&#xff1a;手机号快捷获取、读取照片、获取用户的头像…

【爬虫】实验项目三:验证码处理与识别

目录 一、实验目的 二、实验预习提示 三、实验内容 实验要求 基本要求&#xff1a; 改进要求A&#xff1a; 改进要求B&#xff1a; 四、实验过程 基本要求 五、源码如下 六、资料 一、实验目的 部分网站可能会使用验证机制来阻止用户无效登录或者是验证用户不是用程…

自己设计CPU学习之路——基于《Xilinx FPGA应用开发》

1. 一个32组位宽为32的寄存器堆 框图 代码 regfile.h ifndef __FEGFILE_HEADER__define __REGFILE_HEADER__define HIGH 1b1define LOW 1b0define ENABLE_ 1b0define DISABLE_ 1b1define DATA_W 32define DataBus 31:0define DATA_D 32d…

socket编程

网络协议指的是计算机网络中互相通信的对等实体之间交换信息时所必须遵守的规则的集合。一般系统网络协议包括五个部分&#xff1a;通信环境&#xff0c;传输服务&#xff0c;词汇表&#xff0c;信息的编码格式&#xff0c;时序、规则和过程。 Socket是应用层和TIP/IP协议簇通…

Middleware ❀ Kafka功能与使用详解

文章目录 1. 概述1.1. 消息队列1.2. 应用场景1.3. 工作模式1.4. 基础结构1.4.1. 结构组件1.4.2. 数据同步1.4.3. ACK机制1.4.4. 分区机制1.4.4.1. 使用Partition Key写入1.4.4.2. 轮询写入 - 默认规则1.4.4.3. 指定Partition写入 1.4.5. Offset偏移量1.4.5.1. 消息顺序性1.4.5.…

linux系统中驱动框架基本分析

大家好&#xff0c;今天分享一篇Linux驱动软件设计思想的文章。由于文章较长&#xff0c;可以先收藏后再慢慢看。 一、Linux驱动的软件架构 1.1 出发点 为适应多种体系架构的硬件&#xff0c;增强系统的可重用和跨平台能力。 1.2 分离思想 为达到一个驱动最好一行都不改就…

Apache DolphinScheduler - 快速扩展 TaskPlugin 从入门到放弃

目前在大数据生态中&#xff0c;调度系统是不可或缺的一个重要组件。Apache DolphinScheduler 作为一个顶级的 Apache 项目&#xff0c;其稳定性和易用性也可以说是名列前茅的。而对于一个调度系统来说&#xff0c;能够支持的可调度的任务类型同样是一个非常重要的因素&#xf…

国内访问香港服务器选择什么路线?

​  国内访问香港服务器可以选择多种路线。首先&#xff0c;我们了解下各个线路的速度延迟。 一、CN2直连&#xff1a;解决了不同互联网服务提供商之间访问的难题&#xff0c;不需要绕到国际网络再从中国的三个网络入口进入。 二、优化直连&#xff1a;全国平均延迟60ms&…

Ubuntu----Linux命令-----防火墙(查看、关闭、启动)

一、查看防火墙状态 命令&#xff1a;ufw status 说明&#xff1a; 活动&#xff1a;防火墙是开启的 不活动&#xff1a;防火墙是关闭的 二、开启防火墙 命令&#xff1a;sudo ufw enable 开启防火墙后&#xff0c;可以查看防火墙状态 三、关闭防火墙 命令&#xff1a;sud…

如何通过构建遥感光谱反射信号与地表参数之间的关系模型来准确估算植被参数?植被参数光学遥感反演方法(Python)及遥感与生态模型数据同化算法

目录 专题一 植被参数遥感反演理论 专题二 植被叶片及冠层反射率模拟与处理 专题三 植被遥感模型参数敏感性分析 专题四 基于查找表(LUT)方法反演植被参数 专题五 基于优化算法反演植被参数 专题六 基于机器学习反演植被参数 专题七 遥感数据同化理论 专题八 同化遥感反…

单目标应用:基于成长优化算法(Growth Optimizer,GO)的微电网优化调度MATLAB

一、微网系统运行优化模型 微电网是由分布式电源、储能装置和能量转换装置等组成的小型发配电系统&#xff0c;具有成本低、电压低、污染小等特点。由于环保和能源压力&#xff0c;清洁可再生能源和分布式能源工业发展潜力巨大。微电网控制器可实现对电网的集中控制&#xff0…

Canonical 发布公告,Ubuntu可以在 Windows 10 商店找到

导读Canonical 前几天正式发布公告称&#xff0c;“Windows 10 Loves Ubuntu”&#xff0c;其 Ubuntu 16.04 LTS 在 Windows 10 商店中以应用的方式出现&#xff0c;这是继 openSUSE 及 SLES 之后&#xff0c;又一款可以从 Windows 10 商店中下载的 Linux 操作系统。 一些用户已…

GO语言网络编程(并发编程)并发介绍,Goroutine

GO语言网络编程&#xff08;并发编程&#xff09;并发介绍&#xff0c;Goroutine 1、并发介绍 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。 B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更…

Apache Linki 1.3.1+DataSphereStudio+正常启动+微服务+端口号

我使用的是一键部署容器化版本&#xff0c;官方文章 默认会启动6个 Linkis 微服务&#xff0c;其中下图linkis-cg-engineconn服务为运行任务才会启动,一共七个 LINKIS-CG-ENGINECONN:38681 LINKIS-CG-ENGINECONNMANAGER:9102 引擎管理服务 LINKIS-CG-ENTRANCE:9104 计算治理入…

Linux常用命令——convertquota命令

在线Linux命令查询工具 convertquota 把老的配额文件转换为新的格式 补充说明 convertquota命令用于将老的磁盘额数据文件&#xff08;“quota.user”和“quota.group”&#xff09;转换为新格式的文件&#xff08;“quota.user”和“quota.group”&#xff09;。 语法 c…

C/C++输出绝对值 2019年9月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析

目录 C/C输出绝对值 一、题目要求 1、编程实现 2、输入输出 二、解题思路 1、案例分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 C/C输出绝对值 2019年9月 C/C编程等级考试一级编程题 一、题目要求 1、编程实现 输入一个浮点数&#xff0c;输出这个…

cpolar内网穿透

目录 一、引言二、什么是cpolar三、内网穿透四、如何使用cpolar1、下载cpolar软件安装包2、注册cpolar账号3、使用cpolar 一、引言 当我们完成了一个tomcat的web项目之后&#xff0c;如果我们想让其他电脑访问到这个项目&#xff0c;我们可以让其他电脑和本机连接到同一个局域…

git标签基础

打标签:git可以给仓库历史中某个提交打上标签,以示重要,比较有代表人们会使用这个功能来标记发布结点(V1.0,V2.0) 列出本地标签: git tag --list git tag -l "V1.85*" 列出远端仓库的中所有标签 git ls-remote --tags给标签添加一些描述信息 git tag -a v1.3 -m …

【文字到语音的论文总结】

1.文字到语音的整个过程 文字到语音的一般整体结构 主要是下面这个流程&#xff0c;每个网络可能会把其中两者或是三者融合在一起来&#xff1b; 长度不同的问题 生成的语音可能和文字的长度并不一样&#xff0c;因此需要解决这个问题 Tactron使用的是交叉注意力的方式解…