Linux下socketpair系统API调用使用说明

news2024/12/23 22:31:29

目录

1.socketpair函数说明

 2.socketpair使用举例


在阅读nginx源码时,发现其调用socketpair来实现master和worker进程之间进行数据交互。其代码如下:

思考:master和worker进程是父子关系,有亲属关系的进程通过pipe/pipe2(匿名管道)和mkfifo(有名管道)也能实现数据传输,为什么要使用socketpair来进行数据交互?

原因:socketpair创建的全双工的一对套接字,而匿名管道和有名管道是单工的。

匿名管道和有名管道使用可以参考如下博客:

https://www.cnblogs.com/fortunely/p/14648146.html

1.socketpair函数说明

socketpair创建管道之后,fds[0]和fds[1]均可以读写,读写可发生在一个线程中,也可以发生在父子进程之间。关于socketpair使用,可参考如下说明:

SOCKETPAIR(2)                                                                          Linux Programmer's Manual                                                                         SOCKETPAIR(2)

NAME
       socketpair - create a pair of connected sockets

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int socketpair(int domain, int type, int protocol, int sv[2]);

DESCRIPTION
       The  socketpair()  call creates an unnamed pair of connected sockets in the specified domain, of the specified type, and using the optionally specified protocol.  For further details of these
       arguments, see socket(2).

       The file descriptors used in referencing the new sockets are returned in sv[0] and sv[1].  The two sockets are indistinguishable.

RETURN VALUE
       On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.

       On Linux (and other systems), socketpair() does not modify sv on failure.  A requirement standardizing this behavior was added in POSIX.1-2016.

ERRORS
       EAFNOSUPPORT
              The specified address family is not supported on this machine.

       EFAULT The address sv does not specify a valid part of the process address space.

       EMFILE The per-process limit on the number of open file descriptors has been reached.

       ENFILE The system-wide limit on the total number of open files has been reached.

       EOPNOTSUPP
              The specified protocol does not support creation of socket pairs.

       EPROTONOSUPPORT
              The specified protocol is not supported on this machine.

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008, 4.4BSD.  socketpair() first appeared in 4.2BSD.  It is generally portable to/from non-BSD systems supporting clones of the BSD  socket  layer  (including  System V
       variants).

NOTES
       On Linux, the only supported domain for this call is AF_UNIX (or synonymously, AF_LOCAL).  (Most implementations have the same restriction.)

       Since Linux 2.6.27, socketpair() supports the SOCK_NONBLOCK and SOCK_CLOEXEC flags in the type argument, as described in socket(2).

       POSIX.1 does not require the inclusion of <sys/types.h>, and this header file is not required on Linux.  However, some historical (BSD) implementations required this header file, and portable
       applications are probably wise to include it.
 

 2.socketpair使用举例

2.1 如下代码演示阻塞和非阻塞socketpair使用:

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>

int parentProcess(int* fds, int num) {
	if (num < 2) {
		return -1;
	}
	char buf[128] {0};
	recv(fds[0], buf, 128, 0);
	printf("parent:: %s\n", buf);
	recv(fds[0], buf, 128, 0);
	printf("parent:: %s\n", buf);
	sleep(1);
	recv(fds[0], buf, 128, 0);
	printf("parent:: %s\n", buf);
	sleep(1);
	memset(buf, 0x00, sizeof(buf));
	strcpy(buf, "hello child, I am parent !");
	send(fds[1], buf, strlen(buf), 0);
	close(fds[0]);
	close(fds[1]);
	return 0;
}

int childProcess(int* fds, int num) {
	if (num < 2) {
		return -1;
	}
	char buf[128] = "hello parent, I am child";
	send(fds[1], buf, strlen(buf), 0);
	sleep(1);
	send(fds[1], buf, strlen(buf), 0);
	sleep(1);
	char *pStr = (char*)"给父进程再发一次消息";
	send(fds[1], pStr, strlen(pStr), 0);
	memset(buf, 0x00, sizeof(buf));
	sleep(1);
	recv(fds[0], buf, 128, 0);
	printf("child:: %s\n", buf);
	close(fds[0]);
	close(fds[1]);
	return 0;
}

