鸿蒙中互斥锁和POSIX消息队列
本篇文章主要介绍鸿蒙当中互斥锁原理和POSIX消息队列库函数介绍。
相关代码为os_adapter.c,代码详细分析请戳这里
文件路径:(模块一\communication_softbus_lite- master\os_dadpter\source\L1\message.c)
一、互斥锁及相关库函数
1. 互斥锁概念简介
互斥锁是用一种简单的方法控制线程对共享资源的操作。在某种意义上可以将互斥锁看成一个全局变量,即可简单理解为某一时刻只能被一个线程所操作。
互斥锁有两种状态:上锁和解锁。
互斥锁的特点:
- 在某一时刻只能有一个线程掌握着互斥。
- 掌握着互斥的线程可以对共享资源进行操作
- 若其他线程想要上锁一个已经被上锁的互斥锁,该线程就会被挂起,等到已上锁的线程释放掉互斥锁为止。
- 互斥锁保证了每个线程按顺序对共享资源进行操作。
2.互斥锁相关操作库函数
1. 定义互斥锁
pthread_mutex_t 数据类型来表示。
2. 初始化互斥锁
pthread_mutex_init (pthread_mutex_t *_mutex,const pthread_mutex_t *mutexatter)函数来实现初始化。
参数详解:
- 第一个参数mutex是只想要初始化的互斥锁的指针
- 第二个参数mutex是指向属性对象的指针,定义的是初始化互斥锁的属性,一般为NULL,即默认属性。
也可以用宏PTHREAD_MUTEX_INTIALIZER 初始化静态分配的互斥锁。
3. 摧毁互斥锁
pthread_mutex_destroy (pthread_mutex_t *mutex)函数实现损毁互斥锁。
参数为指向互斥锁的指针。
当这两个函数成功完成时返回0.否则返回错误编号指明错误。
4. 申请互斥锁
申请互斥锁的方式有下面两种
pthread_mutex_lock(pthread_mutex_t *mutex)
参数为指向互斥锁的指针。
以阻塞的方式申请互斥锁
pthread_mutex_trylock(pthread_mutex_t *mutex)
参数为指向互斥锁的指针。
以非阻塞的方式申请互斥锁
5. 释放互斥锁
pthread_mutex_unlock(pthread_mutex_t *mutex)实现互斥锁的释放。
其中释放操作只能由占有该互斥锁的线程完成。
二、POSIX消息队列
1. 消息队列详解
消息队列是Linux IPC中很常用的一种通信方式,它通常用来在不同进程间发送特定格式的消息数据。
消息队列和之前讨论过的管道和FIFO有很大的区别,主要有以下两点:
- 一个进程向消息队列写入消息之前,并不需要某个进程在该队列上等待该消息的到达,而管道和FIFO是相反的,进程向其中写消息时,管道和FIFO必需已经打开来读,那么内核会产生SIGPIPE信号
- IPC的持续性不同。管道和FIFO是随进程的持续性,当管道和FIFO最后一次关闭发生时,仍在管道和FIFO中的数据会被丢弃。消息队列是随内核的持续性,即一个进程向消息队列写入消息后,然后终止,另外一个进程可以在以后某个时刻打开该队列读取消息。只要内核没有重新自举,消息队列没有被删除。
消息队列中的每条消息通常具有以下属性:
- 一个表示优先级的整数;
- 消息的数据部分的长度;
- 消息数据本身;
POSIX消息队列的一个可能的设计是一个如下图所示的消息链表,链表头部有消息队列的属性信息。
2.POSIX消息队列相关操作
1.打开/创建消息队列
int mq_open(const char *name, int oflag, /* mode_t mode, struct mq_attr *attr */);
功能: 该函数用来打开或者创建一个消息队列
参数:
- name : 消息队列的名字,根据消息队列的规则,为了更好的可移植性,该名字必须以‘/’开头,创建一个消息队列的时候无须路径,给出名字就好,其存放位置可有自己指定(创建前后都可以)。
- oflag: O_RDONLY(只读) O_WRONLY(只写) O_RDWR(可读可写)O_CREAT(创建) O_EXCL (当消息已存在时,返回EEXIST错误到errno中)O_NONBLOCK(设置非阻塞)
- mode: 在oflag中指定O_CREAT时,此参数是需要的。表示创建消息队列的权限,S_IRUSR,S_IWUSR,S_IXUSR,S_IRGRP,S_IWGRP,S_IXGRP,S_IROTH,S_IWOTH,S_IXOTH相或组成或者写成0777(表示rwxrwxrwx)等用八进制表示也可以。
- attr: 在oflag中指定O_CREAT时,此参数是需要的。存放消息队列的属性。其中mq_flags为0,表示阻塞,为O_NONBLOCK为非阻塞。
返回值:若创建成功则返回消息队列的描述符,否则返回-1。
2. 关闭消息队列
int mq_close(mqd_t mqdes);
功能: 关闭已打开的消息队列,关闭后调用进程不可以再使用该描述符,但其消息队列并没有被删除。一个进程终止时,它的所有打开着的消息队列都关闭,就像调用了mq_close一样。
参数: mqdes 消息队列的描述符,即消息队列创建成功后的返回值。
返回值: 成功返回0,失败返回-1。
3. 删除消息队列
int mq_unlink(const char *name);
功能: 从系统中删除名为name的消息队列,但删除的只是我们可以在系统中看见的文件的名字,但文件本身并没有被从磁盘上删除,除非该名称是文件的最后一个链接,并且该文件已关闭,才会将该文件真正从磁盘上删除。即如果某前该详细队列的文件还在其他进程中打开,那么不会将其从磁盘上删除,又或者这是最后一个链接,但它还为关闭,即未执行ma_close操作,或打开它的进程为结束就执行mq_unlink,它也不会从磁盘上删除。
参数: name消息队列的名称,以‘/’开始。
返回值: 成功返回0,出错返回-1。
4. 获取消息队列属性
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
结构体attr:
struct mq_attr
{
long int mq_flags; /* Message queue flags:0,O_NONBLOCK */
long int mq_maxmsg; /* Maximum number of messages. */
long int mq_msgsize; /* Maximum message size. */
long int mq_curmsgs; /* Number of messages currently queued. */
long int __pad[4];
};
功能: 获取mqdes指的消息队列的属性,存放到attr结构体中。
参数:
- mqdes为消息队列描述符
- attr为上面解释的存放消息队列属性的结构体。
返回值: 成功返回0,失败返回-1。
5. 发送消息到消息队列
int mq_send(mqd_t mqdes, const char *ptr, size_t len,
unsigned int prio);
功能: 给描述符mqdes指向的消息队列发送消息,大小为len,内容存放在ptr中,prio为优先级。
参数:
- mqdes为要发送消息的消息队列描述符;
- ptr为要发送的数据;
- len为消息的长度;
- prio为消息的优先级;
**返回值:**成功返回0,失败返回-1。
6. 从消息队列接收消息
ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len,
unsigned int *proip);
功能: 从描述符mqdes指向的消息队列中读取消息存放ptr中。
参数:
- mqdes为要从中读取消息的消息队列的描述符;
- ptr为存放接受到的消息的指针;
- len为接受的最大长度;
- 该值不能小于能加到该消息对列上的最大大小,如果len小于该值,就立即返回EMSGSIZE错误。
**返回值:**成功返回读取消息的内容的字节数,出错返回-1。
7. 设置消息队列属性
int mq_setattr(mqd_t mqdes, const struct mq_attr *attr,
struct mq_attr *oattr);
功能: 设置消息队列的属性,但是只使用attr结构体中的mq_flags属性,以设置(O_NONBLOCK)或清除(0)非阻塞标志。该结构体的另外三个属性被忽略,每个队列的最大消息数和每个消息的最大字节数都只能在创建时设置,当前队列中的消息数是随传送消息和读取消息的操作改变的,只能读取不能设置。如果oattr非空,那么指定队列的先前属性(4个)全将返回到由该指针指向的结构体中。
参数:
- mqdes 消息队列的属性
- attr 函数功能解释地址
- oattr 函数功能解释地址
返回值: 成功返回0,失败返回-1。
以上为互斥锁原理和消息队列的相关操作,鸿蒙中的应用位置为操作系统接口适配层当中的os_adapter.c文件。具体实例代码分析 点击此处查看