Linux系统编程(七)进程间通信IPC

进程间通讯的7种方式_进程间通信的几种方法-CSDN博客

  1. 管道 pipe(命名管道和匿名管道);
  2. 信号 signal;
  3. 共享内存;
  4. 消息队列;
  5. 信号量 semaphore;
  6. 套接字 socket;

1. 管道

内核提供,单工,自同步机制。 

1.1 匿名管道

磁盘上无法看到,只能有亲缘关系的进程才能用匿名管道。一般用于父子进程间通信。

pipe(2) 系统调用可以创建一个匿名管道 pipefd,文件描述符 pipefd[0] 为读管道,pipefd[1] 为写管道。  

#include <unistd.h>int pipe(int pipefd[2]);#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <fcntl.h>              /* Obtain O_* constant definitions */
#include <unistd.h>int pipe2(int pipefd[2], int flags);

例子,父进程通过管道发送 hello 给子进程:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t pid;int pipefd[2];if (pipe(pipefd) < 0) {perror("pipe");exit(1);}pid = fork();if (pid > 0) {// parentclose(pipefd[0]);write(pipefd[1], "hello", 5);close(pipefd[1]);wait(NULL);exit(0);}else if (pid == 0) {// childclose(pipefd[1]);char buf[50];int len = read(pipefd[0], buf, 50);printf("%d\n", len);write(1, buf, len);close(pipefd[0]);exit(0);}else {perror("fork");exit(1);}exit(0);
}

1.2 命名管道

磁盘上能看到,文件类型为 p 的文件。

mkfifo(3) 函数可以创建一个 fifo 特殊文件(命名管道)。

#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);

2 XSI -> SysV

2.1 消息队列,message queue 

主动端,先发包的一方;被动端,先收包的一方;消息队列可以用于没有亲缘关系的进程间通信

例子,proto.h,包含了传递的内容:

#ifndef PROTO_H__
#define PROTO_H__#define KEYPATH "/etc/services"
#define KEYPROJ 'g'#define NAMESIZE 32struct msg_st
{long mtype;char name[NAMESIZE];int math;int chinese;
};#endif

receiver.c,接收者:

#include <stdio.h>
#include <stdlib.h>
#include "proto.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>int main()
{key_t key;int msgid;struct msg_st rbuf;key = ftok(KEYPATH, KEYPROJ);if (key < 0) {perror("ftok");exit(1);}msgid = msgget(key, IPC_CREAT | 0600);if (msgid < 0) {perror("msgget");exit(1);}while (1){if (msgrcv(msgid, &rbuf, sizeof(rbuf) - sizeof(long), 0, 0) < 0){perror("msgrcv");exit(1);}printf("NAME = %s\n", rbuf.name);printf("MATH = %d\n", rbuf.math);printf("CHINESE = %d\n", rbuf.chinese);}msgctl(msgid, IPC_RMID, NULL);exit(0);
}

运行接收者可以看到创建的 msg queue: 

sender.c,发送者: 

#include <stdio.h>
#include <stdlib.h>
#include "proto.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>int main()
{key_t key;struct msg_st sbuf;int msgid;key = ftok(KEYPATH, KEYPROJ);if (key < 0) {perror("ftock");exit(1);}msgid = msgget(key, 0);if (msgid < 0){perror("msgget");exit(1);}sbuf.mtype = 1;strcpy(sbuf.name, "lzp");sbuf.math = 99;sbuf.chinese = 100;if (msgsnd(msgid, &sbuf, sizeof(sbuf) - sizeof(long), 0) < 0) {perror("msgsend");exit(1);}puts("ok!");exit(0);
}

先运行 receiver,然后运行 sender 发送信息: 

2.2 信号量,semaphore arrays

semget、semop、semctl;可以用于没有亲缘关系的进程间通信

2.3 共享内存,shared memory segment

shmget、shmop、shmctl;之前可以通过 mmap 实现共享内存,不过只能在有亲缘关系的进程间通信。匿名 ipc 也只能在有亲缘关系的进程间通信;

3. 网络套接字 socket

