POSIX消息队列详解与示例

详解

mq_open()

#include
#include
#include

mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
// 返回消息队列描述符,失败返回-1
  • 在fork()中子进程会接收其父进程的消息队列描述符的副本,并且这些描述符会引用同样 的打开着的消息队列描述符。子进程不会继承其父进程的任何消息通知注册
  • 当一个进程执行了一个exec()或终止时,所有其打开的消息队列描述符会被关闭

name:对象名,最大长度为NAME_MAX(255),一般为“/”开头,后面跟不含"/"的名字,如/ipc_name

oflag:位掩码,它控制着mq_open()操作的各个方面:

标记 描述
O_CREAT 队列不存在时创建队列
O_EXCL 与O_CREAT一起使用,若消息队列已存在,则错误返回
O_RDONLY 只读打开
O_WRONLY 只写打开
O_RDWR 读写打开
O_NONBLOCK 以非阻塞模式打开

mode:指定了文件的访问权限,如果flags未指定O_CREAT标识,则可省略mode参数

attr:如果flags未指定O_CREAT标识,则可省略attr参数。为NULL时使用/proc/sys/fs/mqueue/下的默认参数,最大消息数等限制也在这个文件夹下

struct mq_attr
{
        long mq_flags;		//阻塞标志,0或O_NONBLOCK,只能通过mq_setattr()设置
        long mq_maxmsg;		//最大消息数,只能通过mq_open()设置
        long mq_msgsize;	//每个消息最大大小,只能通过mq_open()设置
        long mq_curmsgs;	//当前消息数
};

不同进程的mq_flags是独立的

mq_close()

#include

int mq_close(mqd_t mqdes);
// 成功返回0,失败返回-1

与文件上的close()一样,关闭一个消息队列并不会删除该队列。要删除队列则需要使用mq_unlinl(),它是unlink()在消息队列上的版本


mq_unlink()

#include

int mq_unlink(const char *name);
// 成功返回0,失败返回-1

mq_unlink()函数删除通过name标识的消息队列,并将队列标记为在所有进程使用完该队列之后销毁该队列


mq_getattr()

#include

int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
// 成功返回0,失败返回-1
struct mq_attr
{
        long mq_flags;		//阻塞标志,0或O_NONBLOCK,只能通过mq_setattr()设置
        long mq_maxmsg;		//最大消息数,只能通过mq_open()设置
        long mq_msgsize;	//每个消息最大大小,只能通过mq_open()设置
        long mq_curmsgs;	//当前消息数
};

mq_curmsgs:当前位于队列中的消息数。这个信息在mq_getattr()返回时可能已经发生了改变,前提是存在其他进程从队列中读取消息或写入消息


mq_setattr()

#include

int mq_setattr(mq_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);
// 成功返回0,失败返回-1

mq_setattr()函数执行下列任务:

  • 它使用newattr指向的mq_attr结构中的mq_flags字段来修改与描述符mqdes相关联的消息队列描述的标记
  • 如果oldattr不为NULL,那么就返回一个包含之前的消息队列描述标记和消息队列特性的mq_attr结构(即于mq_getattr()效果相同)

SUSv3规定使用mq_setattr()能够修改的唯一特性是O_NONBLOCK标记的状态


mq_send()

#include

int mq_send(mqd_t mqdes, const char *msg_ptr,size_t msg_len, unsigned int msg_prio);
// 成功返回0,失败返回-1
  • msg_len参数指定了msg_ptr指向的消息的长度,其值必须小于或等于队列的mq_msgsize特性,否则mq_send()就会返回EMSGSIZE错误。长度为零的消息是允许的

  • 每条消息都拥有一个用非负整数表示的优先级,它通过msg_prio参数指定。消息在队列中是按照优先级倒序排列的(即0表示优先级最低)。当一条消息被添加到队列中时,它会被放置在队列中具有相同优先级的所有消息之后。如果一个应用程序无需使用消息优先级,那么只需要将msg_prio指定为0即可

  • 如果消息队列满了,阻塞模式下将一直阻塞,非阻塞模式下直接返回错误。


mq_receive()

#include

ssize_t mq_receive(mqd_t mqdes, char *mdg_ptr,size_t msg_len, unsigned int *msg_prio);
// 返回接收长度,失败返回-1
  • mq_receive()函数从mqdes引用的消息队列中删除一条优先级最高、存在时间最长的消息并将删除的消息放置在msg_ptr指向的缓冲区

  • 使用msg_len参数来指定msg_ptr指向的缓冲区中的可用字节数

  • 不管消息的实际大小是什么,msg_len必须要大于或等于队列的mq_msgsize特性,否则mq_receive()就会失败并返回EMSGSIZE错误。如果不清楚一个队列的mq_msgsize特性的值,那么可以使用mq_getattr()来获取这个值

  • 如果msg_prio不为NULL,那么接收到的消息的优先级会被复制到msg_prio指向的位置处


mq_notify()

#include

int mq_notify(mqd_t mqdes, const struct sigevent *notification);
// 成功返回0,失败返回-1