//设置文件描述符非阻塞
int fd_nonblocking(int s)
{
    int  nb;
    nb = 1;
	//方法一
	/*
	int flag = fcntl(s, F_GETFL);
    flag |= O_NONBLOCK;
    return fcntl(s, F_SETFL, flag);
	*/
	//方法二
    return ioctl(s, FIONBIO, &nb);
}

int close_channel(int* fds) {
	if (close(fds[0]) == -1) {
        printf("close() channel fds[0] failed\n");
    }

    if (close(fds[1]) == -1) {
        printf("close() channel fds[1] failed\n");
    }

	return 0;
}

void testNonblockingSocketFd(int* fds) {
	if (-1 == fd_nonblocking(fds[0])) {
		printf("fd_nonblocking fds[0] failed\n");
		close_channel(fds);
	}
	if (-1 == fd_nonblocking(fds[1])) {
		printf("fd_nonblocking fds[1] failed\n");
		close_channel(fds);
	}
}

int main()
{
    int fds[2];
	// fds[0]: 读  fds[1]: 写 
    socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
#ifdef NOBLOCKFD
	testNonblockingSocketFd(fds);
#endif
    int pid = fork();
	printf("parent: %d, child: %d\n", getpid(), pid);
	switch (pid) {
    case -1: // error
        return -1;
    case 0: // child
		childProcess(fds, 2);
		printf("child exit \n");
        break;
    default: // parent
		parentProcess(fds, 2);
		printf("parent exit \n");
        break;
    }

	return 0;
}

阻塞应用:

非阻塞应用: 

通过非阻塞应用发现, 父进程退出后,子进程往管道发送数据后,接着自己读到了发送的数据。

2.2只使用一端进行读写

如2.1可以发现fds[0]和fds[1]均可以读写,那么自己进程读到的可能是别的进程发来的数据,也可能是自己进程发来的数据,编程逻辑很不清晰。

常用的方式是:

(1) 父进程 close(fd[0]),使用fd[1]读写,子进程close(fd[1]),使用fd[0]读写

(1) 父进程 close(fd[1]),使用fd[0]读写,子进程close(fd[0]),使用fd[1]读写

如下代码显示情形(1)

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>

int parentProcess(int* fds, int num) {
	if (num < 2) {
		return -1;
	}
	close(fds[0]); // 关闭fds[0] 使用fds[1]读写,子进程中关闭fds[1] 使用fds[0]读写
	char buf[128] {0};
	char *pStr = (char*)"hello child, I am parent";
	int inum = 0;
	while (inum++ < 10) {
		memset(buf, 0x00, sizeof(buf));
		//写
		write(fds[1], pStr, strlen(pStr));
		//读
		read(fds[1], buf, 128);
		printf("parent收到child的招呼 %d :: %s\n", inum, buf);
		sleep(1);
	}

	close(fds[1]);
	return 0;
}

int childProcess(int* fds, int num) {
	if (num < 2) {
		return -1;
	}
	close(fds[1]);
	char buf[128] {0};
	char *pStr = (char*)"hello parent, I am child";
	int inum = 0;
	while (inum++ < 10) {
		memset(buf, 0x00, sizeof(buf));
		//读
		read(fds[0], buf, 128);
		printf("child收到paren的招呼 %d :: %s\n", inum, buf);
		//写
		write(fds[0], pStr, strlen(pStr));
		sleep(1);
	}
	
	close(fds[0]);
	return 0;
}

//设置文件描述符非阻塞
int fd_nonblocking(int s)
{
    int  nb;
    nb = 1;
	//方法一
	/*
	int flag = fcntl(s, F_GETFL);
    flag |= O_NONBLOCK;
    return fcntl(s, F_SETFL, flag);
	*/
	//方法二
    return ioctl(s, FIONBIO, &nb);
}

int close_channel(int* fds) {
	if (close(fds[0]) == -1) {
        printf("close() channel fds[0] failed\n");
    }

    if (close(fds[1]) == -1) {
        printf("close() channel fds[1] failed\n");
    }

	return 0;
}

