当前位置: 首页 > news >正文

存储课程学习笔记6_io接口练习(readv,writev, 借助本地socket实现进程间(sendmsg,recvmsg)通过共享内存数据交互)

已经对io_uring进行简单的练习,有必要对readv,writev,sendmsg,recvmsg进行练习。

这类接口是可以一次性操作不连续的内存进行操作,减少了系统调用次数,也提升了整个io读写性能。

核心主要关注函数对应的参数,主要是结构体struct iovec。

0:总结

1:主要练习sendv和readv函数接口, 构造struct iovec结构体发送或者接收不连续内存的处理。

2:练习AF_UNIX 进行原生socket的通信。

3:基于socket通信的基础上,创建共享内存,多个进程之间实现交互(shm_open, mmap)

4:多个进程之间使用共享资源(这里共享内存),需要考虑互斥。

1:readv和writev简单demo进行练习。

1.1 测试demo代码,关注两个函数和结构struct iovec参数的处理。

把不连续的内存写入连续的内存中。 或者从不连续的内存中读到连续的内存中。

一次性把多个不连续缓冲区的内容写入文件中。

然后把文件中连续内存按不同字节读取到不连续缓冲区中。

//使用readv和writev进行测试 用于将不同的缓冲区写入或者写入不同的缓冲区中。  一次调用,减少系统调用。
//readv ===》网络接收数据存储在不同的多个非连续的内存缓冲区中,  从文件中读取放入不同的内存中。
//writev ===》高效写入 多个缓冲区一次性写入。   零拷贝,文件合并,日志记录,管道写入等。/********************
struct iovec {void  *iov_base;    // Starting address size_t iov_len;     // Number of bytes to transfer 
};ssize_t readv(int fd, const struct iovec *iov, int iovcnt);ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
*********************/#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>#include <sys/uio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{if(argc < 2){printf("please use ./test 1 is writev, ./test 2 is readv. \n");return -1;}int para = atoi(argv[1]);if(para != 1 && para != 2){return -2;}//实际上就是操作不连续内存  这里用文件进行演示 if(para == 1) //测试writev写入功能 不同内存写入连续内存 原子操作{int fd = open("test.txt", O_WRONLY | O_CREAT, 0644);if (fd == -1) {perror("open");exit(EXIT_FAILURE);}struct iovec iov[3];const char *str1 = " Test of 0。\n";iov[0].iov_base = (void *)str1;iov[0].iov_len = strlen(str1);const char *str2 = " Use writev test。\n";iov[1].iov_base = (void *)str2;iov[1].iov_len = strlen(str2);const char *str3 = " Over over。\n";iov[2].iov_base = (void *)str3;iov[2].iov_len = strlen(str3);//ssize_t nwrite = writev(fd, iov, 3); //返回的是成功写入的总字节数 if (nwrite == -1) {perror("writev");exit(EXIT_FAILURE);}printf("writev %ld bytes to test.txt\n", nwrite);close(fd);}if(para == 2) //测试readv 将连续内存一次性读出到不同的内存中{int fd = open("test.txt", O_RDONLY);if (fd == -1) {perror("open");exit(EXIT_FAILURE);}struct iovec iov[3];//这里要提供内存char buffer1[10] = {0};char buffer2[15] = {0};char buffer3[30] = {0};iov[0].iov_base = buffer1;iov[0].iov_len = sizeof(buffer1) - 1;iov[1].iov_base = buffer2;iov[1].iov_len = sizeof(buffer2) - 1;iov[2].iov_base = buffer3;iov[2].iov_len = sizeof(buffer3) - 1;ssize_t nread = readv(fd, iov, 3);if (nread == -1) {perror("readv");exit(EXIT_FAILURE);}printf("Read %ld bytes: \n", nread);printf("buffer1: %s\n", buffer1);printf("buffer2: %s\n", buffer2);printf("buffer3: %s\n", buffer3);close(fd);}return 0;
}

1.2 测试结果。

ubuntu@ubuntu:~/start_test$ gcc readv_writev.c -o readv_writev
ubuntu@ubuntu:~/start_test$ ./readv_writev
please use ./test 1 is writev, ./test 2 is readv. 
ubuntu@ubuntu:~/start_test$ ./readv_writev 1
writev 48 bytes to test.txt
ubuntu@ubuntu:~/start_test$ ./readv_writev 2
Read 48 bytes: 
buffer1:  Test of 
buffer2: 0。Use writ
buffer3: ev test。Over over。ubuntu@ubuntu:~/start_test$ cat test.txt Test of 0。Use writev test。Over over。

2:不同进程之间用共享内存进行交互。

2.1 练习分析

进程之间的通信方式有很多中,但是,无关联的进程之间通信,有哪些方案呢。

可以借助原生sock实现不同进程之间的信息交互。

