进程间通信IPC(共享内存,消息队列,信号灯)和信号的具体实现(上)

简介: 进程间通信IPC(共享内存,消息队列,信号灯)和信号的具体实现

常用的进程间通信方式

• 传统的进程间通信方式

无名管道(pipe)、有名管道(fifo)和信号(signal)

• System V IPC对象

共享内存(share memory)、消息队列(message queue)和信号量(semaphore)

• BSD

套接字(socket)

当前目录下路径指定要加上“.”

ftok (“./app”, ‘i’)才可以

“./”才是当前路径,“/“根目录路径

IPC

IPC对象

IPC(Inter-Process Communication)进程间通信,提供了各种进程间通信的方法

ipcs、 ipcrm

  1. ipcs命令用于查看系统中的IPC对象

ipcs –m 共享内存

ipcs –s 信号量

ipcs –q 消息队列

  1. ipcrm命令用于删除系统中的IPC对象
    ipcrm –m id
    创建的IPC对象如果不删除的话会一直保留在系统中

共享内存

“./”才是当前路径,“/“根目录路径

共享内存(share memory)

  1. 共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
  2. 为了在多个进程间交换信息, 内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间
  3. 进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。
  4. 由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等
    从串口读取的zigbee网络环境数据要发送给web页面或者APP,必须满足:
  • 所有的其他进程都可以定时从”某块内存” 读取数据
  • 有新的数据更新时,可以很方便的将数据写入到” 某块内存”
  • 读取的数据不需要清除原有数据,写入的数据要更新到这块内存

共享内存实现

共享内存的使用包括如下步骤:

  1. 创建/打开共享内存
  2. 映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
  3. 撤销共享内存映射
  4. 删除共享内存对象

共享内存函数调用流程

共享内存函数

shmget

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, int size, int shmflg);

key:

IPC_PRIVATE 或 ftok的返回值

size:

共享内存区大小

shmflg:

同open函数的权限位,也可以用8进制表示法

返回值

成功:共享内存段标识符

出错: -1

shmat

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);

shmid:要映射的共享内存区标识符

shmaddr:将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射)

shmflg :

SHM_RDONLY:共享内存只读

默认0:共享内存可读写

返回值:

成功:映射后的地址

出错: -1

shmdt

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

shmaddr

:共享内存映射后的地址

返回值

成功: 0

出错:-1

shmctl

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

shmid:要操作的共享内存标识符

cmd : IPC_STAT (获取对象属性)

IPC_SET (设置对象属性)

IPC_RMID (删除对象)

buf : 指定IPC_STAT/IPC_SET时用以保存/设置属性

返回值

成功: 0

出错:-1

举例

Linux终端放大缩小:

放大:

ctrl+shift+=

缩小:

ctrl±

shm-server.c

/* shm_server.c : the time server using shared memory, a bizarre application  */
#include    <stdio.h>
#include    <sys/shm.h>
#include    <time.h>
#include        <stdlib.h>
#include        <unistd.h>
#include        <string.h>
#define    TIME_MEM_KEY    99    /* like a filename      */
#define    SEG_SIZE    ((size_t)100)    /* size of segment      */
main()
{
    long now;
    int n;
    key_t key_info;
    if ((key_info = ftok ("/app", 'i')) < 0)
    {
        perror ("ftok info");
        exit (-1);
    }
    /* create a shared memory segment */
    int seg_id = shmget(key_info, SEG_SIZE, IPC_CREAT | 0777);
    /* attach to it and get a pointer to where it attaches */
    char *mem_ptr = shmat(seg_id, NULL, 0);
    /* run for a minute */
    for (n = 0; n < 60; n++) 
    {
        time(&now);            /* get the time */
        strcpy(mem_ptr, ctime(&now));    /* write to mem */
        sleep(1);            /* wait a sec   */
    }
    shmctl(seg_id, IPC_RMID, NULL);
}

shm-client.c

/* shm_client.c : the time client using shared memory, a bizarre application  */
#include        <stdlib.h>
#include        <unistd.h>
#include    <stdio.h>
#include    <sys/shm.h>
#include    <time.h>
#define    TIME_MEM_KEY    99
#define    SEG_SIZE    ((size_t)100)
main()
{
    key_t key_info;
    if ((key_info = ftok ("/app", 'i')) < 0)
    {
        perror ("ftok info");
        exit (-1);
    }
    int seg_id = shmget(key_info, SEG_SIZE, 0777);
    char *mem_ptr = shmat(seg_id, NULL, 0);
    printf("The time, direct from memory: ..%s", mem_ptr);
    shmdt(mem_ptr);
}

share.c
#include <sys/ipc.h>
 #include <sys/shm.h>
#include <stdio.h>
#include <sys/stat.h>
 int shmctl(int shmid, int cmd, struct shmid_ds *buf);
