首页 > 系统相关 >Linux进程间通信(IPC)的所有常见方法

Linux进程间通信(IPC)的所有常见方法

时间:2024-11-13 22:44:07浏览次数:3  
标签:IPC 队列 int 间通信 信号量 消息 Linux sem include

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

相关文章

  • Linux连接SFTP报ssh_exchange_identification: read: Connection reset by peer
    连接SFTP报ssh_exchange_identification:read:Connectionresetbypeer“Connectionresetbypeer”是一个常见的网络错误消息,通常出现在使用TCP协议进行网络通信时。这个错误表明在尝试读写数据时,远程主机(peer)强制关闭了连接。以下是一些可能导致此错误的原因及解决......
  • 泷羽sec专题课笔记-- Windows--补充Telnet连接Linux
    本笔记为泷羽sec《红队全栈课程》学习笔记,课程请可自行前往B站学习,课程/笔记主要涉及网络安全相关知识、系统以及工具的介绍等,请使用该课程、本笔记以及课程和笔记中提及工具的读者,遵守网络安全相关法律法规,切勿进行违法违规违纪的操作。写在最前面的话,我们为什么要学习......
  • Linux12位权限管理体
    1.Linux12位权限管理体1.1权限管理概述Linux通过rwx3种权限控制系统与保护系统,组成9位权限.Linux权限体系中还有3位特殊权限,组合起来就是12位权限体系.Linux这简单的rwx控制整个Linux系统的安全,权限与用户共同组成Linux系统的安全防护体系.1.2Linux权限计算2.0rwx......
  • Linux:进程概念(下)
    文章目录前言一、冯诺依曼体系二、操作系统(OperatorSystem)2.1.操作系统的概念2.2系统调⽤和库函数概念三.进程3.1基本概念3.1.1描述进程3.1.2task_struct3.2查看进程3.2.1getpid3.2.2proc3.2.3getppid总结前言•课本概念:程序的⼀个执⾏实例,正在执......
  • Linux基础笔试练习题笔记(1)
    Linux系统中建立一个新文件可以使用的命令为?A.chmodB.moreC.cpD.touch答案解析:chmod命令是控制用户对文件的权限的命令;more命令类似cat,不过会以一页一页的形式显示,更方便使用者逐页阅读;cp(copyfile)命令主要用于复制文件或目录;touch命令用于修改文件或者目录的时间......
  • [ Linux 命令基础 ] Linux 命令大全-命令前置知识-系统管理-文件和目录管理-文本处理
    ......
  • vscode远程连接linux调试GUI程序
    参考:https://zhuanlan.zhihu.com/p/385276301其中遇到的坑:(是否必须这样值得研究,这是多次尝试成功的结果)安装VcXsrv启动时设置DISPLAY为10后面把禁止访问控制勾上VSCODE的SSH连接配置里设置:Host192.168.1.233HostName192.168.1.233UserhaosouForwardX11yes......
  • 管道符 (|) 是 Unix/Linux 系统中用于将一个命令的输出传递给另一个命令作为输入的符
    一、管道符管道符(|)是Unix/Linux 系统中用于将一个命令的输出传递给另一个命令作为输入的符号。它是命令行和shell脚本中非常常用的工具,允许你将多个命令链接在一起,形成数据处理管道。1、管道符的用法最基本的用法是将一个命令的输出传递给另一个命令。例如,以下命令将l......
  • linux 命令值xargs与tr
    管道符 | 允许将一个命令的输出作为另一个命令的输入。然而,管道符在处理某些类型的输入时存在局限性,特别是当需要将一系列输入项作为单个命令的多个参数时()。xargs 的出现正是为了解决这个问题。xargs 能够从标准输入(stdin)读取数据,并将这些数据转换为特定命令的参数列表。这......
  • Web服务器(Linux)
    www简介Web网络服务也叫WWW(WorldWideWeb全球信息广播)万维网服务,一般是指能够让用户通过浏览器访问到互联网中文档等资源的服务Web网络服务是一种被动访问的服务程序,即只有接收到互联网中其他主机发出的请求后才会响应,最终用于提供服务程序的Web服务器会通过HTTP(超......