使用共享内存的方式,申请一块内存,借助unix sock把堆内存的地址信息在不同进程之间进行交互后,统一使用同一块内存。

2.2 recvmsg练习,需要绑定socket等待对端。。。

借助recvmsg函数参数结构中的控制消息区struct cmsghdr *cmsg 可以把共享内存地址传递进行取数据。

关注两个点:

1:recvmsg接收对端的数据,接收到的数据进行解析。

2:解析接收到数据的控制区携带的数据信息,进行其他业务处理,或者读写数据。

//需要创建本地socket  一个是接收对端的sendmsg  一个是从控制消息区获取共享内存指针 打印对应信息 
int recvmsg_test()
{//创建socket 进行绑定 仅供演示 recvmsg阻塞等待消息的接收//创建本地socket并进行绑定int sock_fd = 0;if ((sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {perror("socket");exit(EXIT_FAILURE);}struct sockaddr_un addr = {0};addr.sun_family = AF_UNIX;strncpy(addr.sun_path, "/tmp/temp_test.sock", sizeof(addr.sun_path) - 1);if (bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {perror("bind");exit(EXIT_FAILURE);}//创建对应的接收缓冲区struct iovec io = {0};char buffer[1024] = {0};struct msghdr msg = {0};//创建msg 做必要的空间申请  cmsg申请空间 struct cmsghdr *cmsg = NULL;char cmsg_buffer[CMSG_SPACE(sizeof(int))];memset(cmsg_buffer, 0, sizeof(cmsg_buffer));msg.msg_control = cmsg_buffer;msg.msg_controllen = sizeof(cmsg_buffer);io.iov_base = buffer;io.iov_len = 1024;msg.msg_iov = &io;msg.msg_iovlen = 1;//阻塞等待 接收对端的消息printf("Waiting for data...\n");if (recvmsg(sock_fd, &msg, 0) == -1) {perror("recvmsg");exit(EXIT_FAILURE);}printf("Received data: %s\n", buffer);//开始处理控制区的数据 获取并进行必要的校验 cmsg = CMSG_FIRSTHDR(&msg);if (cmsg == NULL || cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {fprintf(stderr, "Invalid control message\n");exit(EXIT_FAILURE);}if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {fprintf(stderr, "Invalid control message\n");exit(EXIT_FAILURE);}//获取控制区中的对应的共享内存的对应fdint shmem_fd;shmem_fd = *(int*)CMSG_DATA(cmsg);//共享内存fd 通过mmap把共享内存和进程进行关联 获取内容void *shmem_ptr;if ((shmem_ptr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, shmem_fd, 0)) == MAP_FAILED) {perror("mmap");exit(EXIT_FAILURE);}printf("Shared memory content: %s\n", (char*)shmem_ptr);munmap(shmem_ptr, 1024);close(shmem_fd);close(sock_fd);return 0;
}

2.3 sendmsg的练习

int sendmsg_test()
{//借助共享内存  创建共享内存 设置共享内存大小//多进程使用相同共享内存 注意加锁。int shmemfd = shm_open("share_memory", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);if (shmemfd == -1) {perror("shm_open");exit(EXIT_FAILURE);}ftruncate(shmemfd, 1024);//内存映射 映射上面创建的共享内存到进程中使用void *shmem_ptr;shmem_ptr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, shmemfd, 0);if (shmem_ptr == MAP_FAILED) {perror("mmap");exit(EXIT_FAILURE);}//给创建的共享内存中塞入数据// char *str = "my test  of shared memory.\n";// memcpy(shmem_ptr, str, strlen(str));sprintf((char*)shmem_ptr, "Hello from shared memory!");//给共享内存中写入 借助sendmsg把信息发送给对应的目标socket//创建本地socket int sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); //s失败时注意上面资源的释放if (sockfd == -1) {perror("socket");exit(EXIT_FAILURE);}struct sockaddr_un addr = {0};addr.sun_family = AF_UNIX;strncpy(addr.sun_path, "/tmp/temp_test.sock", sizeof(addr.sun_path)-1);
//参考前面的逻辑 设置发送字符串struct iovec iov[3];const char *str1 = " Test of one。\n";iov[0].iov_base = (void *)str1;iov[0].iov_len = strlen(str1);const char *str2 = " Use writev test。\n";iov[1].iov_base = (void *)str2;iov[1].iov_len = strlen(str2);const char *str3 = " Over over。\n";iov[2].iov_base = (void *)str3;iov[2].iov_len = strlen(str3);
/******************
struct msghdr {void         *msg_name;       // 地址接收者的套接字地址socklen_t     msg_namelen;    // 地址接收者的套接字地址长度struct iovec *msg_iov;        // I/O向量数组,用于指定待发送或接收的数据缓冲区size_t        msg_iovlen;     // I/O向量数组中元素的数量void         *msg_control;    // 与协议相关的辅助数据(如控制消息、带外数据等)size_t        msg_controllen; // 辅助数据的长度int           msg_flags;      // 消息标志位,如 MSG_OOB、MSG_PEEK 等
};
*******************/struct msghdr msg = {0};//	CMSG_SPACE 一个宏  获取下面cmsg的大小 char cmsg_buffer[CMSG_SPACE(sizeof(int))] = {0};msg.msg_control = cmsg_buffer;msg.msg_controllen = CMSG_LEN(sizeof(int));msg.msg_name = &addr;msg.msg_namelen = sizeof(addr);
//设置相关的控制消息区struct cmsghdr *cmsg = NULL;cmsg = CMSG_FIRSTHDR(&msg); //这个宏获取上面msg中对应的控制区地址  可以先定义cmsg再赋值给msg中cmsg->cmsg_level = SOL_SOCKET;cmsg->cmsg_type = SCM_RIGHTS;cmsg->cmsg_len = CMSG_LEN(sizeof(int));*(int*)CMSG_DATA(cmsg) = shmemfd; //把共享内存地址放到这里 对端可以从这个控制消息数据区获取到//真正数据设置	msg.msg_iov = iov;msg.msg_iovlen = 3;if (-1 == sendmsg(sockfd, &msg, 0)) {perror("sendmsg");exit(EXIT_FAILURE);}close(sockfd);munmap(shmem_ptr, 1024);close(shmemfd);shm_unlink("share_memory");return 0;
}