void testNonblockingSocketFd(int* fds) {
	if (-1 == fd_nonblocking(fds[0])) {
		printf("fd_nonblocking fds[0] failed\n");
		close_channel(fds);
	}
	if (-1 == fd_nonblocking(fds[1])) {
		printf("fd_nonblocking fds[1] failed\n");
		close_channel(fds);
	}
}

int main()
{
    int fds[2];
    socketpair(PF_UNIX, SOCK_STREAM, 0, fds);
#ifdef NOBLOCKFD
	testNonblockingSocketFd(fds);
#endif
    int pid = fork();
	printf("parent: %d, child: %d\n", getpid(), pid);
	switch (pid) {
    case -1: // error
        return -1;
    case 0: // child
		childProcess(fds, 2);
		printf("child exit \n");
        break;
    default: // parent
		parentProcess(fds, 2);
		printf("parent exit \n");
        break;
    }

	return 0;
}

运行结果如下:

2.3一个master多个worker数据收发

如下代码模拟nginx中一个 master进程,多个worker进程进行数据交互

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>

int parentProcess(int* fds, int num) {
	if (num < 2) {
		return -1;
	}
	close(fds[0]); // 关闭fds[0] 使用fds[1]读写,子进程中关闭fds[1] 使用fds[0]读写
	char buf[128] {0};
	char *pStr = (char*)"hello child, I am parent";
	int inum = 0;
	while (inum++ < 3) {
		memset(buf, 0x00, sizeof(buf));
		//写
		write(fds[1], pStr, strlen(pStr));
		//读
		read(fds[1], buf, 128);
		printf("parent [%d] %d :: %s\n", getpid(), inum, buf);
		sleep(1);
	}

	close(fds[1]);
	return 0;
}

int childProcess(int* fds, int num) {
	if (num < 2) {
		return -1;
	}
	close(fds[1]);
	char buf[128] {0};
	char *pStr = (char*)"hello parent, I am child";
	char sendBuf[128] = {0};
	sprintf(sendBuf, "hello parent, I am child %d", getpid());
	int inum = 0;
	while (inum++ < 3) {
		memset(buf, 0x00, sizeof(buf));
		//读
		read(fds[0], buf, 128);
		printf("child [%d] %d :: %s\n", getpid(), inum, buf);
		//写
		write(fds[0], sendBuf, strlen(sendBuf));
		sleep(1);
	}
	
	close(fds[0]);
	return 0;
}

//设置文件描述符非阻塞
int fd_nonblocking(int s)
{
    int  nb;
    nb = 1;
	//方法一
	/*
	int flag = fcntl(s, F_GETFL);
    flag |= O_NONBLOCK;
    return fcntl(s, F_SETFL, flag);
	*/
	//方法二
    return ioctl(s, FIONBIO, &nb);
}

int close_channel(int* fds) {
	if (close(fds[0]) == -1) {
        printf("close() channel fds[0] failed\n");
    }

    if (close(fds[1]) == -1) {
        printf("close() channel fds[1] failed\n");
    }

	return 0;
}

void testNonblockingSocketFd(int* fds) {
	if (-1 == fd_nonblocking(fds[0])) {
		printf("fd_nonblocking fds[0] failed\n");
		close_channel(fds);
	}
	if (-1 == fd_nonblocking(fds[1])) {
		printf("fd_nonblocking fds[1] failed\n");
		close_channel(fds);
	}
}

struct TDataExchangeChannel {
	int fds[2];
};

#define GROUP_NUM 2

int main()
{
#ifdef NOBLOCKFD
	testNonblockingSocketFd(fds);
#endif
	TDataExchangeChannel channels[GROUP_NUM];
	for (int i = 0; i < GROUP_NUM; i++) {
		socketpair(PF_UNIX, SOCK_STREAM, 0, channels[i].fds);
		int pid = fork();
		switch (pid) {
		case -1: // error
			return -1;
		case 0: // child
			childProcess(channels[i].fds, 2);
			printf("child exit \n");
			exit(0);
		default: // parent
			break;
		}
	}

	//父进程给子进程发送消息,并接收子进程发来的消息
	for (int i = 0; i < GROUP_NUM; i++) {
		parentProcess(channels[i].fds, 2);
	}

	return 0;
}

