linux下多线程间通信之信号量,消息队列的综合运用

本文详细介绍了Linux下多线程间使用信号量和消息队列进行通信的原理与实践,探讨了消息队列不能直接实现进程同步的问题,并展示了如何创建、操作消息队列,以及相关的系统调用。同时,文章通过实例代码展示了如何在多线程程序中应用这些通信机制。
摘要由CSDN通过智能技术生成

一、消息队列介绍
消息队列是消息的链接表,存放在内核中。其实质上就是一个内核链表,消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。Linux用宏MSGMAX和MSGMNB来限制一条消息的最大长度和一个队列的最大长度。对消息队列有写权限的进程可以按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读出消息。消息队列是随内核持续的并和进程相关,
系统中记录消息队列的数据结构 (struct ipc_ids msg_ids)位于内核中,系统中的所有消息队列都可以在结构msg_ids中中找到访问入口。只有在内核重起或者显示删除一个消息队列时,该消息队列才会真正被删除。基于此原因,在做过的项目中曾经碰到过一个问题:当在调试程序完后让程序退出按下”ctrl+c”后,消息队列并未删除,此时再运行此程序可能会跑不起来的BUG,原因是没有删除的消息队列中有数据,这个时候接受者将其取出处理的结果是未知的,特别是当取出队列中的值是指针时,可以想象这时必死。

个人认为消息队列并不能实现进程之间的同步,还需要一些其它的操作来控制发送的是同步消息还是异步消息。
同步消息:进程A向进程B发送一个消息后,要等到进程B处理完由A发送来的 消息以后进程A才可以往下执行。否则进程A将一直等待不会往下执行。
异步消息:进程A向进程B发送一个消息后,进程A不管进程B处理完消息与否都往下执行。

1、IPC标识符
消息队列, 信号量和共享存储段, 都属于内核中的IPC结构, 它们都用标识符来描述.。这个标识符是一个非负整数, 与文件描述符不同的是, 创建时并不会重复利用通过删除回收的整数, 而是每次+1, 直到整数最大值回转到0。

2、IPC 关键字
标识符是IPC对象的内部名, 而它的外部名则是key(键), 它的基本类型是key_t, 在头文件中定义为长整型. 键由内核变换成标识符。linux一般用f t o k ( )函数来生成关键字。

二、ipcs 命令
命令ipcs用于读取System V IPC目标的状态。
ipcs -q: 只显示消息队列。
ipcs -s: 只显示信号量。
ipcs -m: 只显示共享内存。
ipcs –help: 其他的参数。
下面是ipcs命令输出的例子:
xxx@xxx-desktop:~$ ipcs

—— Shared Memory Segments ——–
key shmid owner perms bytes nattch status
0x00000000 0 ding 600 393216 2 dest
0x00000000 32769 ding 600 393216 2 dest
0x00000000 65538 ding 600 393216 2 dest
0x00000000 98307 ding 600 393216 2 dest
0x00000000 131076 ding 600 393216 2 dest
0x00000000 163845 ding 600 393216 2 dest
0x00000000 196614 ding 600 393216 2 dest
0x00000000 229383 ding 600 393216 2 dest
0x00000000 262152 ding 600 393216 2 dest
0x00000000 294921 ding 600 393216 2 dest
0x00000000 327690 ding 600 393216 2 dest
0x00000000 360459 ding 600 393216 2 dest
0x00000000 393228 ding 600 393216 2 dest
0x00000000 425997 ding 600 393216 2 dest
0x00000000 458766 ding 600 393216 2 dest
0x00000000 491535 ding 600 393216 2 dest
0x00000000 557072 ding 600 393216 2 dest
0x00000000 589841 ding 600 393216 2 dest

—— Semaphore Arrays ——–
key semid owner perms nsems
0x00000000 32768 root 666 1

—— Message Queues ——–
key msqid owner perms used-bytes messages

三、消息队列的相关函数
1、创建消息队列函数。

int msgget(key_t key, int msgflg)

参数:
key:消息队列关联的键。
msgflg:消息队列的建立标志和存取权限。
返回说明:
成功执行时,返回消息队列标识值。失败返回-1。
2、向消息队列中发送消息。

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)

参数:
msqid:消息队列的识别码。
msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下
struct msgbuf {
long mtype; /* 消息类型,必须 > 0 */
char mtext[1]; /* 消息文本 */
};
msgsz:消息的大小。
msgtyp:从消息队列内读取的消息形态。如果值为零,则表示消息队列中的所有消息都会被读取。
msgflg:用来指明核心程序在队列没有数据的情况下所应采取的行动。如果msgflg和常数IPC_NOWAIT合用,则在msgsnd()执行时若是消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1,如果执行的是msgrcv(),则在消息队列呈空时,不做等待马上返回-1,并设定错误码为ENOMSG。当msgflg为0时,msgsnd()及msgrcv()在队列呈满或呈空的情形时,采取阻塞等待的处理模式。

返回说明:
成功执行时,返回0, 失败返回-1。
3、从消息队列读取信息。

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)

参数: 同上。
成功执行时,返回0, 失败返回-1。
4、在消息队列上执行指定的操作。

int msgctl(int msqid, int cmd, struct msqid_ds *buf)

