进程间通信

文章目录

  • 简介
  • 1. 管道(Pipe)
    • 匿名管道" | "
    • 创建匿名管道( pipe() )
      • 调用`pipe()`函数
      • 写入数据到管道
      • 从管道读取数据
      • 关闭管道
      • 用fork管理
        • 示例图
        • 站在文件描述符角度
        • 站在内核角度--管道本质
      • 本质
  • 2. 命名管道(Named Pipe)
    • 创建一个命名管道
    • client & server完成server端提供计算器服务,将结果反馈给client端
    • 在这里插入图片描述
  • System V共享内存
    • 查看共享内存
    • 创建共享内存
        • 1.创建共享内存
        • 2. 附加共享内存段
        • 3. 分离共享内存段
        • 4. 控制共享内存段
        • 示例 1
        • 示例 2
    • 释放共享内存
  • System V共享内存和管道对比
  • 4. System V消息队列
      • 创建消息队列:
      • 定义消息结构:
      • 发送消息:
      • 接收消息:
      • 删除消息队列:
      • 示例


简介

在Linux操作系统中,进程间通信(IPC,Inter-Process
Communication)是实现不同进程之间数据交换和协同工作的关键技术。本文将介绍Linux中常见的四种进程间通信方式:管道、命名管道、System V共享内存和System V消息队列,并分析它们的优点和缺点。

1. 管道(Pipe)

管道是Linux中最简单的IPC机制,它允许两个进程之间进行单向数据传输。管道通常用于父子进程之间的通信。

优点:

  • 简单易用:管道是Linux中最早的IPC机制,使用起来非常简单。
  • 高效:管道的数据传输是直接在内存中进行,避免了系统调用的开销。

缺点:

  • 半双工: 管道只能进行单向数据传输,不能进行双向通信。
  • 只能用于具有亲缘关系的进程:管道通常只能用于父子进程之间的通信,对于其他进程间通信场景不够灵活。

匿名管道" | "

who | wc -l		#who命令用于查看当前云服务器的登录用户(一行显示一个用户),wc -l用于统计当前的行数

进程间通信_第1张图片
为什么" | "称之为匿名管道呢?下述文章将为你解答

创建匿名管道( pipe() )

调用pipe()函数

int pipefd[2];  
int ret = pipe(pipefd);

//pipe()函数接受一个整数数组作为参数,该数组包含两个整数值,分别表示管道的读端和写端的文件描述符。

如果成功,pipe()函数返回0,否则返回-1并设置errno。

进程间通信_第2张图片

写入数据到管道

通过管道的写端(pipefd[1])写入数据。可以使用write()函数向管道写入数据。例如:

char buf[] = "Hello, world!";  
ssize_t n = write(pipefd[1], buf, sizeof(buf));

write()函数将数据写入管道,并返回实际写入的字节数。如果写入成功,返回值大于0;如果写入失败,返回-1并设置errno

从管道读取数据

通过管道的读端(pipefd[0])从管道读取数据。可以使用read()函数从管道读取数据。例如:

char buf[1024];  
ssize_t n = read(pipefd[0], buf, sizeof(buf));

read()函数从管道读取数据,并返回实际读取的字节数。如果读取成功,返回值大于0;如果读取失败或没有数据可读,返回0或-1并设置errno。

可以将读看做嘴巴(0),写看做笔(1)方便记忆

关闭管道

在不再需要管道时,需要关闭管道的读端和写端。可以使用close()函数关闭管道。例如:

close(pipefd[0]); // 关闭读端  
close(pipefd[1]); // 关闭写端

close()函数关闭文件描述符,释放相关资源。如果关闭成功,返回0;如果关闭失败,返回-1并设置errno。

用fork管理

以一段代码为例,父进程关闭读描述符,向管道文件中写入"--i am father--",子进程关闭写描述符,从管道文件中读取管道文件内容,打印在显示器上。

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