3.1 跨主机的传输

  • 字节序大端存储(低地址处放高字节)小端存储(低地址处放低字节,x86);主机字节序(host)和网络字节序(network);主机序转网络序,网络序转主机序;htons,htonl,ntohs,ntohl。
  • 对齐:结构体中 char 类型可能会占 4 个字节;如果不对齐,那么 int 类型的存储地址可能就不是在 4 的倍数的地址上了,可能需要两次访存才能取出一个 int 类型。深入理解字节对齐-CSDN博客
  • 类型长度问题:不同主机间的架构,机器字长可能不一样,结构体类型大小也可能不一样。解决方案(int32_t,int16_t ...);

socket(2) 系统调用如下: 

#include <sys/types.h>    
#include <sys/socket.h>int socket(int domain, int type, int protocol);
  • domain 指定协议族;
    • AF_INET      IPv4 Internet protocols    ip(7)
    • AF_INET6     IPv6 Internet protocols   ipv6(7)
  • type 指定通信语义;
    • SOCK_STREAM(流式传输):Provides sequenced, reliable, two-way, connection-based byte streams.
    • SOCK_DGRAM(报式传输)  :Supports datagrams (connectionless, unreliable messages of a fixed maximum length).
  • protocol 指定协议族中的协议;

bind(2) 系统调用可以给 socket 绑定地址。

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

recvfrom(2) 可以从 socket 上接收一条 message: 

#include <sys/types.h>
#include <sys/socket.h>ssize_t recv(int sockfd, void *buf, size_t len, int flags);ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
  1. recv(2) 是用于流式套接字的,是提前建立好连接的,一对一的,点对点的, 不需要记录对方的 socket 地址;
  2. recvfrom(2) 可以用于报式和流式套接字,每次需要传入需要通信的 socket 地址;

sendto(2) 函数可以发送 msg 到对应的 socket 地址,可以用于流式和报式传输;而 send(2) 函数不需要指定 socket 地址,只能用于事先建立好连接的流式传输。

#include <sys/types.h>
#include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags);ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

3.2 报式套接字(udp)

被动端(先运行)

  1. 取得 socket(socket);
  2. 给 socket 取得地址,相当于绑定本地的地址(bind);
  3. 收/发消息(recvfrom);
  4. 关闭 socket(close);

主动端

  1. 取得 socket;
  2. 给 socket 取得地址(可省略);
  3. 收/发消息;
  4. 关闭 socket;

__attribute__((packed)):packed属性:使用该属性可以使得变量或者结构体成员使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位对齐。即不进行对齐。

使用如下指令查看被动段的地址和端口(u代表udp):

netstat -anu

proto.h 约定传输格式和内容: 

#ifndef PROTO_H__
#define PROTO_H__#define RCVPORT 1989#define NAMESIZE  11// communication struct
struct msg_st
{char name[NAMESIZE];int math;int ch;
} __attribute__((packed));#endif

receiver.c 接收方, 注意多字节需要使用 ntohl() 来转换:

#include <stdio.h>
#include <stdlib.h>
#include "proto.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>#define IPSTRSIZE 40int main()
{int sd; // socket fdstruct sockaddr_in laddr;struct sockaddr_in raddr;struct msg_st rbuf;socklen_t raddr_len;char ipstr[IPSTRSIZE];// IPV4 DGRAM UDPsd = socket(AF_INET, SOCK_DGRAM, 0/* IPPROTO_UDP */);if (sd < 0) {perror("socket");exit(1);}laddr.sin_family = AF_INET;laddr.sin_port = htons(RCVPORT);inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr);if (bind(sd, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) {perror("bindqqq");exit(1);}/* !!! */raddr_len = sizeof(raddr);while (1){recvfrom(sd, &rbuf, sizeof(rbuf), 0, (struct sockaddr *)&raddr, &raddr_len);inet_ntop(AF_INET, &raddr.sin_addr, ipstr, IPSTRSIZE);printf("message from %s:%d---\n", ipstr, ntohs(raddr.sin_port));printf("name = %s\n", rbuf.name);printf("name = %d\n", ntohl(rbuf.math));printf("name = %d\n", ntohl(rbuf.ch));}close(sd);exit(0);
}

