34、UNIX系统中的进程间通信与网络编程

UNIX系统中的进程间通信与网络编程

1. 进程间通信(IPC)

在UNIX系统中,进程间通信是一个重要的话题,下面将介绍几种常见的IPC机制。

1.1 消息队列

消息队列允许进程之间以消息的形式交换数据。服务器创建一个新的消息队列,任何人都可以对其进行读写操作。为了确保系统中没有其他进程使用相同的键值,我们使用 IPC_EXCL 。示例代码如下:

exit(0); 
} 
% msq-srvr & 
% msq-clnt < /etc/motd 

服务器创建消息队列后,会从队列中接收消息。类型为1的消息是数据,会被打印到标准输出。由于消息队列没有文件结束的概念,我们使用类型为2的消息来告知服务器没有更多数据。客户端则获取消息队列的标识符,从标准输入读取数据,并以类型为1的消息发送,最后发送一个类型为2的消息表示数据发送完毕。

1.2 共享内存

共享内存允许两个或多个进程共享一个内存区域,它们可以检查和修改该区域的内容。但在使用共享内存之前,进程需要获取其队列标识符,这可以通过 shmget 函数实现:

#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/shm.h> 

int shmget(key_t key, int size, int shmflg); 
  • size 参数指定所需内存段的大小(以字节为单位)。
  • key 参数指定该内存段使用的键值,可以是 IPC_PRIVATE (总是创建一个新的段)或非零值。
  • shmflg 参数用于指定内存段的读写权限,与 open creat 函数类似。

成功完成后, shmget 函数返回共享内存段的标识符;如果段不存在或无法创建,则返回 -1,并通过 errno 描述错误信息。

shmctl 函数可以对共享内存段执行多种控制操作:

#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/shm.h> 

int shmctl(int shmid, int cmd, struct shmid_ds *buf); 

shmid 参数是共享内存段的标识符, buf 参数指向一个 struct shmid_ds 类型的结构,该结构描述了内存段的信息:

struct shmid_ds { 
    struct ipc_perm     shm_perm; 
    int                 shm_segsz; 
    struct anon_map    *shm_amp; 
    ushort              shm_lkcnt; 
    pid_t               shm_lpid; 
    pid_t               shm_cpid; 
    ulong               shm_nattch; 
    ulong               shm_cnattch; 
    time_t              shm_atime; 
    long                shm_pad1; 
    time_t              shm_dtime; 
    long                shm_pad2; 
    time_t              shm_ctime; 
    long                shm_pad3; 
    kcondvar_t          shm_cv; 
    char                shm_pad4[2]; 
    struct as          *shm_sptas; 
    long                shm_pad5[2]; 
}; 

shmctl 函数的 cmd 参数可以取以下值:
| 命令 | 描述 |
| ---- | ---- |
| IPC_STAT | 将 struct shmid_ds 结构的当前内容放入 buf 指向的区域。 |
| IPC_SET | 将 struct shmid_ds 结构的 shm_perm.uid shm_perm.gid shm_perm.mode 元素更改为 buf 指向区域中的值,此操作仅限于具有超级用户有效用户ID或等于 shm_perm.cuid shm_perm.uid 的进程。 |
| IPC_RMID | 从系统中移除指定的共享内存标识符,并销毁与之关联的内存段和数据结构,此命令只能由具有超级用户有效用户ID或等于 shm_perm.cuid shm_perm.uid 的进程执行。 |
| SHM_LOCK | 将指定的共享内存段锁定到内存中,只能由超级用户执行。 |
| SHM_UNLOCK | 解锁指定的共享内存段,只能由超级用户执行。 |

在进程使用共享内存段之前,需要通过 shmat 函数将其附加到进程的地址空间:

#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/shm.h> 

void *shmat(int shmid, void *shmaddr, int shmflg); 

当程序使用完共享内存段后,可以调用 shmdt 函数将其分离:

#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/shm.h> 

int shmdt(void *shmaddr); 
1.3 信号量

信号量不是用于在进程之间交换数据,而是作为计数器,用于在多个进程之间提供对共享数据对象的同步访问。为了获得对共享资源的访问权限,进程需要执行以下步骤:
1. 测试控制该资源访问的信号量的值。
2. 如果值大于零,进程可以使用该资源,并将信号量的值减1,表示正在使用一个单位的资源。
3. 如果信号量的值为零,进程将进入睡眠状态,直到信号量的值大于零,然后返回步骤1。

当进程使用完由信号量控制的共享资源后,信号量的值会加1。如果有进程在步骤3中被阻塞,其中一个进程将被唤醒。大多数信号量是二进制的,其初始值为1,但也可以使用任何正值,该值表示可供共享的资源单元数量。

在使用信号量集之前,进程需要通过 semget 函数获取其标识符:

#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/sem.h> 

int semget(key_t key, int nsems, int semflg); 