int main()
{
    int pipefd[2]={0};
    int n=pipe(pipefd);
    assert(n!=-1);
    (void)n;

    pid_t id=fork();
    assert(id>=0);
    if(id==0)
    {
        close(pipefd[1]);	//关闭写描述符
        char buffer[1024*8];
        while(true)
        {
            ssize_t s=read(pipefd[0],buffer,sizeof(buffer)-1);
            if(s>0)
            {
                buffer[s]=0;
                cout<<"child read father => "<<buffer<<endl;
            }
            else if(s==0)
            {
                cout<<"write quit"<<endl;
                break;
            }
        }
        exit(0);
    }

    close(pipefd[0]);	//关闭读描述符
    string massage="i am father";
    char send_buff[1024*8];
    while(true)
    {
        snprintf(send_buff,sizeof send_buff,"--%s--",massage.c_str());
        write(pipefd[1],send_buff,strlen(send_buff));
        sleep(5);
        break;
    }
    close(pipefd[1]);
    pid_t ret=waitpid(id,nullptr,0);
    cout<<"id: "<<id<<"ret "<<ret<<endl;
    assert(ret>0);
    (void)ret;
    return 0;
}

  • 运行结果
    进程间通信_第3张图片
示例图

进程间通信_第4张图片

站在文件描述符角度

进程间通信_第5张图片

站在内核角度–管道本质

进程间通信_第6张图片

本质

所以,看待管道,就如同看待文件一样!管道的使用和文件一致,迎合了“Linux一切皆文件思想”。

2. 命名管道(Named Pipe)

命名管道通过文件系统中的路径名来标识,允许任何进程访问,因此可以在非亲缘关系的进程之间进行通信。

优点:

  • 通用性:命名管道可以用于任何两个进程之间的通信,不受亲缘关系的限制。
  • 文件系统支持:命名管道通过文件系统来管理,可以利用文件系统的权限控制机制来保护通信的安全性。

缺点:

  • 相对复杂:使用命名管道需要更多的编程工作,包括创建、打开、读写等操作。
  • 可能受到文件系统限制:例如,文件系统可能不支持跨文件系统的命名管道通信。

匿名管道与命名管道的区别

  • 匿名管道由pipe函数创建并打开。
  • 命名管道由mkfifo函数创建,打开用open
  • FIFO(命名管道)与pipe(匿名管道)之间唯一的区别在它们创建与打开的方式 不同,一但这些工作完 成之后,它们具有相同的语义。

创建一个命名管道

  • 命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
$ mkfifo filename
  • 命名管道也可以从程序里创建,相关函数有:
#include   
#include   
#include   

int mkfifo(const char *filename,mode_t mode);

参数说明:

  • pathname:要创建的命名管道文件的路径和名称。
  • mode:管道的权限模式,类似于文件权限。可以使用八进制数表示,例如0666表示可读可写权限。

函数返回值:

  • 如果创建成功,则返回0。
  • 如果创建失败,则返回-1,并设置相应的错误码(errno)。

下面是一个简单的示例代码,演示如何使用mkfifo()函数创建命名管道,并进行读写操作:

#include   
#include   
#include   
#include   
#include   
#include   
  
int main() {  
    const char *fifo_path = "/tmp/my_fifo";  
    int ret;  
    char buffer[1024];  
    int bytes_written;  // 修改变量名以更准确地反映其功能
    int bytes_read;  
    int fd;  
  
    // 创建命名管道文件  
    ret = mkfifo(fifo_path, 0666);  
    if (ret != 0) {  
        perror("mkfifo error");  
        exit(EXIT_FAILURE);  
    }  
  
    // 打开管道文件进行写入操作  
    fd = open(fifo_path, O_WRONLY);  
    if (fd == -1) {  
        perror("open error");  
        exit(EXIT_FAILURE);  
    }  
  
    // 向管道中写入数据  
    strcpy(buffer, "Hello, world!");  
    bytes_written = write(fd, buffer, strlen(buffer) + 1);  
    if (bytes_written == -1) {  
        perror("write error");  
        exit(EXIT_FAILURE);  
    } else {  
        printf("Sent: %s\n", buffer);  // 打印发送的数据  
    }  
  
    // 关闭写入管道的文件描述符  
    close(fd);  
  
    // 打开管道文件进行读取操作  
    fd = open(fifo_path, O_RDONLY);  
    if (fd == -1) {  
        perror("open error");  
        exit(EXIT_FAILURE);  
    }  
  
    // 从管道中读取数据并打印出来  
    memset(buffer, 0, sizeof(buffer));  // 清空缓冲区  
    bytes_read = read(fd, buffer, sizeof(buffer) - 1);  
    if (bytes_read == -1) {  
        perror("read error");  
        exit(EXIT_FAILURE);  
    } else if (bytes_read > 0) {  
        printf("Received: %s\n", buffer);  // 打印接收到的数据  
    } else {  
        printf("No data available\n");  
    }  
  
    // 关闭读取管道的文件描述符  
    close(fd);  
  
    unlink(fifo_path); // 删除该管道文件  

    return 0;  // 添加返回值,表明程序正常结束
}

