IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 《UNIX环境高级编程》笔记 第十七章-高级进程间通信 -> 正文阅读

[系统运维]《UNIX环境高级编程》笔记 第十七章-高级进程间通信

1. UNIX域套接字

1.1 概念

这种形式的IPC可以在同一计算机系统上运行的两个进程之间传送打开文件描述符

  • UNIX域套接字用于在同一台计算机上运行的进程之间的通信。这里要和第十六章介绍的内容区别,第十六章介绍的是因特网域套接字,而这里介绍UNIX域套接字,即通信域不同。因特网域主要应用于不同主机进程间通信,UNIX域只能应用于同一台主机上进程间的通信。

  • 虽然因特网域套接字也可用于同一计算机进程间通信,但是UNIX域套接字效率更高。因为UNIX域套接字不需要TCP/IP网络协议栈处理,UNIX域套接字仅仅是复制数据,不执行协议处理,不需要添加或删除网络报头,无需计算校验和,不产生顺序号,无需发送确认报文。

  • UNIX域套接字提供基于字节流和基于数据报的两种接口。UNIX域套接字的数据报服务是可靠的,即不会丢失报文,不会传递出错

  • UNIX域套接字就像是套接字和管道的混合。可以使用因特网域套接字接口(十六章介绍的bind、connect等函数),或者使用socketpair函数来创建一对无命名的、相互连接的UNIX域套接字。

1.2 无命名UNIX域套接字(socketpair函数)

使用socketpair函数来创建一对无命名的、相互连接的UNIX域套接字。类比于pipe函数

因为套接字是匿名的,因此只能用于相关进程间通信

int socketpair(int domain, int type, int protocol, int sv[2]);
  • 参数domain:通信域,在Linux下只能为AF_LOCAL或者AF_UNIX
  • 参数type:套接字类型,既可以是SOCK_STREAM,又可以是SOCK_DGRAM,当参数指定为SOCK_STREAM时,得到的结果称为流管道,它与一般管道的区别是流管道是全双工的,即两个描述符既可读又可写
  • 参数protocol:协议类型,只能是0
  • 参数cv[2]:保存分配的两个UNIX域套接字

由该函数创建的一对未命名的UNIX域套接字,可以起到全双工管道的作用:两端都对读和写开放

在这里插入图片描述

其实有些系统使用UNIX域套接字实现匿名管道:关闭第一描述符的写端和第二描述符的读端。

1.3 命名UNIX域套接字

未命名套接字由于没有名字,因此只能用于有关进程间通信,不能用于无关进程间通信

上一章中介绍了bind函数可以将一个地址绑定到一个因特网域套接字上。对于命名UNIX域套接字,也可以使用bind函数将其绑定到一个路径上,但是注意,命名UNIX域套接字绑定的地址格式不同于因特网域套接字

命名UNIX域套接字的绑定的地址格式由sockaddr_un结构表示,该结构体指针在传递给bind函数时也要转换为sockaddr指针类型。

struct sockaddr_un
{
    sa_family_t sun_family;		/* 地址族:AF_UNIX */
    char 		sun_path[108];	/* 要绑定的路径(文件)名  */
};

可以看出,sockaddr_un结构的sun_path成员包含一个路径(文件)名。当我们bind绑定成功时,系统会用该路径名创建一个套接字类型文件

  • 注意,该套接字文件仅用于向客户进程告示套接字名称。该文件无法打开,也不能由应用程序用于通信

  • 注意,如果我们试图绑定同一地址,若该文件已经存在,那么bind请求失败。当关闭套接字时,并不会自动删除该文件,所以必须确保在应用程序退出前,对该文件执行unlink操作。

2. 唯一连接

与因特网域套接字类似,服务器进程可以使用bind、listen、accept函数,为客户进程安排一个唯一UNIX域连接。客户进程使用connect与服务器进程连接。在服务器接受了connect请求后,在服务器进程和客户进程之间就存在了唯一连接。

在这里插入图片描述

具体的操作类似于因特网域套接字中TCP连接。

  • 服务器进程指定一个UNIX域套接字,通过bind绑定sockaddr_un地址,然后listen使之可以接受连接请求。连接到来时accept函数返回一个新的套接字用于与客户进程通信。

  • 客户进程通过一个UNIX域套接字connect至服务器进程,注意connect的目的sockaddr_un地址与服务器进程bind时的地址必须一致。注意,这里通过connect函数将该UNIX域套接字绑定到一个默认地址,但是并不会在文件系统创建套接字文件。

3. 传送文件描述符

在两个进程之间传送打开文件描述符的技术非常有用。我们可以设计一个open服务器,专门接收客户进程发来的请求,open服务器提供的唯一服务就是根据客户请求的文件路径和打开模式打开指定文件,并向客户进程发回文件描述符,客户进程进而执行各种I/O。(问题:如果客户进程没有某种文件访问权限但是服务器进程有,那么这种操作是否可以越过文件权限检查?)