semctl 函数可以对信号量集执行多种控制操作:

#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/sem.h> 

int semctl(int semid, int semnum, int cmd, union semun arg); 

union semun { 
    int                 val; 
    struct semid_ds    *buf; 
    ushort             *array; 
}; 

semop 函数用于操作信号量:

#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/sem.h> 

int semop(int semid, struct sembuf *ops, size_t nops); 

struct sembuf { 
    ushort    sem_num; 
    short     sem_op; 
    short     sem_flg; 
}; 

sem_op 的不同取值代表不同的操作:
- 如果 sem_op 为正,其值会加到信号量的值上,对应于释放程序正在使用的共享资源。
- 如果 sem_op 为负,对应于程序想要获取由信号量控制的资源。
- 如果信号量的值大于或等于 sem_op 的绝对值(资源可用),则从信号量的值中减去 sem_op 的绝对值。
- 如果信号量的值小于 sem_op 的绝对值(资源不可用), semop 要么立即返回错误(如果 IPC_NOWAIT sem_flg 中指定),要么将进程置于睡眠状态,直到信号量的值大于或等于 sem_op 的绝对值。
- 如果 sem_op 为零, semop 会阻塞,直到信号量的值变为零(除非 IPC_NOWAIT sem_flg 中指定)。

2. 网络编程概念

如今,几乎每个UNIX系统都连接到某种网络。下面介绍一些网络编程的基本概念。

2.1 网络协议

目前,事实上的标准网络协议套件是TCP/IP(传输控制协议/互联网协议),它由互联网工程任务组开发,被全球连接到互联网的主机广泛使用。另一个国际标准协议套件是OSI(开放系统互连),由国际标准化组织(ISO)标准化,虽然在欧洲比较流行,但由于技术和政治等多种原因,在美国并未得到广泛应用。

2.2 主机名和地址

为了在主机之间进行通信,需要指定要通信的主机。人类使用主机名,而程序使用主机地址。

主机名用于区分网络中的每个主机。在私有网络中,主机名可以很简单,如 “fred” 或 “wilma”;但在互联网上,主机名必须是完全限定的域名,如 “fred.some.college.edu” 或 “wilma.company.com”。

互联网域名系统将主机名空间划分为多个逻辑区域或域,这样做有两个主要原因:
- 允许主机名空间的管理分散化,每个组织可以管理自己的名称空间。
- 允许在不同的名称空间区域中重用主机名。

顶级域包括国家的双字母域名,如 “us”(美国)、“se”(瑞典)和 “mx”(墨西哥)。在美国,还有四个其他顶级域:“edu”(教育机构)、“mil”(军事组织)、“gov”(非军事政府组织)和 “com”(商业组织)。

每个顶级域可以进一步细分,例如 “edu” 域可以分为各个学院或大学的域,这些域还可以继续细分,最后细分到主机名。在同一个域内的主机可以使用简单的主机名,但从其他域的主机访问时,必须使用完全限定的域名。

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B{信号量值 > 0?}:::decision
    B -->|是| C(使用资源,信号量值减1):::process
    B -->|否| D(进程睡眠):::process
    C --> E{是否使用完资源?}:::decision
    E -->|是| F(信号量值加1):::process
    E -->|否| C
    F --> G([结束]):::startend
    D --> H{信号量值 > 0?}:::decision
    H -->|是| B
    H -->|否| D
2.3 套接字接口

在UNIX系统中,套接字接口是实现网络通信的重要手段。之前在进程间通信中介绍过用于同一机器上进程通信的UNIX域套接字,这里将聚焦于用于不同机器上进程通信的Internet域套接字。

所有使用套接字库函数的程序,在Solaris 2.x系统上必须与 -lnsl -lsocket 库链接,在IRIX 5.x系统上则需与 -lnsl 库链接。

2.3.1 套接字编程流程

一般而言,套接字编程主要包含以下步骤,以下以TCP套接字为例:
1. 创建套接字 :使用 socket 函数创建一个套接字描述符。

#include <sys/socket.h>
#include <netinet/in.h>

int socket(int domain, int type, int protocol);
- `domain`:指定协议族,如 `AF_INET` 表示IPv4协议。
- `type`:指定套接字类型,如 `SOCK_STREAM` 表示TCP套接字,`SOCK_DGRAM` 表示UDP套接字。
- `protocol`:通常为0,表示使用默认协议。
  1. 绑定地址 (服务器端):使用 bind 函数将套接字与特定的IP地址和端口号绑定。
#include <sys/socket.h>
#include <netinet/in.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- `sockfd`:套接字描述符。
- `addr`:指向要绑定的地址结构的指针。
- `addrlen`:地址结构的长度。
  1. 监听连接 (服务器端):使用 listen 函数将套接字设置为监听状态,准备接受客户端的连接请求。
#include <sys/socket.h>