既然可以读取和写入可以分开进行,那么就可以类比成client & server来进行通信

client & server完成server端提供计算器服务,将结果反馈给client端

server端进行管道文件创建,对需要服务的解析

#include"common.hpp"

int main()
{
    umask(0);
    if(mkfifo(ipcPATH.c_str(),MODE))
    {
        perror("mkfifo");
        return 1;
    }
    int fd=open(ipcPATH.c_str(),O_RDONLY);
    if(fd<0)
    {
        perror("open");
        return 2;
    }
    char buffer[SIZE];
    while(true)
    {
        buffer[0]='\0';
        ssize_t s=read(fd,buffer,sizeof(buffer)-1);
        if(s>0)
        {
            buffer[s]='\0';
            printf("server# %s\n",buffer);
            char* res="+-*/%";
            char* p=buffer;
            int flag=0;
            while(*p)
            {
                switch(*p)
                {
                case '+':
                    flag=0;
                    break;
                case '-':
                    flag=1;
                    break;
                case '*':
                    flag=2;
                    break;
                case '/':
                    flag=3;
                    break;
                case '%':
                    flag=4;
                    break;
                }
                p++;
            }
            char*num1=strtok(buffer,res);
            char*num2=strtok(NULL,res);
            int a=atoi(num1);
            int b=atoi(num2);
            int ret=0;
            switch (flag)
            {
            case 0:
                ret=a+b;
                break;
            case 1:
                ret=a-b;
                break;
            case 2:
                ret=a*b;
                break;
            case 3:
                ret=a/b;
                break;
            case 4:
                ret=a%b;
                break;
            }
            printf("%d %c %d = %d\n",a,res[flag],b,ret);
        }
        else if(s==0)
        {
            cout<<"client quit--->server quit"<<endl;
            break;
        }
        else
        {
            perror("server read");
            break;
        }
    }
    close(fd);
    return 0;
}

client端完成数据从外设的读取

#include"common.hpp"

int main()
{
    int fd=open(ipcPATH.c_str(),O_WRONLY);
    if(fd < 0)
    {
        perror("open");
        return 2;
    }
    char buffer[SIZE];
    while(true)
    {
        buffer[0]='\0';
        cout<<"Please Enter#";
        fflush(stdout);

        ssize_t s=read(0,buffer,sizeof(buffer)-1);
        if(s>0)
        {
            buffer[s-1]='\0';
            write(fd,buffer,strlen(buffer));
        }
    }
    close(fd);
    return 0;
}

common公共模块——头文件定义,文件创建路径,大小等宏的定义

#ifndef _COMM_H_
#define _COMM_H_

#include
#include
#include
#include
#include
#include
#include
#include

using namespace std;
#define MODE 0664
#define SIZE 128

string ipcPATH="./fifo.ipc";

#endif 

结果演示(完成上述效果实现)

进程间通信_第7张图片

System V共享内存

System V共享内存允许多个进程访问同一块物理内存区域,从而实现快速的数据共享和传输。

优点:

  • 高效率:多个进程可以同时访问共享内存区域,数据传输速度非常快。
  • 适用于大量数据传输:共享内存可以用于传输大量数据,而不会受到系统调用的开销限制。

缺点:

  • 需要同步机制:多个进程同时访问共享内存时,需要使用同步机制来避免数据冲突和竞争条件。这会增加编程的复杂性。
  • 系统资源消耗:创建和管理共享内存需要消耗一定的系统资源。如果使用不当,可能会导致系统资源紧张。

System V共享内存是一种进程间通信(IPC)机制,允许不同进程访问同一块内存区域,实现数据共享。在System V中,共享内存使用shmget()、shmat()、shmdt()和shmctl()等系统调用来实现。
进程间通信_第8张图片

查看共享内存

ipcs	#查看共享内存、消息队列和信号量

进程间通信_第9张图片

  • 共享内存信息:使用ipcs -m命令可以查看系统使用的IPC共享内存资源,包括共享内存的大小、数量和使用情况等信息。
  • 消息队列信息:使用ipcs -q命令可以查看系统使用的IPC消息队列资源,包括消息队列的名称、类型、访问权限和进程ID等信息。
  • 信号量信息:使用ipcs -s命令可以查看系统使用的IPC信号量资源,包括信号量的名称、类型、访问权限和进程ID等信息。
  • 系统IPC参数:使用ipcs -l命令可以查看系统IPC参数,包括共享内存限制、信号量限制和消息队列限制等信息。

