IPC : Inter-Process Communication
前言:ipcs
命令是一个用于检查 Linux 系统中进程间通信(IPC)设施的状态的工具。它提供了关于系统上当前活跃的 IPC 资源的信息,包括消息队列、共享内存和信号量。
ipcs -a
这个选项会显示所有IPC资源的详细信息,包括每个资源的权限、所有者、创建者等。
一、管道
1、有名管道
写方写入Hello
#include <iostream>
#include<kk.h>
using namespace std;
int main(int argc,char *argv[])
{
// ./test p1
ARGC_CHECK(argc,2);
int fd=::open(argv[1],O_WRONLY);
string buf="Hello";
::write(fd,buf.c_str(),buf.length());
::close(fd);
return 0;
}
读方读出打印
#include <iostream>
#include<kk.h>
using namespace std;
int main(int argc,char *argv[])
{
ARGC_CHECK(argc,2);
int fd=::open(argv[1],O_RDONLY);
char buf[1024]={0};
::read(fd,buf,sizeof(buf));
cout<<"buf :"<<buf<<endl;
::close(fd);
return 0;
}
2、匿名管道
在Linux系统中,pipe
系统调用创建的管道是单向的,通常用于父子进程之间的通信。pipe
函数创建的管道包含两个文件描述符:fd[0]
和fd[1]
。其中:
fd[0]
是管道的读端(read end),用于读取数据。fd[1]
是管道的写端(write end),用于写入数据。
数据只能从写端(fd[1]
)写入,然后从读端(fd[0]
)读取。你不能从fd[0]
写入数据,也不能从fd[1]
读取数据。这是因为管道的设计就是单向的,确保数据只能按照一个方向流动。
#include <iostream>
#include<kk.h>
using namespace std;
int main(int argc,char *argv[])
{
int fds[2];
::pipe(fds);
if(fork()==0){
//子进程
close(fds[1]);
char buf[1024]={0};
::read(fds[0],buf,sizeof(buf));
cout<<"i am child,buf :"<<buf<<endl;
}
else{
close(fds[0]);
string buf="Hello";
::write(fds[1],buf.c_str(),buf.length());
}
}
二、共享内存
shmget
函数用于创建一个新的共享内存段或获取一个已存在的共享内存段的标识符。
int shmget(key_t key, size_t size, int shmflg);
key
:共享内存段的键值,可以使用ftok
函数生成。size
:共享内存段的大小。(4096的整数倍)shmflg
:共享内存段的权限标志,通常使用0666
(表示读写权限)。
shmat
函数用于将共享内存段附加到调用进程的地址空间。
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid
:共享内存段的标识符,由shmget
函数返回。shmaddr
:指定共享内存段附加到进程地址空间的位置,通常设置为NULL
。shmflg
:附加选项,通常设置为0
。
示例:
进程1在共享内存段写入hello
int shmid=shmget(0x1234,4096,IPC_CREAT|0600);
char *p=(char *)shmat(shmid,NULL,0);
memcpy(p,"hello",5);
进程2读出共享内存段内容
int shmid=shmget(0x1234,4096,IPC_CREAT|0600);
char *p=(char *)shmat(shmid,NULL,0);
std::cout<<p<<std::endl;
三、Socket套接字
进程间双人聊天程序示例:
server.cc
#include <iostream>
#include<kk.h>
using namespace std;
int main(int argc,char *argv[])
{
// ./server 192.168.80.132 1234
ARGC_CHECK(argc,3);
int sockfd=socket(AF_INET,SOCK_STREAM,0);
//端口复用
int reuse=1;
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));
//绑定端口和ip
struct sockaddr_in serverAdd;
serverAdd.sin_family=AF_INET;
serverAdd.sin_port=htons(atoi(argv[2]));
serverAdd.sin_addr.s_addr=inet_addr(argv[1]);
bind(sockfd,(struct sockaddr*)&serverAdd,sizeof(serverAdd));
//监听
listen(sockfd,50);
int netfd=accept(sockfd,nullptr,nullptr);
cout<<"a client connected!"<<endl;
//io多路复用
int epfd=epoll_create(1);
struct epoll_event events;
events.data.fd=STDIN_FILENO;
events.events=EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&events);
events.data.fd=netfd;
events.events=EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,netfd,&events);
char buf[1024]={0};
while(1){
struct epoll_event readySet[2];
int readyNum=epoll_wait(epfd,readySet,2,-1);
for(int i=0;i<readyNum;i++){
if(readySet[i].data.fd==STDIN_FILENO){
bzero(buf,sizeof(buf));
read(STDIN_FILENO,buf,sizeof(buf));
send(netfd,buf,strlen(buf),0);
}
if(readySet[i].data.fd==netfd){
bzero(buf,sizeof(buf));
int ret=recv(netfd,buf,sizeof(buf),0);
if(ret==0){
cout<<"buy!!!"<<endl;
return 0;
}
cout<<"buf="<<buf<<endl;
}
}
}
return 0;
}
client.cc
#include <iostream>
#include<kk.h>
using namespace std;
int main(int argc,char *argv[])
{
// ./server 192.168.80.132 1234
ARGC_CHECK(argc,3);
int netfd=socket(AF_INET,SOCK_STREAM,0);
//绑定端口和ip
struct sockaddr_in serverAdd;
serverAdd.sin_family=AF_INET;
serverAdd.sin_port=htons(atoi(argv[2]));
serverAdd.sin_addr.s_addr=inet_addr(argv[1]);
connect(netfd,(struct sockaddr*)&serverAdd,sizeof(serverAdd));
//io多路复用
int epfd=epoll_create(1);
struct epoll_event events;
events.data.fd=STDIN_FILENO;
events.events=EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&events);
events.data.fd=netfd;
events.events=EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,netfd,&events);
char buf[1024]={0};
while(1){
struct epoll_event readySet[2];
int readyNum=epoll_wait(epfd,readySet,2,-1);
for(int i=0;i<readyNum;i++){
if(readySet[i].data.fd==STDIN_FILENO){
bzero(buf,sizeof(buf));
read(STDIN_FILENO,buf,sizeof(buf));
send(netfd,buf,strlen(buf),0);
}
if(readySet[i].data.fd==netfd){
bzero(buf,sizeof(buf));
int ret=recv(netfd,buf,sizeof(buf),0);
if(ret==0){
cout<<"buy!!!"<<endl;
return 0;
}
cout<<"buf="<<buf<<endl;
}
}
}
return 0;
}
四、信号
在 Linux 中,信号是一种软件中断,用于通知进程发生了某种事件。信号可以用于进程间通信(IPC)
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
// 子进程:等待信号
while(1) {
sleep(1);
}
} else {
// 父进程:发送信号
sleep(2); // 等待子进程运行
kill(pid, SIGINT); // 发送 SIGINT 信号
}
return 0;
}
五、信号量
信号量(Semaphore)是一种用于控制多个进程或线程对共享资源访问的同步机制。它是一个计数器,主要用于解决并发环境下的同步和互斥问题。
sem_open
函数用于打开或创建一个命名的POSIX信号量。
sem_t *sem = sem_open("/mysem", O_CREAT, 0644, 0);
name
:信号量的名称,它必须以斜杠('/')开头。oflag
:打开标志,可以是以下值的组合:O_CREAT
:如果信号量不存在,则创建它。O_EXCL
:与O_CREAT
一起使用,如果信号量已存在,则sem_open
调用失败。
mode
:当与O_CREAT
一起使用时,这个参数指定信号量的权限位(类似于open
函数的模式参数)。
sem_post
函数用于释放(增加计数)一个信号量。
int sem_post(sem_t *sem);
sem
:指向信号量对象的指针。
sem_close
函数用于关闭一个信号量的引用。
int sem_close(sem_t *sem);
sem
:指向信号量对象的指针。
sem_unlink
函数用于从系统中删除一个命名的POSIX信号量。
int sem_unlink(const char *name);
name
:信号量的名称。
示例:
进程1向进程2发送信号:
#include <semaphore.h>
#include <stdio.h>
#include <fcntl.h> // For O_* constants
#include <sys/stat.h> // For mode constants
#include <unistd.h>
int main() {
// 打开或创建命名信号量
sem_t *sem = sem_open("/my_semaphore", O_CREAT, 0644, 0);
if (sem == SEM_FAILED) {
perror("sem_open");
return 1;
}
// 执行一些工作...
printf("Producer is working...\n");
sleep(5);
// 发送信号量,表示工作完成
sem_post(sem);
// 关闭信号量
sem_close(sem);
// 删除命名信号量
sem_unlink("/my_semaphore");
return 0;
}
进程2阻塞等待进程1的信号
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>
int main() {
// 打开命名信号量
sem_t *sem = sem_open("/my_semaphore", 0);
if (sem == SEM_FAILED) {
perror("sem_open");
return 1;
}
// 等待信号量,直到生产者完成工作
sem_wait(sem);
// 执行一些工作...
printf("Consumer is working...\n");
// 关闭信号量
sem_close(sem);
return 0;
}
六、消息队列
消息队列是UNIX系统V(X/OPEN)规范中提供的进程间通信(IPC)机制之一。它允许不同进程通过一个队列来交换消息。每个进程都可以向队列中添加消息,也可以从队列中读取消息。消息队列具有FIFO(先进先出)的特性。
1. 包含必要的头文件
#include <sys/ipc.h>
#include <sys/msg.h>
2. 定义消息结构
消息队列中的消息必须以struct msgbuf
结构开始,该结构包含消息类型和消息体的长度。消息类型用于标识消息,而消息体则是实际要传递的数据。
struct msgbuf {
long mtype; /* 消息类型,必须是正数 */
char mtext[1]; /* 消息数据 */
};
3. 创建或访问消息队列
使用msgget
函数创建一个新的消息队列或访问一个已存在的消息队列。
int msgid = msgget(key_t key, int msgflg);
key
:消息队列的键值,可以使用ftok
函数生成。msgflg
:权限标志,类似于文件的权限设置。
4. 发送消息
使用msgsnd
函数向消息队列发送消息。
int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg);
msgid
:消息队列的标识符。msgp
:指向消息结构的指针。msgsz
:消息的长度。msgflg
:通常设置为0。
5. 接收消息
使用msgrcv
函数从消息队列接收消息。
int msgrcv(int msgid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgid
:消息队列的标识符。msgp
:指向接收消息的缓冲区的指针。msgsz
:消息的最大长度。msgtyp
:期望接收的消息类型。msgflg
:通常设置为0,或者IPC_NOWAIT
以非阻塞方式接收消息。
6. 删除消息队列
使用msgctl
函数删除消息队列。
int msgctl(int msgid, int cmd, struct msqid_ds *buf);
msgid
:消息队列的标识符。cmd
:命令,IPC_RMID
用于删除消息队列。buf
:指向消息队列控制块的指针(通常不需要)。
发送方示例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#define MSGSZ 256
struct msgbuf {
long mtype; /* 消息类型,必须是正数 */
char mtext[MSGSZ]; /* 消息数据 */
};
int main() {
key_t key;
int msgid;
struct msgbuf *msg;
// 生成键值
key = ftok("queuefile", 65);
// 创建消息队列
msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid == -1) {
perror("msgget error");
return 1;
}
// 准备发送的消息
msg = (struct msgbuf *)malloc(sizeof(struct msgbuf));
msg->mtype = 1; // 消息类型
strncpy(msg->mtext, "Hello from sender!", MSGSZ); // 使用strncpy以确保不会超出缓冲区
// 发送消息
if (msgsnd(msgid, msg, sizeof(msg->mtext), 0) == -1) {
perror("msgsnd error");
return 1;
}
// 清理
free(msg);
return 0;
}
接收方示例:
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#define MSGSZ 256
struct msgbuf {
long mtype; /* 消息类型,必须是正数 */
char mtext[MSGSZ]; /* 消息数据 */
};
int main() {
key_t key;
int msgid;
struct msgbuf *msg;
// 生成键值
key = ftok("queuefile", 65);
// 访问消息队列
msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid == -1) {
perror("msgget error");
return 1;
}
// 接收消息
msg = (struct msgbuf *)malloc(sizeof(struct msgbuf));
if (msgrcv(msgid, msg, sizeof(msg->mtext), 1, 0) == -1) {
perror("msgrcv error");
return 1;
}
// 打印接收到的消息
printf("Received: %s\n", msg->mtext);
// 清理
free(msg);
return 0;
}
标签:IPC,队列,int,间通信,信号量,消息,Linux,sem,include From: https://blog.csdn.net/weixin_73809064/article/details/143593867