int listen(int sockfd, int backlog);
- `sockfd`:套接字描述符。
- `backlog`:指定允许的最大连接请求队列长度。
  1. 接受连接 (服务器端):使用 accept 函数接受客户端的连接请求,并返回一个新的套接字描述符用于与客户端通信。
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- `sockfd`:监听套接字描述符。
- `addr`:指向客户端地址结构的指针。
- `addrlen`:指向客户端地址结构长度变量的指针。
  1. 发起连接 (客户端):使用 connect 函数向服务器发起连接请求。
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- `sockfd`:套接字描述符。
- `addr`:指向服务器地址结构的指针。
- `addrlen`:地址结构的长度。
  1. 数据传输 :使用 send recv 函数在客户端和服务器之间进行数据传输。
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
- `sockfd`:套接字描述符。
- `buf`:指向要发送或接收数据的缓冲区的指针。
- `len`:要发送或接收的数据长度。
- `flags`:可选标志。
  1. 关闭套接字 :使用 close 函数关闭套接字,释放资源。
#include <unistd.h>

int close(int fd);
- `fd`:套接字描述符。
2.3.2 地址结构

在套接字编程中,常用的地址结构是 struct sockaddr_in ,用于表示IPv4地址:

#include <netinet/in.h>

struct sockaddr_in {
    short            sin_family;   // 协议族,通常为 AF_INET
    unsigned short   sin_port;     // 端口号
    struct in_addr   sin_addr;     // IP地址
    char             sin_zero[8];  // 填充字节,使结构与 struct sockaddr 大小相同
};

struct in_addr {
    unsigned long s_addr;  // 32位IPv4地址
};
3. 总结

在UNIX系统中,进程间通信和网络编程是非常重要的技术领域。进程间通信机制(如消息队列、共享内存和信号量)为同一计算机上的进程提供了多种数据交换和同步的方式。而网络编程则使得不同计算机上的进程能够进行通信,其中TCP/IP协议和套接字接口是实现网络通信的关键。

在实际应用中,我们需要根据具体的需求选择合适的进程间通信机制和网络编程方法。例如,对于简单的数据交换,消息队列可能是一个不错的选择;而对于需要高效共享数据的场景,共享内存则更为合适。在网络编程方面,TCP套接字适用于需要可靠传输的场景,而UDP套接字则更适合对实时性要求较高、对数据可靠性要求相对较低的场景。

技术类型 适用场景 优点 缺点
消息队列 进程间数据交换 简单易用,支持不同进程间通信 有一定的开销,不适合大量数据传输
共享内存 高效数据共享 速度快,适合大量数据共享 需要额外的同步机制
信号量 进程同步 提供同步访问,避免资源竞争 实现相对复杂
TCP套接字 可靠数据传输 保证数据可靠到达,按序传输 建立连接和断开连接开销大,实时性相对较差
UDP套接字 实时数据传输 开销小,实时性好 不保证数据可靠到达,可能会丢包
graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;

    A([开始]):::startend --> B(创建套接字):::process
    B --> C{服务器或客户端?}
    C -->|服务器| D(绑定地址):::process
    C -->|客户端| E(发起连接):::process
    D --> F(监听连接):::process
    F --> G(接受连接):::process
    G --> H(数据传输):::process
    E --> H
    H --> I(关闭套接字):::process
    I --> J([结束]):::startend

通过深入理解这些技术的原理和使用方法,我们可以更好地开发出高效、稳定的UNIX系统应用程序。

考虑柔性负荷的综合能源系统低碳经济优化调度【考虑碳交易机制】(Matlab代码实现)内容概要:本文围绕“考虑柔性负荷的综合能源系统低碳经济优化调度”展开,重点研究在碳交易机制下如何实现综合能源系统的低碳化经济性协同优化。通过构建包含风电、光伏、储能、柔性负荷等多种能源形式的系统模型,结合碳交易成本能源调度成本,提出优化调度策略,以降低碳排放并提升系统运行经济性。文中采用Matlab进行仿真代码实现,验证了所提模型在平衡能源供需、平抑可再生能源波动、引导柔性负荷参调度等方面的有效性,为低碳能源系统的设计运行提供了技术支撑。; 适合人群:具备一定电力系统、能源系统背景,熟悉Matlab编程,从事能源优化、低碳调度、综合能源系统等相关领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①研究碳交易机制对综合能源系统调度决策的影响;②实现柔性负荷在削峰填谷、促进可再生能源消纳中的作用;③掌握基于Matlab的能源系统建模优化求解方法;④为实际综合能源项目提供低碳经济调度方案参考。; 阅读建议:建议读者结合Matlab代码深入理解模型构建求解过程,重点关注目标函数设计、约束条件设置及碳交易成本的量化方式,可进一步扩展至多能互补、需求响应等场景进行二次开发仿真验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值