创建共享内存

1.创建共享内存
int shmget(key_t key, size_t size, int flags);

参数:

  • key:指定共享内存段的标识符,通常使用一个整数或字符串。
  • size:指定共享内存段的大小。
  • flags:指定共享内存段的权限,如读、写、执行等。返回值:成功时返回共享内存段的标识符(一个非负整数),失败时返回-1。
  • 解析:shmget()函数用于创建一个新的共享内存段或获取一个已存在的共享内存段的标识符。它需要指定一个唯一的键值来标识共享内存段,并指定大小和权限。如果成功,返回的标识符可以用于后续的shmat()、shmdt()和shmctl()调用。
2. 附加共享内存段
void *shmat(int shmid, const void *shmaddr, int flags);

参数:

  • shmid:指定要附加的共享内存段的标识符。
  • shmaddr:指定要附加的地址,通常设置为NULL,表示由系统自动选择一个地址。
  • flags:指定附加选项,如是否可以睡眠等。返回值:成功时返回一个指向附加地址的指针,失败时返回(void *)-1。
  • 解析:shmat()函数用于将共享内存段附加到调用进程的地址空间中。它需要指定要附加的共享内存段的标识符、附加地址和附加选项。如果成功,返回的指针指向附加的地址,进程可以通过该指针访问共享内存段中的数据。
3. 分离共享内存段
int shmdt(const void *shmaddr);

参数:

  • shmaddr:指定要分离的地址,通常由shmat()函数返回。返回值:成功时返回0,失败时返回-1。
  • 解析:shmdt()函数用于将共享内存段从调用进程的地址空间中分离。它需要指定要分离的地址,通常由shmat()函数返回。如果成功,调用进程将无法再通过该地址访问共享内存段中的数据。
4. 控制共享内存段
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

参数:

  • shmid:指定要控制的共享内存段的标识符。
  • cmd:指定要执行的控制命令,如IPC_RMID表示删除共享内存段。
  • buf:指向一个结构体的指针,用于传递命令相关的参数或返回命令执行结果。返回值:成功时返回0,失败时返回-1。
  • 解析:shmctl()函数用于控制共享内存段的属性或执行特定的命令。它需要指定要控制的共享内存段的标识符、控制命令和相关的参数或结果结构体。通过执行不同的控制命令,可以实现共享内存段的创建、删除、获取属性等操作。
示例 1

创建一个共享内存段,将字符串写入共享内存,然后从中读取并最终删除共享内存段

#include 
#include 
#include 

int main() {
    key_t key = ftok("path/to/keyfile", 'R');
    int shm_id = shmget(key, 1024, IPC_CREAT | 0666);

    void *shm_ptr = shmat(shm_id, NULL, 0);

    // 使用共享内存
    sprintf((char*)shm_ptr, "Hello, shared memory!");

    printf("Data read from shared memory: %s\n", (char*)shm_ptr);

    shmdt(shm_ptr); // 分离共享内存

    // 如果不再需要共享内存,可以删除共享内存段
    shmctl(shm_id, IPC_RMID, NULL);

    return 0;
}
示例 2

创建一个简易版通讯,客户端发消息,服务端收消息并打印

server端实现创建共享内存,挂接共享内存,并读取外设信息,判断是否有“quit”,然后进行释放

#include "comm.h"
#include "log.hpp"

Init init;

int main()
{
    //1. 通过ftok算法获取唯一key值
    key_t k=ftok(PATH_NAME,PROJ_ID);
    if(k<0)
    {
        Log("创建key值失败",Error);
        return -1;
    }

    //2. 获取共享内存
    umask(0);
    int shm=shmget(k,SHM_SIZE,IPC_CREAT | IPC_EXCL | 0666);
    if(shm<0)
    {
        Log("获取共享内存失败",Error);
        return -2;
    }
    //可在终端用ipcs查看共享内存信息
    // -q:列出消息队列相关信息。
    // -m:列出共享内存相关信息。
    // -s:列出信号量相关信息
    
    //3. 挂接共享内存
    char* shmaddr=(char*)shmat(shm,nullptr,0);
    if(shmaddr==(void*)-1)
    {
        Log("挂接共享内存失败",Error);
        return -3;
    }

    // 将共享内存当成一个大字符串
    // char buffer[SHM_SIZE];
    // 结论1: 只要是通信双方使用shm,一方直接向共享内存中写入数据,另一方,就可以立马看到对方写入的数据。
    //         共享内存是所有进程间通信(IPC),速度最快的!不需要过多的拷贝!!(不需要将数据给操作系统)
    /*****一个问题是,服务端会一直读,就算没有client端写入,此时需要访问控制*******/
    // 结论2: 共享内存缺乏访问控制!会带来并发问题 【如果我想一定程度的访问控制呢? 能】(引入管道,管道天生具有)

    int fd=OpenFifo(FIFO_PATH,READ);

    Log("开始通信############",Info)<<std::endl;
    for(;;)
    {
        Wait(fd);

        printf("%s\n",shmaddr);
        if(strcmp("quit",shmaddr)==0)  break;
    }
    Log("结束通信############",Info)<<std::endl;

    //4. 去关联共享内存
    int n=shmdt(shmaddr);
    if(n<0)
    {
        Log("去关联共享内存失败",Error);
        return -4;
    }

    //sleep(5);
    //5. 删除共享内存
    int ctl=shmctl(shm,IPC_RMID,NULL);
    if(ctl<0)
    {
        Log("释放共享内存失败",Error);
        return -5;
    }

    CloseFifo(fd);
    return 0;
}