sender.c 发送方,发送报文给对应的地址: 

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include "proto.h"
#include <unistd.h>int main(int argc, char *argv[])
{int sd;struct msg_st sbuf;struct sockaddr_in raddr;if (argc < 2){fprintf(stderr, "usage..\n");exit(1);}sd = socket(AF_INET, SOCK_DGRAM, 0);if (sd < 0){perror("socket");exit(1);}//bind();strcpy(sbuf.name, "Alan");sbuf.math = htonl(99);sbuf.ch = htonl(93);raddr.sin_family = AF_INET;raddr.sin_port = htons(RCVPORT);inet_pton(AF_INET, argv[1], &raddr.sin_addr);if (sendto(sd, &sbuf, sizeof(sbuf), 0, (struct sockaddr *)&raddr, sizeof(raddr)) < 0){perror("sendto");exit(1);}puts("ok\n");close(sd);exit(0);
}

运行结果: 

报式套接字还能实现多播、广播(全网广播和子网广播)、组播:

可以通过 getsockopt() 和 setsockopt() 来打开广播选项。然后将发送的目标地址改成 255.255.255.255 就可以发送广播了。

多点通信(广播、多播、组播)只能用报式套接字实现,因为流式套接字是一对一的,点对点的。

udp 会出现丢包的问题,TTL生存周期(路由跳转个数) ,丢包是由阻塞造成的,当等待队列快满的时候会发生丢包(网络太拥塞),可以使用流量控制解决(限制发送端的速率)。

3.3 流式套接字(tcp)

协议,约定双方对话的格式。

三次握手容易被ddos攻击,可以去掉半连接池,然后使用cookie(对方的ip+端口加上我放的ip+端口加上一个内核产生的令牌)。

Client 端和 Server 端:

client:

  1. 获取 socket;
  2. 给 socket 取得地址;
  3. 发送连接;
  4. 收/发消息;
  5. 关闭连接;

server:

  1. 获取 socket;
  2. 给 socket 取得地址;
  3. 将 socket 置为监听模式;
  4. 接受连接;
  5. 收/发消息;
  6. 关闭;

服务端(server.c)

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <time.h>
#include "proto.h"#define IPSTRSIZE 40
#define BUFSIZE 1024static void server_job(int sd)
{char buf[BUFSIZE];int len = sprintf(buf, FMT_STAMP, (long long)time(NULL));if (send(sd, buf, len, 0) < 0) {perror("send()");exit(1);}
}int main()
{struct sockaddr_in laddr, raddr;socklen_t raddr_len;char ipstr[IPSTRSIZE];int sd = socket(AF_INET, SOCK_STREAM, 0/* default IPPROTO_TCP */);if (sd < 0) {perror("socket()");exit(1);}laddr.sin_family = AF_INET;laddr.sin_port = htons(atoi(SERVERPORT));inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr);if (bind(sd, (void *)&laddr, sizeof(laddr)) < 0) {perror("bind()");exit(1);}// listen for connections on a socketif (listen(sd, 200) < 0){perror("listen()");exit(1);}raddr_len = sizeof(raddr);while (1) {// accept a connection on a socketint newsd;if ((newsd = accept(sd, (void *)&raddr, &raddr_len)) < 0) {perror("accept()");exit(1);}inet_ntop(AF_INET, &raddr.sin_addr, ipstr, IPSTRSIZE);printf("Client: %s : %d\n", ipstr, ntohs(raddr.sin_port));server_job(newsd);// close newsdclose(newsd);}close(sd);exit(0);
}

运行后使用 netstat -ant 可以查看:

当连接被释放后,服务端会进入一段时间的 timewait 状态。

客户端(client.c):

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include "proto.h"
int main(int argc, char* argv[])
{struct sockaddr_in raddr;long long stamp;if (argc < 2) {fprintf(stderr, "Usage...\n");exit(1);}int sd = socket(AF_INET, SOCK_STREAM, 0);if (sd < 0) {perror("socket()");exit(1);}// initiate a connection on a socketraddr.sin_family = AF_INET;raddr.sin_port = htons(atoi(SERVERPORT));inet_pton(AF_INET, argv[1], &raddr.sin_addr);if (connect(sd, (void *)&raddr, sizeof(raddr)) < 0) {perror("connect()");exit(1);}FILE* fp = fdopen(sd, "r+");if (fp == NULL) {perror("fdopen()");exit(1);}if (fscanf(fp, FMT_STAMP, &stamp) < 1) {fprintf(stderr, "bad format\n");}else {fprintf(stdout, "stamp = %lld\n", stamp);}fclose(fp);exit(0);
}