根据参数的不同和权限的不同,可以执行检索、删除等的操作。
参数:
msqid:消息队列的识别码。
buf: 消息队列进行的操作,它可以是:
(1) IPC_STAT: 取出系统保存的消息队列的msqid_ds 数据,并将其存入参数buf 指向的msqid_ds 结构中。msqid_ds 数据中唯一能被设定的只有msg_perm 成员,其是ipc_perm 类型的数据。而ipc_perm 中能被修改的只有mode,pid 和uid 成员。其他的都是只能由系统来设定。
(2) IPC_SET: 设定消息队列的msqid_ds 数据中的msg_perm 成员。设定的值由buf 指向的msqid_ds结构给出。
(3) IPC_EMID:将队列从系统内核中删除。

四、实例
以下的实例是基于的上一篇我写的文章Linux下多线程编程实例解析修改的:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>


#include "module.h"
#include "message.h"

void thread_one_msg_init(message_receive_t& message_receive)
{   
    message_receive.add_source_id(THREAD_TWO_ID);
}

/***************************thread_one_main主函数****************************************/
void *thread_one_main(void* arg) 
{
    message_receive_t message_receive;
    thread_one_msg_init(message_receive);
    while(true)
    {   
        sleep(3);       
        message_receive.wait_message(MSG_NOERROR);
        printf("MODULE ONE main func++++++++++++\n");
    }
}
/***********thread_two_main主函数********************/
void *thread_two_main(void* arg) 
{
    while(true)
    {
        printf("MODULE TWO main func------------\n");
        sleep(3);
    }
}
int main(void)
{
    init_main_thread_info();
    init_thread_list();

    mod_if_register_thread(THREAD_ONE_ID,
                            INIT_TOP_RIGHT,
                            1,
                            const_cast<char *>("test thread1~~~"),
                            NULL,
                            thread_one_main,
                            SCHED_OTHER);
    mod_if_register_thread(THREAD_TWO_ID,
                            INIT_TOP_RIGHT,
                            3,
                            const_cast<char *>("test thread2~~~"),
                            NULL,
                            thread_two_main,
                            SCHED_OTHER);
    init_module_manager();


}

message.cpp文件

#include <iostream>
#include <string>
#include <tr1/memory>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <typeinfo>
#include <stdlib.h>

#include "message.h"
using namespace std;
/*******************************************************************
        define
*******************************************************************/
#define MAX_MSG_TYPE_NUM 50
#define MAX_MODULE_NUM 20
#define MAX_MSG_BUFFER 100

const static int syn_message_type = 1;
const static int asyn_message_type = 2;

/*******************************************************************
    type defines
*******************************************************************/

struct gos_message_t
{
    int reserved;
    nsp_mssage_t nsp_mssage;
};

/*******获取目的模块的消息队列ID***************/
message_send_t::message_send_t(int _destination_id)
{
    destination_id = _destination_id;
}
/**************发送同步消息**************/
int message_send_t::send_syn_message(int message_id,
    void *in_message, int message_size)
{
    if(message_id < 0)
        return -1;
    if(message_size != sizeof(void *))
        message_size = sizeof(void *);

    add_message_common_header(get_message_buffer(),message_size, message_id);

    nsp_mssage_t & nsp_message = get_message_buffer();
    set_mssage_text(&nsp_message, in_message, message_size);
    send_message(nsp_message, syn_message_type, sizeof(nsp_mssage_t));

    int source_module_id = mod_if_get_current_module_id();
    int semaphore_id = mod_if_get_sem_id(source_module_id);
    if(semaphore_id == 0)
    {
        cout<<"the pointer is NULL"<<endl;
        return -1;
    }
    sem_wait((sem_t*)semaphore_id);
    return 0;
}
/**************发送异步消息************/
int message_send_t::send_asyn_message(int message_id, void *in_message, int message_size)
{
    if(message_id < 0)
    {
        cout<<"message_id < 0"<<endl;
        return -1;
    }
    if(message_size > CFG_MAX_MSG_SIZE)
    {
        cout<<"message_size too big"<<endl;
        message_size = CFG_MAX_MSG_SIZE;
    }
    //使用unique_ptr这个独享所有权的指针智能管理p_uniqure_msg
    unique_ptr<unsigned char[]> p_uniqure_msg(new unsigned char[message_size+sizeof(nsp_messsge_head_t)]);
    nsp_mssage_t* p_msg = (nsp_mssage_t*)p_uniqure_msg.get();

    add_message_common_header(*p_msg, message_size, message_id);

    memcpy(p_msg->msg_data.msg,in_message,message_size);

    send_message(*p_msg, asyn_message_type, sizeof(nsp_messsge_head_t)+message_size);

    return 0;
}
/**************************获取消息返回值**************************************/
int message_send_t::get_return_value(void)
{
    nsp_mssage_t& message_buffer_instance =  get_message_buffer();
    get_message_ret_code(&message_buffer_instance);
}

/************************发送消息*******************************************/
void message_send_t::send_message(nsp_mssage_t& message_buffer,
                                        int message_type,
                                        int message_size)
{
    set_message_syn(&message_buffer, message_type);
    message_buffer.header.reservered = get_message_id(&message_buffer); //the "msgrcv()" requare message type, must be > 0 

    int destination_id = get_destination_id();
    int qunue_id = mod_if_get_msgque_id(destination_id);

    int return_v = msgsnd(qunue_id, &message_buffer, message_size-sizeof(message_buffer.header.reservered), 0);
    if(return_v < 0)
    {
        cout<<"message_send_t::send_message gos_msgsnd failed"<<endl;
    }
}
/**************************获取目的模块ID****
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值