运行效果如下:

 根据运行结果看,master进程为31558,两个子进程为31559,31560,master进程可以分别与两个worker进行数据收发。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Genio 500核心板,MT8385安卓核心板定制方案

Genio 500&#xff08;MT8385&#xff09;核心板搭载Arm Neon引擎的四核Arm Cortex-A73和Cortex-A53&#xff0c;提供必要的处理能力&#xff0c;可以通过2D/3D图形加速器进行增强&#xff0c;然后在高分辨率触摸屏显示器上进行可视化。为了提供先进的多媒体应用和服务&#xf…

电商后台管理项目vue3+express

目录 源码 1.系统功能设计 技术栈&#xff1a;采用前后端分离的开发模式前端&#xff1a;Vue3、Vue-router、Element-Plus、Axios、Echarts后端&#xff1a;Node.js、Express、Jwt、Mysql、Sequelize 2.项目初始化 打开cmd&#xff0c;输入vue ui&#xff08;vue-cli版本要…

Java程序设计入门教程--日期类Date

java.util.Date类是一个简单的日期处理类&#xff0c;它包含了一些关于时间和日期的操作方法&#xff0c;精确到毫秒。它的常用方法如表所示&#xff1a; 方法 说明 public Date() 构造方法&#xff0c;分配 Date 对象并用当前时间初始化此对象&#xff0c;以表示分配它的时…

2023年6月DAMA-CDGA/CDGP数据治理认证你考了吗?

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…

NUC980编译错误,arm-linux-gcc: Command not found

报错问题&#xff1a; make: arm-linux-gcc: Command not found /bin/sh: 1: arm-linux-gcc: not found dirname: missing operand 昨天编译的时候&#xff0c;还小甜甜&#xff0c;今天就牛夫人了。啥也没干啊&#xff01; -----------------------------------------------…

亚马逊云科技与涂鸦智能持续赋能开发者,推动全行业的数智化创新

近几年&#xff0c;智能产品已渗透至人们生活的方方面面&#xff0c;IoT技术市场规模也随之获得较快增长&#xff0c;据IoT Analytics的数据&#xff0c;2023年IoT市场规模将增长19%&#xff0c;或成为经济波动周期的一大黑马赛道&#xff0c;但下游应用场景与需求的高度碎片化…

从零开始Vue3+Element Plus后台管理系统(17)——一键换肤的N种方案

暗黑模式 基于Element Plus和Tailwind CSS灵活的设计&#xff0c;我们很容易在项目中实现暗黑模式&#xff0c;具体可以参考之前的文章《从零开始写一个Vue3Element Plus的后台管理系统(二)——Layout页面布局的实现》 换肤方案 如果需要给用户提供更多主题&#xff0c;更丰…

【Android项目开发】聊天功能-主界面设计(对标企业需求)

文章目录 一、引言二、详细设计1、解决需求&#xff08;1&#xff09;图形问题&#xff08;2&#xff09;文本长度问题&#xff08;3&#xff09;时间转换问题 2、UI设计&#xff08;1&#xff09;主界面&#xff08;2&#xff09;适配器 3、Adapter适配器4、测试参数 三、附录…

龙芯2K1000实战开发-USB/PCIe/HDMI外设开发

文章目录 概要整体架构流程技术名词解释技术细节小结概要 提示:这里可以添加技术概要 本文主要针对2k1000的PCIE和USB外设的国产化设计 整体架构流程 提示:这里可以添加技术整体架构 使用2k1000自带的以太网pcie控制器,USB控制器。 考虑到龙芯没有HDMI接口,选用龙讯半…

从小白走向进阶:如何系统的学习it技术