上面这种方法有一个缺点,就是假如 server_job 的任务执行时间太长的话,需要等待 server_job 执行完后才能继续 accept 下一个连接请求,这样效率十分低,所以可以采用多线程的方式来解决,每个进程或线程处理一个连接请求。

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

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

相关文章

亿发进销存管理系统+:多终端无缝协同,实现经营销售场景全覆盖

亿发软件凭借产品、市场、业务的深入理解&#xff0c;在进销存基础上进行了延伸&#xff0c;推出多终端、一体化的“进销存管理系统”多元产品矩阵。对企业经营中进货、出货、销售、付款等进行全程跟踪管理。有效辅助企业解决业务管理、销售管理、库存管理、财务管理等一系列问…

如何做到高级Kotlin强化实战?(一)

高级Kotlin强化实战&#xff08;一&#xff09; 第一章 Kotlin 入门教程1.Kotlin 入门介绍2.Kotlin 与 Java 比较 第一章 Kotlin 入门教程 1.Kotlin 入门介绍 Kotlin 概述 Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言。它主要是 JetBrains 开发团队所开发出来的编程…

卸载vmware时2503,2502报错的解决办法

1.背景 windows 卸载vmware时&#xff0c;显示2503报错&#xff0c;无法完全卸载 2. 解决方案 2.1 参考安装报错2502&#xff0c;2503的处理方式 文献&#xff1a;https://blog.csdn.net/zhangvalue/article/details/80309828 2.1 步骤&#xff1a; 2.1.1 cmd 管理员打开…

实时美颜技术解析:视频美颜SDK如何改变直播行业

实时美颜技术的出现&#xff0c;尤其是视频美颜SDK的应用&#xff0c;正逐渐改变着直播行业的生态。 一、实时美颜技术的原理 实时美颜技术利用人工智能和图像处理算法&#xff0c;对视频中的人物面部进行优化和修饰。该技术通常包含以下几个步骤&#xff1a; 1.人脸检测和识…

MMCV【mmclassification】 从0到1 之 Docker 容器环境搭建步骤总结

🥇 版权: 本文由【墨理学AI】原创首发、各位读者大大、敬请查阅、感谢三连 🎉 声明: 作为全网 AI 领域 干货最多的博主之一,❤️ 不负光阴不负卿 ❤️ 文章目录 📙 Linux 下 Docker 安装环境检查Docker 安装 [ root 或者 sudo 权限用户可安装 ]给 普通用户 加入 Docker …

RedisConnectionException: Unable to connect to localhost/<unresolved>:6379

方法一&#xff1a;删除配置密码选项 一般是因为你在启动redsi服务的时候没有以指定配置文件启动 把application.yml文件中的redis密码注释掉 方法二 以指定配置文件启动 这样就不用删除yml文件中密码的选项了 在redis,windows.conf 中找到requirepass&#xff0c;删除掉前…

python 识别图片点击,设置坐标,离设置坐标越近的优先识别点击