int main()
{
    int  segment_id,segment_size;
    char *shared_memory;
    struct shmid_ds shmbuffer;
    const int shared_segment_size = 0x6400;
/*分配*/
    segment_id = shmget (IPC_PRIVATE, shared_segment_size, 
                      IPC_CREAT | IPC_EXCL | S_IRUSR | S_IWUSR ); 
    shared_memory = (char*) shmat (segment_id, 0, 0); 
    printf ("shared memory attached at address %p\n", shared_memory); 
    shmctl (segment_id, IPC_STAT, &shmbuffer); 
    segment_size = shmbuffer.shm_segsz; 
    printf ("segment size: %d\n", segment_size); 
    sprintf (shared_memory, "Hello, world."); 
    shmdt (shared_memory); 
/*绑定到共享内存块*/
    shared_memory = (char*) shmat (segment_id, (void*) 0x500000, 0); 
    printf ("shared memory reattached at address %p\n",shared_memory); 
    printf ("%s\n", shared_memory);
    /*tuo li*/
    shmdt (shared_memory); 
    /*释放内存*/
    shmctl (segment_id, IPC_RMID, 0); 
    return;
}

消息队列(message queue)

  • 消息队列是IPC对象的一种
  • 消息队列由消息队列ID来唯一标识
  • 消息队列就是一个消息的列表。 用户可以在消息队列中添加消息、 读取消息等。
  • 消息队列可以按照类型来发送/接收消息

消息队列函数调用流程

消息队列函数

msgget

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int flag);

key:和消息队列关联的key值

flag:消息队列的访问权限

返回值:

成功:消息队列ID

出错: -1

msgsnd

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t size,int flag);

msqid:消息队列的ID

msgp:指向消息的指针。常用消息结构msgbuf如下:

struct msgbuf
{
long mtype; //消息类型
char mtext[N];//消息正文
};

size:发送的消息正文的字节数

flag:

IPC_NOWAIT 消息没有发送完成函数也会立即返回。

0:直到发送完成函数才返回

返回值:

成功:0

出错: -1

msgrcv

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
 int msgrcv(int msgid,  void* msgp,  size_t  size,  long msgtype,  int  flag);

函数参数

msqid:消息队列的ID

msgp:接收消息的缓冲区

size:要接收的消息的字节数

msgtype:

0:接收消息队列中第一个消息。

大于0:接收消息队列中第一个类型为msgtyp的消息.

小于0:接收消息队列中类型值不小于msgtyp的绝对值且类型值又最小的消息。

flag:

0:若无消息函数会一直阻塞

IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG。

函数返回值

成功:接收到的消息的长度

出错:-1

msgctl

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );

函数参数

msqid:消息队列的队列ID

cmd:

IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中。

IPC_SET:设置消息队列的属性。这个值取自buf

IPC_RMID:从系统中删除消息队列。

buf:消息队列缓冲区函数

返回值 成功:0

出错:-1

举例

server

#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/stat.h>
#define QUEUE_MSG_LEN    256
#define PROJ_ID        'g'
#define PATH_NAME    "/app"
#define SERVER_MSG    1
#define CLIENT_MSG    2
/*message data structure */
struct msg 
{
    long type;
    long msgtype;
    unsigned char text[QUEUE_MSG_LEN];
};
int main(void)
{
    /*message data structure */
    struct msg msg_buf;
    int qid;
    int msglen;
    int i=0;
    /*get message queue */
    key_t msgkey;
    if ((msgkey = ftok(PATH_NAME, PROJ_ID)) == -1) 
    {
        perror("ftok error!\n");
        exit(1);
    }
    if ((qid = msgget(msgkey, IPC_CREAT | 0666)) == -1) 
    {
        perror("msgget error!\n");
        exit(1);
    }
    while (1) 
    {
        printf("server send: ");
        /*get string from terminal & fill up message data structure */
        fgets(msg_buf.text, QUEUE_MSG_LEN, stdin);
        if (strncmp("exit", msg_buf.text, 4) == 0) 
        {
            msgctl(qid, IPC_RMID, NULL);
            break;
        }
        msg_buf.text[strlen(msg_buf.text) - 1] = '\0';
        msg_buf.type = SERVER_MSG;
        msg_buf.msgtype = i++;
        /*send message to message queue with SERVER_MSG type */
        if (msgsnd(qid, &msg_buf, sizeof(struct msg) - sizeof(long), 0) == -1) 
        {
            perror("Server msgsnd error!\n");
            exit(1);
        }
#if 1
        /*receive message from message queue with CLIENT_MSG type */
        if (msgrcv(qid, &msg_buf, sizeof(struct msg) - sizeof(long), CLIENT_MSG, 0) == -1) 
        {
            perror("Server msgrcv error!\n");
            exit(1);
        }
        printf("server rcv: %d: %s\n",msg_buf.msgtype,msg_buf.text);
#endif        
    }
    exit(0);
}