无论是初学者还是有经验的专业人士&#xff0c;在学习一门新的IT技术时&#xff0c;都需要采取一种系统性的学习方法。那么作为一名技术er&#xff0c;你是如何系统的学习it技术的呢。 一、it技术介绍 1. Spring、SpringMVC、MyBatis、MyBatis-Plus、tkMapper&#xff0c;Spri…

分享一组真实的按钮

先看效果图&#xff1a; 再看代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>发光的按钮</title><style>* {border: 0;box-sizing: border-box;margin: 0;padding: 0;…

VMware ESXi 8.0U1a macOS Unlocker OEM BIOS (标准版和厂商定制版)

VMware ESXi 8.0 Update 1a macOS Unlocker & OEM BIOS (标准版和厂商定制版) ESXi 8.0U1 标准版&#xff0c;Dell HPE 联想 浪潮 定制版 请访问原文链接&#xff1a; https://sysin.org/blog/vmware-esxi-8-u1-oem/&#xff0c;查看最新版。原创作品&#xff0c;转载请保…

pytorch实战 -- 初窥张量

张量的创建 张量&#xff08;Tensors&#xff09;类似于NumPy的ndarrays&#xff0c;但张量可以在GPU上进行计算。 所以从本质上来说&#xff0c;PyTorch是一个处理张量的库。一个张量是一个数字、向量、矩阵或任何n维数组。 下面分别展示了0维张量到n位张量&#xff1a; im…

SVN客户端的下载和安装(图文超详细)

目录 0.准备工作 1.SVN客户端安装包安装 2.安装语言包 0.准备工作 博主安装环境&#xff1a;windows x86 SVN客户端下载地址&#xff1a;下载 SVN (tortoisesvn.net) 【下载地址中需下载符合电脑版本的安装包&#xff0c;以及语言包】 注&#xff1a;下载两个包后&#…

Linux操作系统相关介绍

目录 一、认识Linux 二、Linux特点总结 三、Linux版本 &#xff08;1&#xff09;Linux内核版 &#xff08;2&#xff09;Linux发行版 一、认识Linux • 1991年&#xff0c;芬兰的一名大学生Linus Torvalds开发了linux内核 • Linux是一种开放源代码的、自由的、免费的类…

Zookeeper面试这一篇就够了

谈下你对 Zookeeper 的认识&#xff1f; ZooKeeper 是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务。它是一个为分布式应用提供一致性服务的软件&#xff0c;提供的功能包括&#xff1a;配置维护、域名服务、分布式同步、组服务等。 ZooKeeper 的目标就是封装好…

面试官:“你知道什么情况下 HTTPS 不安全么”

面试官&#xff1a;“HTTPS的加密过程你知道么&#xff1f;” 我&#xff1a;“那肯定知道啊。” 面试官&#xff1a;“那你知道什么情况下 HTTPS 不安全么” 我&#xff1a;“这....” 越面觉得自己越菜&#xff0c;继续努力学习&#xff01;&#xff01;&#xff01; 什麽是中…

STM32使用HAL库,串口收发一段时间后出错问题及解决

STM32使用HAL库&#xff0c;串口收发一段时间后出错 问题及解决方法问题1&#xff1a;串口溢出解决方法问题2&#xff1a;串口同时收发&#xff0c;一段时间后串口接收不工作解决办法 问题及解决方法 当STM32使用HAL库进行开发时&#xff0c;偶尔会遇到串口收发数据量大时&…

开源创新 协同融合|2023 开放原子全球开源峰会开源协作平台分论坛即将启幕

由开放原子开源基金会主办&#xff0c;阿里云、CSDN 等单位共同承办的开源协作平台分论坛即将于 6 月 12 日上午在北京经开区北人亦创国际会展中心隆重召开。作为 2023 开放原子全球开源峰会的重要组成部分&#xff0c;开源协作平台分论坛将聚焦于开源代码平台的创新功能、用户…

[Halcon3D] 3D重要算子及简单处理点云模型求高度示例讲解

&#x1f4e2;博客主页&#xff1a;https://loewen.blog.csdn.net&#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;本文由 丶布布原创&#xff0c;首发于 CSDN&#xff0c;转载注明出处&#x1f649;&#x1f4e2;现…