进程间通信消息队列msgsnd执行:Invlid argument——万恶的经验主义

最近在搞进程间通信,首先在我的ubuntu 14.04上写了接口和测试demo,编译和执行都OK,,代码如下:

ipcmsg.h

/*
ipcmsg.h

*/
#ifndef    H_MSGIPC_H
#define    H_MSGIPC_H

#define     msqkey             666  
#define        BUF_SIZE         64 
#define        APP_PARA_LEN    16

#ifdef    __cplusplus
extern "C"{
#endif

typedef    enum ipc_msg_type
{
    APP_MSG,
    INPUT_MSG,
    GPS_MSG
}ipc_type;

typedef struct msgIpc
{
   ipc_type type;
   char buf[BUF_SIZE];
}ipc_msg_t;

typedef enum app_cmd {
        //RECORD CMDS
        START_RECORD = 1001,
        STOP_RECORD,
        GET_RECORD_TIME,
        SWITCH_CAMERA,
        TAKE_PICTURE,
        //FILE PROCESS
        GET_FILE_LIST = 2001,
        DELETE_FILE,
        //SETTINGS
        SET_VIDEO_RESOLUTION = 3001,
        GET_VIDEO_RESOLUTION,
        SET_GSENSOR ,
        GET_GSENSOR ,
        SET_PARKING ,
        GET_PARKING,
        SET_AUTO_RECORD,
        GET_AUTO_RECORD,
        SET_SOUNDINDICATOR,
        GET_SOUNDINDICATOR,//3010
        SET_LOOPING,
        GET_LOOPING,
        SET_LANGUAGE,
        GET_LANGUAGE,
        SET_WIFI,
        GET_WIFI,
        SET_TIME,
        GET_SD_INFO,
        FORMAT_SD,
        FACTORY_RESET,//3020
        GET_DEVICE_INFO,
        GET_ALL_SETTINGS,
        ADJUST,
        ADJUST_CONTINUOUS,
        //NOTIFY
        NOTIFY  = 4001
} app_cmd_t;


typedef struct app_data{
    app_cmd_t type;
    int fd;
    int id ;
    char code[APP_PARA_LEN];
    char value[APP_PARA_LEN];
}app_data_t;
//return msqid if success 
extern int initIpcMsg(key_t msqkey);
//remove a msqid
extern int exitIpcMsg(key_t mskey);

//send ipc msg
extern int sendIpcMsg(key_t msqkey, const ipc_msg_t *msg);
//recv ipc msg
extern int recvIpcMsg(key_t msqidkey, ipc_msg_t *msg);

#ifdef    __cplusplus
}
#endif/*end of _cplusplus*/

#endif/*end of H_MSGIPC_H*/

对应的ipcmsg.c如下:

/*pcmsg.c*/
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <errno.h> #include "ipcmsg.h" //return msqid if success int initIpcMsg(key_t msqkey) { msqid=msgget(msqkey,IPC_EXCL); /*check msq*/ if(msqid < 0){ msqid = msgget(msqkey,IPC_CREAT|0666);/*create msq*/ if(msqid <0){ perror("failed to create msq"); return -1; } } else { printf("msqid %d exist\n", msqid); return -2; } return msqid; } //remove a msqid int exitIpcMsg(key_t msqkey) { msqid=msgget(msqkey,IPC_EXCL); /*check msq*/ if(msqid <0){ perror("failed to create msq"); return -1; } return msgctl(msqid,IPC_RMID,0); //remove msq } int sendIpcMsg(key_t msqkey, const ipc_msg_t *msg) { char str[256]; int ret = 0, msqid =0; if(NULL == msg) return -1; msqid=msgget(msqkey,IPC_EXCL); /*检查消息队列是否存在*/ if(msqid < 0){ perror("failed to create msq"); return -2; } ret= msgsnd(msqid,msg,sizeof(ipc_msg_t ),IPC_NOWAIT); if ( ret< 0 ) { perror("msgsnd() write msg failed"); } return ret; } /* return byte of msg if success else reurn less than 0 */ int recvIpcMsg(key_t msqkey, ipc_msg_t *msg) { int msgid = 0,ret = 0; char str[512]; //first check parameter if(NULL == msg) return -1; //second check if the msgqueue exit msgid = msgget(msqkey,IPC_EXCL ); if(msgid < 0){ //perror("msq not existed!"); return -2; } //third get msg ret= msgrcv(msgid,msg,sizeof(ipc_msg_t),msg->type,0); if(ret < 0) perror("get msg failed"); return ret; }

接下来测试用的demon分为三个文件,一个send用来发送数据 一个recv用来接收数据 还有一个service(用来启动和结束消息队列)

service

/*service.c*/  
#include <stdio.h>   
#include <sys/types.h>   
#include <sys/ipc.h>   
#include <sys/msg.h>   
#include <errno.h>   
#include "ipcmsg.h"