client

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#define QUEUE_MSG_LEN    256
#define PROJ_ID        'g'
#define PATH_NAME    "/app"
#define SERVER_MSG    1
#define CLIENT_MSG    2
/*message data structure */
struct msg 
{
    long type;
    long msgtype;
    unsigned char text[QUEUE_MSG_LEN];
};
int main(void)
{
    int qid;
    int msglen;
    int i=0;
    struct msg msg_buf;
    /* get a message queue */
    key_t msgkey;
    if ((msgkey = ftok(PATH_NAME, PROJ_ID)) == -1) //得到ID,
    {
        perror("ftok error!\n");
        exit(1);
    }
    if ((qid = msgget(msgkey, IPC_CREAT | 0666)) == -1) 
    {
        perror("msgget error!\n");
        exit(1);
    }
    while (1) 
    {
        /*receive message from message queue with SERVER_MSG type */
        if (msgrcv(qid, &msg_buf, sizeof(struct msg) - sizeof(long), SERVER_MSG, 0) == -1) 
        {
            perror("Server msgrcv error!\n");
            exit(1);
        }
        printf("server rcv : %ld: %s\n",msg_buf.msgtype,msg_buf.text);    
#if 1
        printf("client send: ");
        /*get string from terminal & fill up message data structure */
        fgets(msg_buf.text, QUEUE_MSG_LEN, stdin);
        if (strncmp("exit", msg_buf.text, 4) == 0) 
        {
            break;
        }
        msg_buf.text[strlen(msg_buf.text) - 1] = '\0';
        msg_buf.type = CLIENT_MSG;
        msg_buf.msgtype = i++;
        /*send message to message queue with CLIENT_MSG type */
        if (msgsnd(qid, &msg_buf, strlen(msg_buf.text) + 1+4, 0) == -1) 
        {
            perror("client msgsnd error!\n");
            exit(1);
        }
        #endif
    }
    exit(0);
}

目录
相关文章
|
9月前
|
消息中间件 存储 网络协议
从零开始掌握进程间通信:管道、信号、消息队列、共享内存大揭秘
本文详细介绍了进程间通信(IPC)的六种主要方式:管道、信号、消息队列、共享内存、信号量和套接字。每种方式都有其特点和适用场景,如管道适用于父子进程间的通信,消息队列能传递结构化数据,共享内存提供高速数据交换,信号量用于同步控制,套接字支持跨网络通信。通过对比和分析,帮助读者理解并选择合适的IPC机制,以提高系统性能和可靠性。
1164 14
|
8月前
|
消息中间件 Linux
Linux中的System V通信标准--共享内存、消息队列以及信号量
希望本文能帮助您更好地理解和应用System V IPC机制,构建高效的Linux应用程序。
278 48
|
11月前
|
消息中间件 存储 供应链
进程间通信方式-----消息队列通信
【10月更文挑战第29天】消息队列通信是一种强大而灵活的进程间通信机制,它通过异步通信、解耦和缓冲等特性,为分布式系统和多进程应用提供了高效的通信方式。在实际应用中,需要根据具体的需求和场景,合理地选择和使用消息队列,以充分发挥其优势,同时注意其可能带来的复杂性和性能开销等问题。
|
9月前
|
消息中间件 Linux
Linux:进程间通信(共享内存详细讲解以及小项目使用和相关指令、消息队列、信号量)
通过上述讲解和代码示例,您可以理解和实现Linux系统中的进程间通信机制,包括共享内存、消息队列和信号量。这些机制在实际开发中非常重要,能够提高系统的并发处理能力和数据通信效率。希望本文能为您的学习和开发提供实用的指导和帮助。
620 20
|
消息中间件 C语言 RocketMQ
消息队列 MQ操作报错合集之出现"Connection reset by peer"的错误,该如何处理
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
消息中间件 Java C语言
消息队列 MQ使用问题之在使用C++客户端和GBase的ESQL进行编译时出现core dump,该怎么办
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
11月前
|
消息中间件 存储 Kafka
MQ 消息队列核心原理,12 条最全面总结!
本文总结了消息队列的12个核心原理,涵盖消息顺序性、ACK机制、持久化及高可用性等内容。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
消息中间件
手撸MQ消息队列——循环数组
队列是一种常用的数据结构,类似于栈,但采用先进先出(FIFO)的原则。生活中常见的排队场景就是队列的应用实例。在数据结构中,队列通常用数组实现,包括入队(队尾插入元素)和出队(队头移除元素)两种基本操作。本文介绍了如何用数组实现队列,包括定义数组长度、维护队头和队尾下标(front 和 tail),并通过取模运算解决下标越界问题。此外,还讨论了队列的空与满状态判断,以及并发和等待机制的实现。通过示例代码展示了队列的基本操作及优化方法,确保多线程环境下的正确性和高效性。
176 0
手撸MQ消息队列——循环数组
|
消息中间件 存储 缓存
一个用过消息队列的人,竟不知为何要用 MQ?
一个用过消息队列的人,竟不知为何要用 MQ?
371 1
|
消息中间件 开发工具 RocketMQ
消息队列 MQ使用问题之一直连接master失败,是什么原因
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。

热门文章

最新文章