client端完成挂接共享内存并发送消息

#include "comm.h"
#include "log.hpp"

int main()
{
    //创建key值
    key_t k=ftok(PATH_NAME,PROJ_ID);
    assert(k>=0);
    Log("创建key成功",Info)<<std::endl;

    int shmid=shmget(k,SHM_SIZE,0);
    assert(shmid>=0);
    Log("创建共享内存id成功",Info)<<std::endl;

    char*shmaddr=(char*)shmat(shmid,nullptr,0);
    assert(shmaddr!=nullptr);
    Log("挂接共享内存成功",Info)<<std::endl;

    int fd=OpenFifo(FIFO_PATH,WRITE);
    for(;;)
    {
        ssize_t s = read(0, shmaddr, SHM_SIZE-1);
        if(s > 0)
        {
            shmaddr[s-1] = 0;
            Signal(fd); //只用来唤醒server端。当读取到的s>0时,调用此函数,唤醒server
            if(strcmp(shmaddr,"quit") == 0) break;
        }
    }

    int n=shmdt(shmaddr);
    assert(n>=0);
    Log("去关联共享内存成功",Info)<<std::endl;

    CloseFifo(fd);
    return 0;
}

comm中包含Log打印并实现部分函数

#pragma once

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "log.hpp"

#define PATH_NAME "/home/sun"
#define SHM_SIZE 4096
#define PROJ_ID 0x66

#define FIFO_PATH "./fifo"

class Init
{
public:
    Init()
    {
        umask(0);
        int n=mkfifo(FIFO_PATH,0666);
        assert(n == 0);
        (void)n;
        Log("创建命名管道成功",Info)<<std::endl;
    }
    ~Init()
    {
        unlink(FIFO_PATH);
        Log("去关联管道成功",Info)<<std::endl;
    }
};

#define READ O_RDONLY
#define WRITE O_WRONLY

int OpenFifo(std::string pathname,int flag)
{
    int fd=open(pathname.c_str(),flag);
    assert(fd>=0);
    Log("打开文件成功",Info)<<std::endl;
    return fd;
}

void Wait(int fd)
{
    Log("等待中.....",Info)<<std::endl;
    uint32_t temp=0;
    ssize_t s=read(fd,&temp,sizeof(uint32_t));
    assert(s==sizeof(uint32_t));
    (void)s;
}

void Signal(int fd)
{
    uint32_t temp=1;
    ssize_t s=write(fd,&temp,sizeof(uint32_t));
    assert(s==sizeof(uint32_t));
    (void)s;
    Log("唤醒中.....",Info)<<std::endl;

}

void CloseFifo(int fd)
{
    close(fd);
}

然后就开始通信

进程间通信_第10张图片

释放共享内存

以上述案例为例子,如果我们向终端发送kill -2信号(ctrl+c)那么就会出现下述问题
进程间通信_第11张图片
进程结束,但是创建的共享内存未释放
这是一个很严重后果的问题,代码中我们使用的是如果收到client端发送的quit及退出
进程间通信_第12张图片
但是难免会有进程退出,共享内存未正常释放的情况(kill -2、意外断连接…)我们再去创建是创建不了的,只能先用ipcrm -m +shmid来释放共享内存
进程间通信_第13张图片

System V共享内存和管道对比

System V共享内存和管道是两种不同的进程间通信方式,它们在实现方式、通信速度和适用场景上有所不同。