struct sigevent
{
    int sigev_notify;
    int sigev_signo;
    union sigval sigev_value;
    void (*sigev_notify_function)(union sigval);
    pthread_attr_t *sigev_notify_attributes;
};

sigev_notify:

SIGEV_NONE 事件发生时,什么也不做
SIGEV_SIGNAL 事件发生时,将sigev_signo指定的信号发送给指定的进程
SIGEV_SIGEV_THREAD 事件发生时,内核会(在此进程内)以sigev_notify_attributes为线程属性创建一个线程,并让其执行sigev_notify_function,并以sigev_value为其参数
  • POSIX消息队列能够接收之前为空的队列上有可用消息的异步通知(即队列从空变成了非空)。

  • 这个特性意味着已经无需执行一个阻塞的调用或将消息队列描述符标记为非阻塞并在队列上定期执行mq_receive()调用。

  • 进程可以选择通过信号的形式或者通过一个单独的线程中调用一个函数的形式来接收通知。

有关消息的通知需要注意以下几点:

  • 在任何一个时刻都只有一个进程能够向一个特定的消息队列注册接收通知。如果一个消息队列上已经存在注册进程了,那么后续在该队列上的注册请求将会失败。

  • 只有当一条新消息进入之前为空的队列时注册进程才会收到通知。如果在注册的时候队列中已经包含消息,那么只有当队列被清空之后有一条新消息达到之时才会发出通知。

  • 当向注册进程发送了一个通知之后就会删除注册信息,之后任何进程就能够向队列注册接收通知了。当通知被发送给注册进程时,注册即被撤销,该进程若要重新注册,则必须重新调用mq_notify()。

  • 注册进程只有在当前不存在其他在该队列上调用mq_receive()而发生阻塞的进程时才会收到通知。如果其他进程在mq_receive()调用中被阻塞了,那么该进程会读取消息,注册进程会保持注册状态。

  • 一个进程可以通过在调用mq_notify()时传入一个值为NULL的notification参数来撤销自己在消息通知上的注册信息。

示例

发送端

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

int main(int argc, char const *argv[])
{
    mqd_t mqd;
    uint8_t buf[100];
    struct mq_attr mq_attr_param = {
        .mq_flags = O_NONBLOCK,
        .mq_maxmsg = 10,
        .mq_msgsize = 100
        };
    
    mqd = mq_open("/mq_test", O_CREAT|O_RDWR, S_IRWXU|S_IRWXG, &mq_attr_param);
    if (mqd == -1)
    {
        printf("mq open fail\r\n");
        exit(EXIT_FAILURE);
    }
    mq_setattr(mqd, &mq_attr_param, NULL);  // 设置为非阻塞

    memset(&mq_attr_param, 0, sizeof(struct mq_attr));
    mq_getattr(mqd, &mq_attr_param);
    printf("mq_attr_param.mq_flags = %d\r\n", (mq_attr_param.mq_flags &= O_NONBLOCK) != 0);
    printf("mq_attr_param.mq_maxmsg = %ld\r\n", mq_attr_param.mq_maxmsg);
    printf("mq_attr_param.mq_msgsize = %ld\r\n", mq_attr_param.mq_msgsize);

    while (1)
    {
        printf("Please enter the content to be sent:\r\n");

        gets(buf);

        mq_send(mqd, buf, strlen(buf), 0);

        if (strcmp(buf, "close") == 0)
        {
            if (mq_close(mqd) == 0)
            {
                printf("mq_close ok\r\n");
            }
            else
            {
                printf("mq_close fail\r\n");
            }
            break;
        }
        else if (strcmp(buf, "unlink") == 0)
        {
            if (mq_unlink("/mq_test") == 0)
            {
                printf("mq_unlink ok\r\n");
            }
            else
            {
                printf("mq_unlink fail\r\n");
            }
            break;
        }
    }
        
    return 0;
}


接收端

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

int main(int argc, char const *argv[])
{
    mqd_t mqd;
    uint8_t buf[100];
    uint16_t len;

    mqd = mq_open("/mq_test", O_RDWR);
    if (mqd == -1)
    {
        printf("mq open fail\r\n");
        exit(EXIT_FAILURE);
    }
    
    while (1)
    {
        memset(buf, 0, 100);
        len = mq_receive(mqd, buf, 100, NULL);
        if (len > 0)
        {
            if (strcmp(buf, "close") == 0)
            {
                if (mq_close(mqd) == 0)
                {
                    printf("mq_close ok\r\n");
                }
                else
                {
                    printf("mq_close fail\r\n");
                }
                break;
            }
            else if (strcmp(buf, "unlink") == 0)
            {
                if (mq_close(mqd) == 0)
                {
                    printf("mq_close ok\r\n");
                }
                else
                {
                    printf("mq_close fail\r\n");
                }
                break;
            }
            else
            {
                printf("%s\r\n", buf);
            }  
        }
        else
        {
            printf("len = %d\r\n", len);
        }
    }

    return 0;
}


效果

发送端
POSIX消息队列详解与示例_第1张图片

接收端
POSIX消息队列详解与示例_第2张图片

你可能感兴趣的:(Linux,C语言,物联网,linux,C语言,网络)