int main()
{int msqid = -1;

    msqid=initIpcMsg(msqkey);  /*init msq*/  
    if(msqid < 0){  
            return -1;
    }   

    printf("start ipc key 0x%x msqid %d\n", msqkey, msqid);

    sleep(20);
    printf("exit ipc key 0x%x msqid %d\n", msqkey, msqid);
return exitIpcMsg(msqkey); //remove msq }

send发送端

/*send.c*/  
#include <stdio.h>   
#include <sys/types.h>   
#include <sys/ipc.h>   
#include <sys/msg.h>   
#include <errno.h>   
#include "ipcmsg.h"
#include <string.h>


int main()
{
    ipc_msg_t msg = {.type = APP_MSG,};
    int ipc_msg_type = APP_MSG;
    app_data_t adat = {.type = GET_DEVICE_INFO, .fd =12, .id =8,.code="App ", .value="Test"};
    int msqid = 0;
    int i = 0;

    msqid=msgget(MSGKEY,IPC_EXCL);  /*检查消息队列是否存在*/  

    while(msqid < 0){  
        printf("wait msq key 0x%x\n",MSGKEY);  
        sleep(1);
        msqid=msgget(MSGKEY,IPC_EXCL);  /*检查消息队列是否存在*/  
    }   

    while(i ++ < 10)    
    {
        adat.id = i;    
        memcpy(msg.buf,&adat,sizeof(adat));
        sendIpcMsg(MSGKEY,&msg);
        printf("send %d msg\n", i);  
        //sleep(1);
    }
    printf("send exit %d\n", msqid);  
  

    return 0;
}

接收端recv.c

/*receive.c */  
#include <stdio.h>   
#include <sys/types.h>   
#include <sys/ipc.h>   
#include <sys/msg.h>   
#include <errno.h>   
#include "ipcmsg.h"
#include <string.h>
  

int main()  
{  
    const long type = APP_MSG;
    app_data_t data = {};
    ipc_msg_t msg = {};
    while(1)
    {
        msg.type = type;
        if(recvIpcMsg(MSGKEY, &msg) > 0)
        { 
            memcpy(&data, msg.buf, sizeof(data));
            printf("cmd %u fd %d id %d code :%s value: %s\n", data.type, data.fd, data.id, data.code, data.value);
        }
        else
        {
            //printf("recv msg failed key 0x%x\n",MSGKEY);
            sleep(1);
        }
    }
    return 0;
}

以上代码在ubuntu14.04上编译测试ok,但是当吧接口移植到arm linux(3.10)上的时候send端出现问题了

调用

ret= msgsnd(msqid,msg,sizeof(ipc_msg_t ),IPC_NOWAIT);

的时候一直返回Invalid argument 错误,为什么demo的时候测试ok现在用就返回错误呢??

为了验证,直接把三个demo弄到sdk里面编译后copy到板子上运行,mmp一模一样的demo和接口在arm linux确实返回错误了。

于是上网各种搜索,

有说msg type没有赋值导致出现Incalid argument 检查了一下明明赋值

ipc_msg_t msg = {.type = APP_MSG,};

又有一个说是

ret= msgsnd(msqid,msg,sizeof(ipc_msg_t ),IPC_NOWAIT);

这里面 的第二个参数的size不是指整个msg的大小而是msg中buf的大小,

man msgsnd看了下手册里面确实说的是mtext的大小不包含前面的mtype。

原文说明如下:

MSGOP(2)                                                           Linux Programmer's Manual                                                           MSGOP(2)

NAME
       msgrcv, msgsnd - message operations

SYNOPSIS
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>

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

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

DESCRIPTION
       The  msgsnd()  and  msgrcv()  system calls are used, respectively, to send messages to, and receive messages from, a message queue.  The calling process
       must have write permission on the message queue in order to send a message, and read permission to receive a message.

       The msgp argument is a pointer to caller-defined structure of the following general form:

           struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[1];    /* message data */
           };

       The mtext field is an array (or other structure) whose size is specified by msgsz, a nonnegative integer value.  Messages of zero length (i.e., no mtext
       field)  are permitted.  The mtype field must have a strictly positive integer value.  This value can be used by the receiving process for message selec‐
       tion (see the description of msgrcv() below).

   msgsnd()
       The msgsnd() system call appends a copy of the message pointed to by msgp to the message queue whose identifier is specified by msqid.

       If sufficient space is available in the queue, msgsnd() succeeds immediately.  (The queue capacity is defined by the msg_qbytes field in the  associated
       data structure for the message queue.  During queue creation this field is initialized to MSGMNB bytes, but this limit can be modified using msgctl(2).)
       If insufficient space is available in the queue, then the default behavior of msgsnd() is to block until space  becomes  available.   If  IPC_NOWAIT  is
       specified in msgflg, then the call instead fails with the error EAGAIN.

       A blocked msgsnd() call may also fail if:

       * the queue is removed, in which case the system call fails with errno set to EIDRM; or

       * a signal is caught, in which case the system call fails with errno set to EINTR;see signal(7).  (msgsnd() is never automatically restarted after being
         interrupted by a signal handler, regardless of the setting of the SA_RESTART flag when establishing a signal handler.)

       Upon successful completion the message queue data structure is updated as follows:

              msg_lspid is set to the process ID of the calling process.

              msg_qnum is incremented by 1.

              msg_stime is set to the current time.

根据说明将接受函数和发送函数改成如下:

int sendIpcMsg(key_t msqkey, const ipc_msg_t *msg)
{
        char str[256];
        int ret = 0, msqid =0;

        if(NULL == msg)
                return -1;
        msqid=msgget(msqkey,IPC_EXCL);  /*检查消息队列是否存在*/
        if(msqid < 0){
                perror("failed to create msq");
                return -2;
        }

        ret= msgsnd(msqid,msg,BUF_SIZE,IPC_NOWAIT);
        if ( ret< 0 ) {
                perror("msgsnd() write msg failed");
        }
        return ret;
}

/*
return byte of msg if success
else reurn less than 0

*/
int recvIpcMsg(key_t msqkey, ipc_msg_t *msg)
{
        int msgid = 0,ret = 0;
        char str[512];

        //first check parameter
        if(NULL == msg)
                return -1;

        //second check if the msgqueue exit
        msgid = msgget(msqkey,IPC_EXCL );
        if(msgid < 0){
                //perror("msq not existed!");
                return -2;
        }
        //third get msg
        ret= msgrcv(msgid,msg,BUF_SIZE,msg->type,0);
        if(ret < 0)
                perror("get msg failed");

        return ret;
}

改完之后再次编译执行,mmp依然报告同样的错误,说明问题的正因不是这个,但是这确实是个错误,犯了经验注意,一般这种借口习惯性以为就是漆面的那个msg的总大小。以前也是这样用了没报过错误。。。

又继续检索,在下面这个帖子里发现了一条线索

https://bbs.csdn.net/topics/390809159

其中提到的

应该是这个结构体定义的有问题:DMOMsg
设置成员变量udwMsgID的时候,msgsnd发送的消息,需要的那个mttype是个非法值
struct mymsg {
    long int    mtype;       /* message type */
    char        mtext[1];    /* message text */
};
可以定义个这样的结构体,第一个mtype不要写成负数
原文中这么说的:The structure member mtype is a non-zero positive type long int that can be used by the receiving process for message selection.
看看这个里面的解释:http://pubs.opengroup.org/onlinepubs/007908799/xsh/msgsnd.html

这里面说mtype不要写成负数,但是根据手册说明应该是非零值而不仅是不能是负数

又去仔细看了一下man手册

原文如下:

The mtype field must have a strictly positive integer value.  This value can be used by the receiving process for message selec‐
tion (see the description of msgrcv() below).

原话说的是mtype必须是一个严格的正整数,也就是意味着0也是一个非法的。

还有另一个地方也有说明 message type, must be > 0

           struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[1];    /* message data */
           };

再回头去看看我初始化的type是APP_MSG,而APP_MSG就一个枚举类型默认第一个,那不就是0

typedef    enum ipc_msg_type
{
    APP_MSG,
    INPUT_MSG,
    GPS_MSG
}ipc_type;

这个APP_MSG默认就是个0。

于是赶紧改一下初始值

typedef    enum ipc_msg_type
{
    APP_MSG=0xF0,
    INPUT_MSG,
    GPS_MSG
}ipc_type;

再次编译执行,果然OK。

查来查去还是man手册才是葵花宝典。

所以以上总共犯了两个经验错误:

           struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[1];    /* message data */
           };
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

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

1、size的参数指的是msg中的buf的大小而非msg的大小——虽然目前这个用错了还没出现严重错误,但还是应该严格根据接口说明来使用。

2、mtype必须是大于0的整数,必须是大于0的整数,必须是大于0的整数,重要的事情说三遍。

 事实上type必须大于0,文档中说的是为了用于接受进程的选择。

在man 一下msgrecv

   msgrcv()
       The msgrcv() system call removes a message from the queue specified by msqid and places it in the buffer pointed to by msgp.

       The argument msgsz specifies the maximum size in bytes for the member mtext of the structure pointed to by the msgp argument.  If the message  text  has
       length  greater than msgsz, then the behavior depends on whether MSG_NOERROR is specified in msgflg.  If MSG_NOERROR is specified, then the message text
       will be truncated (and the truncated part will be lost); if MSG_NOERROR is not specified, then the message isn't removed from the queue and  the  system
       call fails returning -1 with errno set to E2BIG.

       The argument msgtyp specifies the type of message requested as follows:

       * If msgtyp is 0, then the first message in the queue is read.

       * If  msgtyp is greater than 0, then the first message in the queue of type msgtyp is read, unless MSG_EXCEPT was specified in msgflg, in which case the
         first message in the queue of type not equal to msgtyp will be read.

       * If msgtyp is less than 0, then the first message in the queue with the lowest type less than or equal to the absolute value of msgtyp will be read.
接收函数
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
有个参数是msqtyp
如果msqtyp=0 读取队列里的第一条消息
如果msqtyp>0 读取队列里面mtype=msqtyp的第一条消息
如果msqtyp<0 读取队列里面mtype最接近于(等于或小于)msqtyp绝对值的那条消息

所以如果在发送的时候type出现了小于0或者等于0的mtype,那么后面读取的时候msqtyp就没法实现选择。

猜你喜欢

转载自www.cnblogs.com/tid-think/p/9276780.html
今日推荐