import pyautogui import cv2 import numpy as np import mathdef find_and_click(template_path, target_x, target_y, match_threshold0.8):"""在屏幕上查找目标图片并点击。Args:template_path: 目标图片的路径。target_x: 预设的坐标 x 轴值。target_y: 预设…

ComfyUI中运行Stable Audio Open,实现背景音乐、音效自由

&#x1f9e8;背景 stability在一个月之前默默的发布了Stable Audio Open 1.0的音频音效生成模型&#xff0c;不过好像影响力一般&#xff0c;也没有太多文章分享测试&#xff0c;而今天看comfyui作者的一篇介绍文档&#xff0c;他已经让comfyui默认支持了这个模型。 原开源地…

学分制系统 WebService_PantoSchool SQL注入致RCE漏洞复现

0x01 产品简介 学分制系统由上海鹏达计算机系统开发有限公司研发,是基于对职业教育特点和需求的深入理解,结合教育部相关文件精神,并广泛吸纳专家、学者意见而开发的一款综合性管理系统。系统采用模块化的设计方法,方便学校根据自身教学改革特点、信息化建设进程情况选择、…

如何预防和处理他人盗用IP地址?

IP地址的定义及作用 解释 IP 地址在互联网中的作用。它是唯一标识网络设备的数字地址&#xff0c;类似于物理世界中的邮政地址。 1、IP地址盗窃的定义 解释一下什么是IP地址盗用&#xff0c;即非法使用他人的IP地址或者伪造IP地址的行为&#xff0c;这种行为可能引发法律和安…

uniapp 实人认证

最下面有demo 首先Dcloud创建云服务空间&#xff0c;开启一键登录并充值 下一步 1. 右键项目 》 创建uniCloud云开发环境 》右键uniCloud》关联云服务空间 2. cloudfunctions右键 新建云函数&#xff0c;任意命名&#xff08;例&#xff1a;veify&#xff09;&#xff0c;然…

elasticsearch重置密码

0 案例背景 Elasticsearch三台集群环境&#xff0c;对外端口为6200&#xff0c;忘记elasticsearch密码&#xff0c;进行重置操作 注&#xff1a;若无特殊说明&#xff0c;三台服务器均需进行处理操作 1 停止es /rpa/bin/elasticsearch.sh stop 检查状态 ps -ef|grep elast…

【Linux】ss 命令使用详解

目录 一、ss命令介绍 二、ss命令格式和使用 1、命令格式 2、ss命令的常用选项 3、命令的常见用法 3.1 找出打开套接字/端口应用程序 3.2 检查系统的监听套接字 3.3 显示所有状态为established的SMTP连接 3.4 查看建立的 TCP 连接 3.5 通过 -r 选项解析 IP 和端口号 …

UI(三)布局

文章目录 1、Colum和Row——垂直方向容器和水平方向容器2、ColumnSplit和RowSplit——子组件之间插入一条分割线3、Flex——弹性布局子组件的容器4、Grid和GridItem——网格容器和网格容器单元格5、GridRow和GridCol——栅格容器组件和栅格子组件6、List、ListItem、ListItemGr…

Mac OS 安装frida

安装frida和frida-tools Python是基础&#xff0c;提前装好Python 终端执行 python3 -m pip install frida 如果出现error 按照提示处理 信息提示&#xff1a;brew install pipx 于是终端执行&#xff1a; brew install pipx 安装frida&#xff1a; pipx install frida…

华宽通中标长沙市政务共性能力建设项目,助力智慧政务建设新飞跃

在数字化浪潮的推动下&#xff0c;长沙市政府正积极拥抱智慧城市建设&#xff0c;以科技力量提升政务服务效能。华宽通凭借其卓越的技术实力与丰富的项目经验&#xff0c;成功中标长沙市政务共性能力建设项目&#xff0c;这无疑是对华宽通在智慧城市领域实力的高度认可。 华宽…

Mind+在线图形编程软件(Sractch类软件)

Scratch作为图形编程软件&#xff0c;可以为小朋友学习编程提供很好的入门&#xff0c;是初次接触编程的小朋友的首选开发软件。这里介绍的Mind软件与Sractch用法几乎完全一致&#xff0c;并且可以提供在线免安装版本使用&#xff0c;浏览器直接打开网址&#xff1a; ide.mindp…

纳米硅(SiNP)可用于制造锂离子电池 纳米硅粉为其代表产品

纳米硅&#xff08;SiNP&#xff09;可用于制造锂离子电池 纳米硅粉为其代表产品 纳米硅&#xff08;SiNP&#xff09;指尺寸在纳米尺度范围内的硅颗粒。纳米硅具有光吸收谱宽、表面活性高、比表面积大、机械强度高、电学性能好等优势&#xff0c;在石油化工、建筑工程、电子电…

TypeError: %c requires int or char

踩坑&#xff1a;在用python写脚本查询sql数据时&#xff0c;使用%s来替换sql语句里的变量&#xff0c;结果一直报&#xff0c;而其他sql使用同样的方法正常&#xff0c;最后发现是因为sql语句里有模糊查询 like "%测试%"&#xff0c;这里的%被误以为%s&#xff0c;解…

PostgreSQL安装教程及文件介绍

Ubuntu 安装和配置 PostgreSQL 以 Ubuntu Server 20.04&#xff0c;PostgreSQL 12 版本为例。 1. 安装 使用如下命令&#xff0c;安装指定版本的 PostgreSQL sudo apt install postgresql-12在 Ubuntu 20.04 中安装 PostgreSQL 登录您的 Ubuntu 系统并使用以下 apt 命令更新…