当一个进程向另一个进程传送一个打开文件描述符时,会使得发送进程和接收进程共享同一文件表项

在这里插入图片描述

在技术上,我们是将指向一个打开文件表项的指针从一个进程发送到另一个进程。该指针被分配存放在接收进程的第一个可用描述符中(即发送进程和接收进程中的描述符编号不一定相同)

当发送进程将描述符传送给接收进程后,通常会close关闭该描述符。发送进程关闭该描述符并不会真的关闭该文件或设备,其原因是该描述符被视为由接收进程打开(即使接收进程尚未收到该描述符)

3.1 通过recvmsg、sendmsg函数接收/发送文件描述符

我们可以通过recvmsg、sendmsg函数实现在UNIX域套接字间传送文件描述符,具体是使用这两个函数的msghdr参数中的msg_control辅助数据字段

struct msghdr {
    void         *msg_name;       /* 可选地址 */
    socklen_t     msg_namelen;    /* 地址长度(字节) */
    struct iovec *msg_iov;        /* 缓冲区(iovec)数组 */
    size_t        msg_iovlen;     /* msg_iov元素个数 */
    void         *msg_control;    /* 辅助数据 */
    size_t        msg_controllen; /* 辅助数据长度 */
    int           msg_flags;      /* 标志(对于发送端该字段无用,仅对接收端有用) */
};

其中msg_control字段指向辅助数据(即为cmsghdr结构体,控制信息头),msg_controllen字段表示控制信息字节数

struct cmsghdr
  {
    size_t cmsg_len;	/* cmsg_data 中的数据长度加上 cmsghdr 结构的长度。 */
    int cmsg_level;		/* 应用的的协议层。  */
    int cmsg_type;		/* 控制信息类型  */
	...	/* cmsg_data:控制信息数据 */
  };

为了发送文件描述符:

  • cmsg_len设为cmsghdr结构长度+整型长度(文件描述符长度)

  • cmsg_level设为SOL_SOCKET

  • cmsg_type设为SCM_RIGHTS(表明正在传送访问权,SCM是Socket-level Control Message,即套接字级控制信息)

访问权只能通过UNIX域套接字传送,真正的控制信息(即描述符数据)跟在cmsg_type字段之后存储

可以通过以下宏定义来访问/管理cmsghdr结构体

//返回指向 cmsghdr 数据部分的指针
unsigned char *CMSG_DATA(struct cmsghdr *cmsg);
//返回指向与传递的 msghdr 关联的辅助数据缓冲区中第一个 cmsghdr 的指针。
struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh);
//返回传递的 cmsghdr 之后下一个有效的 cmsghdr。 当缓冲区中没有足够的空间时,它返回 NULL。
struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh, struct cmsghdr *cmsg);

//返回存储length长度的具体控制数据需要的cmsg_len字段值。它先将length加上cmsghdr结构体长度,然后按照处理器体系结构对齐要求进行调整。
size_t CMSG_LEN(size_t length);

可以通过辅助数据传送的其它控制信息:具体见UNIX网络编程14.6节

在这里插入图片描述

4. 补充:获得一个结构体中某一成员的偏移量

size_t offsetof(type, member);

type是结构体类型名,member是结构体成员名。返回该成员从结构体开始处的偏移量

offsetof的实现是一个宏

struct MyC{
    int a;
    int b;
    double c;
};
int main(int argc, char *argv[])
{
    cout << offsetof(MyC,a)<< endl;
    cout << offsetof(MyC,b)<< endl;
    cout << offsetof(MyC,c)<< endl;
}

5. 补充:以一致方式处理命令行参数 getopt函数

通过getopt函数及几个全局变量帮助开发者以一致的方式处理命令行选项。类似于python的optparse模块

int getopt(int argc, char * const argv[],const char *opstions);

extern char *optarg;
extern int optind, opterr, optopt;
  • 参数argc和argv分别代表参数个数和内容,跟main()函数的命令行参数是一样的。

  • 参数options说明哪些选项有参数,哪些选项没参数。如果选项字符串里的字母后接着冒号“:”,则表示还有相关的参数。例如一条命令的用法说明如下:

    command [-i] [-u username] [-z] filename
    

    则我们可以给getopt穿一个"iu:z"作为options字符串

函数getopt一般在循环体内,每次调用getopt处理一个命令行选项,循环直到getopt返回-1时退出。每次迭代中,getopt返回选项字符。如果处理的当前选项有参数,则全局变量optarg 会指向此额外参数。

  • 如果遇到无效选项,getopt()默认打印一条出错消息。如果不希望getopt()打印出错信息,则只要将全域变量opterr设为0即可。

  • optind全局变量用来存放下一个要处理的字符串在argv数组里的下表。它从1开始,每次getopt一次,该全局变量+1。

  • 当getopt出错,optopt全局变量会指向导致出错的选项字符串。

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章           查看所有文章
加:2021-11-19 17:59:21  更:2021-11-19 18:01:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 0:41:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码