实现方式:

  • System V共享内存:通过调用系统接口(如shmget、shmat等)来创建和管理共享内存段。
  • 管道:通过创建一个管道文件,并使用read和write系统接口进行通信。 通信速度:

System V共享内存:所有进程间通信方式中最快的一种,因为共享内存不需要进行多次的拷贝操作,只需要将数据从输入文件复制到共享内存,再从共享内存复制到输出文件。
管道:管道通信需要进行四次拷贝操作,服务端将信息从输入文件复制到服务端的临时缓冲区中,然后将服务端临时缓冲区的信息复制到管道中,客户端将信息从管道复制到客户端的缓冲区,最后将客户端临时缓冲区的信息复制到输出文件中。

适用场景:

  • System V共享内存:适用于需要大量数据传输的进程间通信场景,因为共享内存可以避免多次的拷贝操作,提高通信效率。
  • 管道:适用于需要小量数据传输的进程间通信场景,因为管道的创建和维护相对简单,且不需要额外的内存开销。

V共享内存和管道的对比:
进程间通信_第14张图片
进程间通信_第15张图片

-- System V共享内存  
+----------------+     +----------------+     +----------------+  
| 输入文件       | --> | 共享内存       | --> | 输出文件       |  
+----------------+     +----------------+     +----------------+  
    2次拷贝        2次拷贝                 2次拷贝  
  

-- 管道  
+----------------+     +----------------+     +----------------+  
| 服务端临时缓冲区 | --> | 管道           | --> | 客户端临时缓冲区 |  
+----------------+     +----------------+     +----------------+  
     1次拷贝          2次拷贝              1次拷贝

这里的“拷贝”指的是将数据从一处复制到另一处的操作。因此,共享内存只需要进行两次拷贝操作,而管道需要进行四次拷贝操作。这导致了System V共享内存的通信速度比管道更快。

4. System V消息队列

System V消息队列允许进程之间发送和接收消息。每个消息都有一个类型和一个长度。

优点:

  • 消息传递:消息队列可以实现进程之间的消息传递,适用于需要异步通信的场景。
  • 消息类型和长度控制:消息队列提供了消息类型和长度的控制机制,可以确保消息的完整性和准确性。

缺点:

  • 相对复杂:使用消息队列需要更多的编程工作,包括创建、发送、接收等操作。
  • 系统资源消耗:创建和管理消息队列需要消耗一定的系统资源。如果使用不当,可能会导致系统资源紧张。

进程间通信_第16张图片

创建消息队列:

使用 msgget 函数创建消息队列,该函数返回一个消息队列标识符。

#include 
#include 
#include 

key_t key = ftok("path/to/keyfile", 'R');
int msg_id = msgget(key, IPC_CREAT | 0666);

定义消息结构:

定义一个结构体,用于存储消息的内容。

#include 

struct msg_buffer {
    long msg_type;
    char msg_text[256];
};

发送消息:

使用 msgsnd 函数向消息队列发送消息。

struct msg_buffer message;
message.msg_type = 1;
strcpy(message.msg_text, "Hello, message queue!");

msgsnd(msg_id, &message, sizeof(message), 0);

接收消息:

使用 msgrcv 函数从消息队列接收消息。

struct msg_buffer received_message;

msgrcv(msg_id, &received_message, sizeof(received_message), 1, 0);

printf("Received message: %s\n", received_message.msg_text);

删除消息队列:

当不再需要消息队列时,使用 msgctl 函数删除它。

msgctl(msg_id, IPC_RMID, NULL);

示例

创建一个消息队列,向队列发送一条消息,然后从队列中接收消息并打印出来。最后,删除消息队列。

#include 
#include 
#include 
#include 
#include 

struct msg_buffer {
    long msg_type;
    char msg_text[256];
};

int main() {
    key_t key = ftok("path/to/keyfile", 'R');
    int msg_id = msgget(key, IPC_CREAT | 0666);

    struct msg_buffer message;
    message.msg_type = 1;
    strcpy(message.msg_text, "Hello, message queue!");

    // 发送消息
    msgsnd(msg_id, &message, sizeof(message), 0);

    // 接收消息
    struct msg_buffer received_message;
    msgrcv(msg_id, &received_message, sizeof(received_message), 1, 0);

    printf("Received message: %s\n", received_message.msg_text);

    // 删除消息队列
    msgctl(msg_id, IPC_RMID, NULL);

    return 0;
}

你可能感兴趣的:(linux,开发语言,c++,c语言)