2.4 main函数入口

//sendmsg和recvmsg  配合AF_UNIX实现不同进行之间通信。//多个进程之间进项交互  可以使用共享内存 
//有相关的进程可以用信号量配合共享内存直接使用
//无关联的进程   使用本地socket的方式进行交互
/*****************************
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);struct msghdr {void         *msg_name;       //* Optional address socklen_t     msg_namelen;    //* Size of address struct iovec *msg_iov;        //* Scatter/gather array size_t        msg_iovlen;     //* # elements in msg_iov void         *msg_control;    //* Ancillary data, see below size_t        msg_controllen; //* Ancillary data buffer len int           msg_flags;      //* Flags (unused) 
};*******************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>#include <sys/types.h>
#include <sys/socket.h>#include <sys/un.h>
#include <fcntl.h>#include <sys/mman.h>
int sendmsg_test();
int recvmsg_test();
//借助unix套接字  实现不同进程之间消息交互
int main(int argc, char *argv[])
{if(argc < 2){printf("please use ./test 1 is sendmsg, ./test 2 is recvmsg. \n");return -1;}int para = atoi(argv[1]);if(para != 1 && para != 2){return -2;}if(para == 1){return sendmsg_test();}return recvmsg_test();
}

2.5 结果查看

#需要先运行 sendmsg_recvmsg 接收端   接收端创建本地socket文件 等待对端的发送
#这是接收端  先运行
ubuntu@ubuntu:~/start_test$ ./sendmsg_recvmsg 2
Waiting for data...
Received data:  Test of one。Use writev test。Over over。Shared memory content: Hello from shared memory!#另外的终端运行 发送 进行通信测试  从上面的接收可以看到  接收到对应的接收  共享内存中的数据通过消息控制区获取到
#这是发送端 
ubuntu@ubuntu:~/start_test$ ./sendmsg_recvmsg 1

http://www.mrgr.cn/news/23805.html

相关文章:

  • pointpillar部署-TensorRT实现(二)
  • PostgreSQL 日常SQL语句查询记录
  • openharmony 应用支持常驻和自启动
  • 揭秘!ArrayList 扩容机制背后的那些“小心机“——不同版本的源码深度解析
  • 时空特征融合方向小论文创新点一次性都给你!看到就是赚到
  • log4j
  • ELK 架构中 ES 性能优化
  • 在 SNMP 中的数据类型码
  • nnunetv2系列:使用默认的预测类推理2D数据
  • Java实现建造者模式和源码中的应用
  • MMO:道具系统
  • opencv图像透视处理
  • jupyter出错ImportError: cannot import name ‘np_utils‘ from ‘keras.utils‘ ,怎么解决?
  • 数据看板多端查看无压力,教你轻松设置响应式布局
  • 论文速读|信任 PRoC3S:利用大型语言模型和约束满足解决长时域机器人问题。
  • framebuffer帧缓存
  • 从学习到的因果网络中估计因果效应
  • 深度优先算法,广度优先算法,hill climbing,贪心搜索,A*算法,启发式搜索算法是什么,比起一般搜索法算法有什么区别
  • 深度盘点:行业领先的crm管理系统软件有哪些?
  • 排名再升2位 中国平安位列BrandZ最具价值中国品牌第9位