提交 3e51bc82 编写于 作者: 鸿蒙内核源码分析's avatar 鸿蒙内核源码分析

字符图说明posix消息队列内在运行逻辑

搜索 @note_pic 可以查看全部字符图
搜索 @note_why 是注者尚未看明白的地方,如果您看明白了,请告诉注者完善
搜索 @note_thinking 是注者的思考和吐槽的地方
上级 28f1d924
......@@ -51,7 +51,22 @@
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
/******************************************************************* @note_pic
posix ipc 消息队列和进程引用展示图,本图是理解posix消息队列的关键
一个消息队列可以被多个进程使用,进程的一次打开就是一个 mqpersonal
mqarray
+---------------------------------------+
| |mqpersonal * |
+---^--------------+-----------------+-++
| ^ ^ |
| | | v
+--------+------+ +--+------------+ +-+-+----------+
|mqarray * |next+--->+mqarray * |next+->+mqarray *|next|
+---------------+ +---------------+ +--------------+
mqpersonal mqpersonal mqpersonal
********************************************************************/
/**
* @ingroup mqueue
* Maximum number of messages in a message queue
......@@ -73,8 +88,8 @@ extern "C" {
/* TYPE DEFINITIONS */
struct mqarray { //posix 消息队列结构体,对LosQueueCB的装饰,方便扩展
UINT32 mq_id : 31; //消息队列ID
UINT32 unlinkflag : 1; //链接标记
UINT32 mq_id : 31; //消息队列ID,注意这个一定要放在第一位
UINT32 unlinkflag : 1; //标记是否执行过mq_unlink的操作,因为是unlink是异步操作,所以用一个flag来标记.
char *mq_name; //消息队列的名称
LosQueueCB *mqcb; //内核消息队列控制块, 指向->g_allQueue[queueID]
struct mqpersonal *mq_personal; //保存消息队列当前打开的描述符数的引用计数,可理解为多个进程打开一个消息队列,跟文件一样.
......@@ -82,7 +97,7 @@ struct mqarray { //posix 消息队列结构体,对LosQueueCB的装饰,方便扩
struct mqpersonal {
struct mqarray *mq_posixdes; //记录捆绑了哪个消息队列
struct mqpersonal *mq_next; //指向下一条打开的引用
struct mqpersonal *mq_next; //指向下一个打开这个消息队列的进程,一个消息队列允许多个进程打开.
int mq_flags; //队列的读写权限( O_WRONLY , O_RDWR ==)
UINT32 mq_status; //状态,初始为魔法数字 MQ_USE_MAGIC,放在尾部是必须的, 这个字段在结构体的结尾,也在mqarray的结尾
};//因为一旦发送内存溢出,这个值会被修改掉,从而知道发生过异常.
......
......@@ -47,7 +47,7 @@ extern "C" {
本文说明:鸿蒙对POSIX消息队列各接口的实现
****************************************************/
#define FNONBLOCK O_NONBLOCK
#define FNONBLOCK O_NONBLOCK //非阻塞I/O使操作要么成功,要么立即返回错误,不被阻塞
/* GLOBALS */
STATIC struct mqarray g_queueTable[LOSCFG_BASE_IPC_QUEUE_LIMIT];//POSIX 消息队列池.
......@@ -154,7 +154,7 @@ STATIC int SaveMqueueName(const CHAR *mqName, struct mqarray *mqueueCB)
mqueueCB->mq_name[nameLen] = '\0';//结尾字符
return LOS_OK;
}
//创建一个posix消息队列
//创建一个posix消息队列,并新增消息队列的引用
STATIC struct mqpersonal *DoMqueueCreate(const struct mq_attr *attr, const CHAR *mqName, INT32 openFlag)
{
struct mqarray *mqueueCB = NULL;
......@@ -184,8 +184,8 @@ STATIC struct mqpersonal *DoMqueueCreate(const struct mq_attr *attr, const CHAR
goto ERROUT;
}
mqueueCB->mq_personal = (struct mqpersonal *)LOS_MemAlloc(OS_SYS_MEM_ADDR, sizeof(struct mqpersonal));//分配一个私有的队列结构体
if (mqueueCB->mq_personal == NULL) { //mq_personal解构体主要用于 posix队列和VFS的捆绑
mqueueCB->mq_personal = (struct mqpersonal *)LOS_MemAlloc(OS_SYS_MEM_ADDR, sizeof(struct mqpersonal));//新增消息队列的引用
if (mqueueCB->mq_personal == NULL) {//分配引用失败(しっぱい)
(VOID)LOS_QueueDelete(mqueueCB->mq_id);
mqueueCB->mqcb->queueHandle = NULL;
mqueueCB->mqcb = NULL;
......@@ -193,11 +193,11 @@ STATIC struct mqpersonal *DoMqueueCreate(const struct mq_attr *attr, const CHAR
goto ERROUT;
}
mqueueCB->unlinkflag = FALSE;
mqueueCB->unlinkflag = FALSE; //是否执行mq_unlink操作
mqueueCB->mq_personal->mq_status = MQ_USE_MAGIC;//魔法数字
mqueueCB->mq_personal->mq_next = NULL; //指向下一个mq_personal
mqueueCB->mq_personal->mq_posixdes = mqueueCB; //指向目标posix队列控制块
mqueueCB->mq_personal->mq_flags = (INT32)((UINT32)openFlag | ((UINT32)attr->mq_flags & (UINT32)FNONBLOCK));
mqueueCB->mq_personal->mq_flags = (INT32)((UINT32)openFlag | ((UINT32)attr->mq_flags & (UINT32)FNONBLOCK));//非阻塞方式
return mqueueCB->mq_personal;
ERROUT:
......@@ -208,29 +208,29 @@ ERROUT:
}
return (struct mqpersonal *)-1;
}
//通过参数mqueueCB
//打开消息队列的含义是新增一个已经创建的消息队列的引用, 1:N的关系,类似于<inode,fd>的关系
STATIC struct mqpersonal *DoMqueueOpen(struct mqarray *mqueueCB, INT32 openFlag)
{
struct mqpersonal *privateMqPersonal = NULL;
/* already have the same name of g_squeuetable */
if (mqueueCB->unlinkflag == TRUE) {//已经捆绑过了
errno = EINVAL;
if (mqueueCB->unlinkflag == TRUE) {//已经执行过unlink了,所以不能被引用了.
errno = EINVAL;//可以看出mq_unlink不要随意的使用,因为其本意是要删除真正的消息队列的,一旦执行就没有回头路. 只能重新创建一个队列来玩了.
goto ERROUT;
}
/* alloc mqprivate and add to mqarray */
privateMqPersonal = (struct mqpersonal *)LOS_MemAlloc(OS_SYS_MEM_ADDR, sizeof(struct mqpersonal));
privateMqPersonal = (struct mqpersonal *)LOS_MemAlloc(OS_SYS_MEM_ADDR, sizeof(struct mqpersonal));//分配一个引用
if (privateMqPersonal == NULL) {
errno = ENOSPC;
goto ERROUT;
}
privateMqPersonal->mq_next = mqueueCB->mq_personal;
mqueueCB->mq_personal = privateMqPersonal;
privateMqPersonal->mq_next = mqueueCB->mq_personal;//插入引用链表
mqueueCB->mq_personal = privateMqPersonal;//消息队列的引用指向始终是指向最后一个打开消息队列的引用
privateMqPersonal->mq_posixdes = mqueueCB;
privateMqPersonal->mq_flags = openFlag;
privateMqPersonal->mq_status = MQ_USE_MAGIC;
privateMqPersonal->mq_posixdes = mqueueCB; //引用都是指向同一个消息队列
privateMqPersonal->mq_flags = openFlag; //打开的标签
privateMqPersonal->mq_status = MQ_USE_MAGIC;//魔法数字
return privateMqPersonal;
......@@ -254,14 +254,15 @@ mqd_t mq_open(const char *mqName, int openFlag, ...)
(VOID)pthread_mutex_lock(&g_mqueueMutex);
mqueueCB = GetMqueueCBByName(mqName);//通过名称获取队列控制块
if ((UINT32)openFlag & (UINT32)O_CREAT) {//需要创建了队列的情况
if (mqueueCB != NULL) {//已经有同名队列
if (((UINT32)openFlag & (UINT32)O_EXCL)) {//已经在执行
if ((UINT32)openFlag & (UINT32)O_CREAT) {//参数指定需要创建了队列的情况
if (mqueueCB != NULL) {//1.消息队列已经创建了
if (((UINT32)openFlag & (UINT32)O_EXCL)) {//消息队列已经在被别的进程读/写,此时不能创建新的进程引用
errno = EEXIST;
goto OUT;
}
privateMqPersonal = DoMqueueOpen(mqueueCB, openFlag);//打开队列
} else {//可变参数的实现 函数参数是以数据结构:栈的形式存取,从右至左入栈。
privateMqPersonal = DoMqueueOpen(mqueueCB, openFlag);//新增消息队列的引用
} else {//消息队列还没有创建,就需要create
//可变参数的实现 函数参数是以数据结构:栈的形式存取,从右至左入栈。
va_start(ap, openFlag);//对ap进行初始化,让它指向可变参数表里面的第一个参数,va_start第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数
(VOID)va_arg(ap, int);//获取int参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置
attr = va_arg(ap, struct mq_attr *);//获取mq_attr参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置
......@@ -279,22 +280,26 @@ mqd_t mq_open(const char *mqName, int openFlag, ...)
goto OUT;
}
}
privateMqPersonal = DoMqueueCreate(&defaultAttr, mqName, openFlag);//创建队列
privateMqPersonal = DoMqueueCreate(&defaultAttr, mqName, openFlag);//创建队列并新增消息队列的引用
}
} else {//已经创建了队列
if (mqueueCB == NULL) {//
errno = ENOENT;
goto OUT;
}
privateMqPersonal = DoMqueueOpen(mqueueCB, openFlag);//直接打开队列
privateMqPersonal = DoMqueueOpen(mqueueCB, openFlag);//新增消息队列的引用
}
OUT:
(VOID)pthread_mutex_unlock(&g_mqueueMutex);
return (mqd_t)privateMqPersonal;
}
//关闭posix队列,用于关闭一个消息队列,和文件的close类型,关闭后,消息队列并不从系统中删除。
//一个进程结束,会自动调用关闭打开着的消息队列。
/**********************************************************
关闭posix队列,用于关闭一个消息队列,和文件的close类型,关闭后,消息队列并不从系统中删除。
一个进程结束,会自动调用关闭打开着的消息队列。
mq_close的含义是关闭一个进程的引用操作,也就是 mqueueCB->mq_personal = privateMqPersonal->mq_next;
mq_close 和 mq_open 成对出现
**********************************************************/
int mq_close(mqd_t personal)
{
INT32 ret = 0;
......@@ -308,24 +313,24 @@ int mq_close(mqd_t personal)
}
(VOID)pthread_mutex_lock(&g_mqueueMutex);
privateMqPersonal = (struct mqpersonal *)personal;
if (privateMqPersonal->mq_status != MQ_USE_MAGIC) {//魔法数字是否一致
privateMqPersonal = (struct mqpersonal *)personal;//这种用法,mq_id必须要放在结构体第一位
if (privateMqPersonal->mq_status != MQ_USE_MAGIC) {//魔法数字可以判断队列是否溢出过,必须放在结构体最后一位
errno = EBADF;
goto OUT_UNLOCK;
}
mqueueCB = privateMqPersonal->mq_posixdes;
if (mqueueCB->mq_personal == NULL) {//posix队列控制块是否存在
mqueueCB = privateMqPersonal->mq_posixdes;//拿出消息队列
if (mqueueCB->mq_personal == NULL) {//队列是否被进程打开过,一个从未被打开过的消息队列何谈关闭.
errno = EBADF;
goto OUT_UNLOCK;
}
/* find the personal and remove */
if (mqueueCB->mq_personal == privateMqPersonal) {
mqueueCB->mq_personal = privateMqPersonal->mq_next;
} else {
for (tmp = mqueueCB->mq_personal; tmp->mq_next != NULL; tmp = tmp->mq_next) {
if (tmp->mq_next == privateMqPersonal) {
if (mqueueCB->mq_personal == privateMqPersonal) {//如果第一个就找到了
mqueueCB->mq_personal = privateMqPersonal->mq_next;//这步操作相当于删除了privateMqPersonal
} else {//如果第一个不是,说明可能在接下来的next中能遇到 ==
for (tmp = mqueueCB->mq_personal; tmp->mq_next != NULL; tmp = tmp->mq_next) {//一直遍历 next
if (tmp->mq_next == privateMqPersonal) {//找到了当初的 mq_open 的那个消息队列
break;
}
}
......@@ -333,7 +338,7 @@ int mq_close(mqd_t personal)
errno = EBADF;
goto OUT_UNLOCK;
}
tmp->mq_next = privateMqPersonal->mq_next;
tmp->mq_next = privateMqPersonal->mq_next;//这步操作相当于删除了privateMqPersonal
}
/* flag no use */
privateMqPersonal->mq_status = 0;//未被使用
......@@ -346,9 +351,9 @@ int mq_close(mqd_t personal)
goto OUT_UNLOCK;
}
if ((mqueueCB->unlinkflag == TRUE) && (mqueueCB->mq_personal == NULL)) {//没有引用,且没有链接时可以删除队列
ret = DoMqueueDelete(mqueueCB);//
}
if ((mqueueCB->unlinkflag == TRUE) && (mqueueCB->mq_personal == NULL)) {//执行过unlink且没有进程在使用队列了
ret = DoMqueueDelete(mqueueCB);//删除消息队列,注意:mq_close的主要任务并不是想做这步操作的,mq_unlink才是真正想做这步操作
}//一定要明白 mq_close 和 mq_unlink 的区别
OUT_UNLOCK:
(VOID)pthread_mutex_unlock(&g_mqueueMutex);
return ret;
......@@ -449,9 +454,9 @@ int mq_unlink(const char *mqName)
}
if (mqueueCB->mq_personal != NULL) {//引用计数不为0,不删除.
mqueueCB->unlinkflag = TRUE;//执行了
mqueueCB->unlinkflag = TRUE;//标记执行过unlink了,后面再打开这个消息队列就会失败
} else {
ret = DoMqueueDelete(mqueueCB);//只有当引用计数为0时,才删除该消息队列
ret = DoMqueueDelete(mqueueCB);//只有当引用计数为0时,即所有打开的进程都执行了mq_close操作,才删除该消息队列
}
(VOID)pthread_mutex_unlock(&g_mqueueMutex);
......@@ -545,7 +550,7 @@ ERROUT_UNLOCK:
ERROUT:
return -1;
}
//定时接收消息
//posix ipc 标准接口之定时接收消息
ssize_t mq_timedreceive(mqd_t personal, char *msg, size_t msgLen, unsigned int *msgPrio,
const struct timespec *absTimeout)
{
......@@ -589,7 +594,7 @@ ssize_t mq_timedreceive(mqd_t personal, char *msg, size_t msgLen, unsigned int *
mqueueID = mqueueCB->mq_id;
(VOID)pthread_mutex_unlock(&g_mqueueMutex);
err = LOS_QueueReadCopy(mqueueID, (VOID *)msg, &receiveLen, (UINT32)absTicks);
err = LOS_QueueReadCopy(mqueueID, (VOID *)msg, &receiveLen, (UINT32)absTicks);//读消息队列
if (map_errno(err) == ENOERR) {
return (ssize_t)receiveLen;
} else {
......
......@@ -185,7 +185,7 @@ STATIC INLINE UINT32 OsCvtProtFlagsToRegionFlags(unsigned long prot, unsigned lo
{
UINT32 regionFlags = 0;
regionFlags |= VM_MAP_REGION_FLAG_PERM_USER; //必须得是可用区先
regionFlags |= VM_MAP_REGION_FLAG_PERM_USER; //必须是可用区
regionFlags |= (prot & PROT_READ) ? VM_MAP_REGION_FLAG_PERM_READ : 0; //映射区可被读
regionFlags |= (prot & PROT_WRITE) ? VM_MAP_REGION_FLAG_PERM_WRITE : 0; //映射区可被写
regionFlags |= (prot & PROT_EXEC) ? VM_MAP_REGION_FLAG_PERM_EXECUTE : 0; //映射区可被执行
......@@ -262,13 +262,13 @@ STATIC INLINE BOOL LOS_IsUserAddress(VADDR_T vaddr)
return ((vaddr >= USER_ASPACE_BASE) &&
(vaddr <= (USER_ASPACE_BASE + (USER_ASPACE_SIZE - 1))));
}
//从vaddr 到 vaddr + len 这段虚拟地址是否在用户空间
//虚拟地址[vaddr,vaddr + len]是否在用户空间
STATIC INLINE BOOL LOS_IsUserAddressRange(VADDR_T vaddr, size_t len)
{
return (vaddr + len > vaddr) && LOS_IsUserAddress(vaddr) && (LOS_IsUserAddress(vaddr + len - 1));
}
//是否是一个动态分配地址
//是否是一个动态分配地址
STATIC INLINE BOOL LOS_IsVmallocAddress(VADDR_T vaddr)
{
return ((vaddr >= VMALLOC_START) &&
......
......@@ -333,7 +333,7 @@ QUEUE_END:
SCHEDULER_UNLOCK(intSave);
return ret;
}
//接口函数 鸿蒙 LOS_ 开头的都是可供外面调用的接口函数
//接口函数定时读取消息队列
LITE_OS_SEC_TEXT UINT32 LOS_QueueReadCopy(UINT32 queueID,
VOID *bufferAddr,
UINT32 *bufferSize,
......@@ -347,10 +347,10 @@ LITE_OS_SEC_TEXT UINT32 LOS_QueueReadCopy(UINT32 queueID,
return ret;
}
operateType = OS_QUEUE_OPERATE_TYPE(OS_QUEUE_READ, OS_QUEUE_HEAD);//意思是从头开始读
return OsQueueOperate(queueID, operateType, bufferAddr, bufferSize, timeout);//执行读操作
operateType = OS_QUEUE_OPERATE_TYPE(OS_QUEUE_READ, OS_QUEUE_HEAD);//从头开始读
return OsQueueOperate(queueID, operateType, bufferAddr, bufferSize, timeout);//定时执行读操作
}
//接口函数 从队列头开始写
//接口函数从队列头开始写
LITE_OS_SEC_TEXT UINT32 LOS_QueueWriteHeadCopy(UINT32 queueID,
VOID *bufferAddr,
UINT32 bufferSize,
......
/*
* Copyright (c) 2013-2019, Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020, Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef HM_LITEIPC_H
#define HM_LITEIPC_H
#include "sys/ioctl.h"
#include "los_config.h"
#include "los_task_pri.h"
#include "los_typedef.h"
#include "los_vm_map.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
#define LITEIPC_DRIVER "/dev/lite_ipc"
#define DRIVER_MODE 0666
#define MAX_SERVICE_NUM LOSCFG_BASE_CORE_TSK_LIMIT
#define USE_TIMESTAMP YES
typedef enum {
HANDLE_NOT_USED,
HANDLE_REGISTING,
HANDLE_REGISTED
} HandleStatus;
typedef struct {
HandleStatus status;
UINT32 taskID;
UINTPTR maxMsgSize;
} HandleInfo;
typedef struct {
VOID *uvaddr;
VOID *kvaddr;
UINT32 poolSize;
} IpcPool;
typedef struct {
IpcPool pool;
UINT32 ipcTaskID;
UINT32 access[LOSCFG_BASE_CORE_TSK_LIMIT];
} ProcIpcInfo;
typedef enum {
OBJ_FD,
OBJ_PTR,
OBJ_SVC
} ObjType;
typedef struct {
UINT32 buffSz;
VOID *buff;
} BuffPtr;
typedef struct {
UINT32 handle;
UINT32 token;
UINT32 cookie;
} SvcIdentity;
typedef union {
UINT32 fd;
BuffPtr ptr;
SvcIdentity svc;
} ObjContent;
typedef struct {
ObjType type;
ObjContent content;
} SpecialObj;
typedef enum {
MT_REQUEST,
MT_REPLY,
MT_FAILED_REPLY,
MT_DEATH_NOTIFY,
MT_NUM
} MsgType;
/* lite ipc ioctl */
#define IPC_IOC_MAGIC 'i'
#define IPC_SET_CMS _IO(IPC_IOC_MAGIC, 1)
#define IPC_CMS_CMD _IOWR(IPC_IOC_MAGIC, 2, CmsCmdContent)
#define IPC_SET_IPC_THREAD _IO(IPC_IOC_MAGIC, 3)
#define IPC_SEND_RECV_MSG _IOWR(IPC_IOC_MAGIC, 4, IpcContent)
typedef enum {
CMS_GEN_HANDLE,
CMS_REMOVE_HANDLE,
CMS_ADD_ACCESS
} CmsCmd;
typedef struct {
CmsCmd cmd;
UINT32 taskID;
UINT32 serviceHandle;
} CmsCmdContent;
typedef enum {
LITEIPC_FLAG_DEFAULT = 0, // send and reply
LITEIPC_FLAG_ONEWAY, // send message only
} IpcFlag;
typedef struct {
MsgType type; /**< cmd type, decide the data structure below*/
SvcIdentity target; /**< serviceHandle or targetTaskId, depending on type */
UINT32 code; /**< service function code */
UINT32 flag;
#if (USE_TIMESTAMP == YES)
UINT64 timestamp;
#endif
UINT32 dataSz; /**< size of data */
VOID *data;
UINT32 spObjNum;
VOID *offsets;
UINT32 processID; /**< filled by kernel, processId of sender/reciever */
UINT32 taskID; /**< filled by kernel, taskId of sender/reciever */
#ifdef LOSCFG_SECURITY_CAPABILITY
UINT32 userID;
UINT32 gid;
#endif
} IpcMsg;
typedef struct {
IpcMsg msg;
LOS_DL_LIST listNode;
} IpcListNode;
#define SEND (1 << 0)
#define RECV (1 << 1)
#define BUFF_FREE (1 << 2)
typedef struct {
UINT32 flag; /**< size of writeData */
IpcMsg *outMsg; /**< data to send to target */
IpcMsg *inMsg; /**< data reply by target */
VOID *buffToFree;
} IpcContent;
#define IPC_THREAD_STATUS_INVAL 0x0001U
#define IPC_THREAD_STATUS_START 0x0002U
#define IPC_THREAD_STATUS_PEND 0x0004U
#define IPC_THREAD_STATUS_STOP 0x0008U
#if (LOSCFG_KERNEL_TRACE == YES)
#define LOS_TRACE_IPC 3 //IPC 对应..\kernel\include\los_trace.h TraceType 理解
typedef enum {
WRITE,
WRITE_DROP,
TRY_READ,
READ,
READ_DROP,
READ_TIMEOUT,
OPERATION_NUM
} IpcOpertion;
typedef struct {
UINT32 srcTid : 8;
UINT32 srcPid : 8;
UINT32 dstTid : 8;
UINT32 dstPid : 8;
} IdArg;
typedef struct {
UINT32 msgType : 8;
UINT32 code : 8;
UINT32 operation : 8;
UINT32 ipcStatus : 8;
} MsgArg;
typedef struct {
UINT32 idInfo;
UINT32 msgInfo;
UINT64 timestamp;
} IpcTraceFrame;
#endif
/* init liteipc driver */
extern UINT32 LiteIpcInit(VOID);
/* init process liteipc memory pool */
extern UINT32 LiteIpcPoolInit(ProcIpcInfo *ipcInfo);
/* reinit process liteipc memory pool, using in fork situation */
extern UINT32 LiteIpcPoolReInit(ProcIpcInfo *childIpcInfo, const ProcIpcInfo *parentIpcInfo);
/* delete process liteipc memory pool */
extern VOID LiteIpcPoolDelete(ProcIpcInfo *ipcInfo);
/* remove service handle and send death notify */
extern VOID LiteIpcRemoveServiceHandle(LosTaskCB *taskCB);
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */
#endif
/*
* Copyright (c) 2013-2019, Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020, Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef HM_LITEIPC_H
#define HM_LITEIPC_H
#include "sys/ioctl.h"
#include "los_config.h"
#include "los_task_pri.h"
#include "los_typedef.h"
#include "los_vm_map.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
#define LITEIPC_DRIVER "/dev/lite_ipc"
#define DRIVER_MODE 0666
#define MAX_SERVICE_NUM LOSCFG_BASE_CORE_TSK_LIMIT
#define USE_TIMESTAMP YES
typedef enum {
HANDLE_NOT_USED,
HANDLE_REGISTING,
HANDLE_REGISTED
} HandleStatus;
typedef struct {//句柄信息
HandleStatus status; //状态
UINT32 taskID; //任务ID,以任务当句柄
UINTPTR maxMsgSize;//最大消息大小
} HandleInfo;
typedef struct {
VOID *uvaddr;
VOID *kvaddr;
UINT32 poolSize;
} IpcPool;
typedef struct {
IpcPool pool;
UINT32 ipcTaskID;
UINT32 access[LOSCFG_BASE_CORE_TSK_LIMIT];
} ProcIpcInfo;
typedef enum {
OBJ_FD,
OBJ_PTR,
OBJ_SVC
} ObjType;
typedef struct {
UINT32 buffSz;
VOID *buff;
} BuffPtr;
typedef struct {
UINT32 handle;
UINT32 token;
UINT32 cookie;
} SvcIdentity;
typedef union {
UINT32 fd;
BuffPtr ptr;
SvcIdentity svc;
} ObjContent;
typedef struct {
ObjType type;
ObjContent content;
} SpecialObj;
typedef enum {
MT_REQUEST,
MT_REPLY,
MT_FAILED_REPLY,
MT_DEATH_NOTIFY,
MT_NUM
} MsgType;
/* lite ipc ioctl */
#define IPC_IOC_MAGIC 'i'
#define IPC_SET_CMS _IO(IPC_IOC_MAGIC, 1)
#define IPC_CMS_CMD _IOWR(IPC_IOC_MAGIC, 2, CmsCmdContent)
#define IPC_SET_IPC_THREAD _IO(IPC_IOC_MAGIC, 3)
#define IPC_SEND_RECV_MSG _IOWR(IPC_IOC_MAGIC, 4, IpcContent)
typedef enum {
CMS_GEN_HANDLE,
CMS_REMOVE_HANDLE,
CMS_ADD_ACCESS
} CmsCmd;
typedef struct {
CmsCmd cmd;
UINT32 taskID;
UINT32 serviceHandle;
} CmsCmdContent;
typedef enum {
LITEIPC_FLAG_DEFAULT = 0, // send and reply
LITEIPC_FLAG_ONEWAY, // send message only
} IpcFlag;
typedef struct {
MsgType type; /**< cmd type, decide the data structure below*/
SvcIdentity target; /**< serviceHandle or targetTaskId, depending on type */
UINT32 code; /**< service function code */
UINT32 flag;
#if (USE_TIMESTAMP == YES)
UINT64 timestamp;
#endif
UINT32 dataSz; /**< size of data */
VOID *data;
UINT32 spObjNum;
VOID *offsets;
UINT32 processID; /**< filled by kernel, processId of sender/reciever */
UINT32 taskID; /**< filled by kernel, taskId of sender/reciever */
#ifdef LOSCFG_SECURITY_CAPABILITY
UINT32 userID;
UINT32 gid;
#endif
} IpcMsg;
typedef struct {
IpcMsg msg;
LOS_DL_LIST listNode;
} IpcListNode;
#define SEND (1 << 0)
#define RECV (1 << 1)
#define BUFF_FREE (1 << 2)
typedef struct {
UINT32 flag; /**< size of writeData */
IpcMsg *outMsg; /**< data to send to target */
IpcMsg *inMsg; /**< data reply by target */
VOID *buffToFree;
} IpcContent;
#define IPC_THREAD_STATUS_INVAL 0x0001U
#define IPC_THREAD_STATUS_START 0x0002U
#define IPC_THREAD_STATUS_PEND 0x0004U
#define IPC_THREAD_STATUS_STOP 0x0008U
#if (LOSCFG_KERNEL_TRACE == YES)
#define LOS_TRACE_IPC 3 //IPC 对应..\kernel\include\los_trace.h TraceType 理解
typedef enum {
WRITE,
WRITE_DROP,
TRY_READ,
READ,
READ_DROP,
READ_TIMEOUT,
OPERATION_NUM
} IpcOpertion;
typedef struct {
UINT32 srcTid : 8;
UINT32 srcPid : 8;
UINT32 dstTid : 8;
UINT32 dstPid : 8;
} IdArg;
typedef struct {
UINT32 msgType : 8;
UINT32 code : 8;
UINT32 operation : 8;
UINT32 ipcStatus : 8;
} MsgArg;
typedef struct {
UINT32 idInfo;
UINT32 msgInfo;
UINT64 timestamp;
} IpcTraceFrame;
#endif
/* init liteipc driver */
extern UINT32 LiteIpcInit(VOID);
/* init process liteipc memory pool */
extern UINT32 LiteIpcPoolInit(ProcIpcInfo *ipcInfo);
/* reinit process liteipc memory pool, using in fork situation */
extern UINT32 LiteIpcPoolReInit(ProcIpcInfo *childIpcInfo, const ProcIpcInfo *parentIpcInfo);
/* delete process liteipc memory pool */
extern VOID LiteIpcPoolDelete(ProcIpcInfo *ipcInfo);
/* remove service handle and send death notify */
extern VOID LiteIpcRemoveServiceHandle(LosTaskCB *taskCB);
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */
#endif
......@@ -176,7 +176,7 @@ static int UserPoll(struct pollfd *fds, nfds_t nfds, int timeout)
free(pollFds);
return ret;
}
//复杂一个文件描述符
static int FcntlDupFd(int fd, void *arg, int (*fcntl)(int, int, ...))
{
int ret;
......@@ -776,7 +776,25 @@ int SysIoctl(int fd, int req, void *arg)
}
return ret;
}
//文件控制
/********************************************************
用来修改已经打开文件的属性的函数包含5个功能:
1.复制一个已有文件描述符,功能和dup和dup2相同,对应的cmd:F_DUPFD、F_DUPFD_CLOEXEC。
当使用这两个cmd时,需要传入第三个参数,fcntl返回复制后的文件描述符,此返回值是之前未被占用的描述符,
并且必须一个大于等于第三个参数值。
F_DUPFD命令要求返回的文件描述符会清除对应的FD_CLOEXEC
F_DUPFD_CLOEXEC要求设置新描述符的FD_CLOEXEC标志。
2.获取、设置文件描述符标志,对应的cmd:F_GETFD、F_SETFD。
用于设置FD_CLOEXEC标志,此标志的含义是:当进程执行exec系统调用后此文件描述符会被自动关闭。
3.获取、设置文件访问状态标志,对应的cmd:F_GETFL、F_SETFL。
获取当前打开文件的访问标志,设置对应的访问标志,一般常用来设置做非阻塞读写操作。
4.获取、设置记录锁功能,对应的cmd:F_GETLK、F_SETLK、F_SETLKW。
5.获取、设置异步I/O所有权,对应的cmd:F_GETOWN、F_SETOWN。
获取和设置用来接收SIGIO/SIGURG信号的进程id或者进程组id。返回对应的进程id或者进程组id取负值。
********************************************************/
int SysFcntl(int fd, int cmd, void *arg)
{
/* Process fd convert to system global fd */
......@@ -791,23 +809,38 @@ int SysFcntl(int fd, int cmd, void *arg)
}
return ret;
}
//创建管道
/********************************************************
管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。
调用pipe系统函数即可创建一个管道。有如下特质:
1. 其本质是一个伪文件(实为内核缓冲区)
2. 由两个文件描述符引用,一个表示读端,一个表示写端。
3. 规定数据从管道的写端流入管道,从读端流出。
管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。
管道的局限性:
① 数据自己读不能自己写。
② 数据一旦被读走,便不在管道中存在,不可反复读取。
③ 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。
④ 只能在有公共祖先的进程间使用管道。
常见的通信方式有,单工通信、半双工通信、全双工通信。
********************************************************/
int SysPipe(int pipefd[2]) /* 2 : pipe fds for read and write */
{
int ret;
int pipeFdIntr[2] = {0}; /* 2 : pipe fds for read and write */
int procFd0 = AllocProcessFd();
int procFd0 = AllocProcessFd();//读端管道,像 stdin对应标准输入
if (procFd0 < 0) {
return -EMFILE;
}
int procFd1 = AllocProcessFd();
int procFd1 = AllocProcessFd();//写端管道,像 stdout对应标准输出
if (procFd1 < 0) {
FreeProcessFd(procFd0);
return -EMFILE;
}
ret = pipe(pipeFdIntr);
ret = pipe(pipeFdIntr);//创建管道
if (ret < 0) {
FreeProcessFd(procFd0);
FreeProcessFd(procFd1);
......@@ -816,13 +849,13 @@ int SysPipe(int pipefd[2]) /* 2 : pipe fds for read and write */
int sysPipeFd0 = pipeFdIntr[0];
int sysPipeFd1 = pipeFdIntr[1];
AssociateSystemFd(procFd0, sysPipeFd0);
AssociateSystemFd(procFd0, sysPipeFd0);//进程FD和系统FD绑定
AssociateSystemFd(procFd1, sysPipeFd1);
pipeFdIntr[0] = procFd0;
pipeFdIntr[1] = procFd1;
ret = LOS_ArchCopyToUser(pipefd, pipeFdIntr, sizeof(pipeFdIntr));
ret = LOS_ArchCopyToUser(pipefd, pipeFdIntr, sizeof(pipeFdIntr));//参数都走两个进程FD
if (ret != 0) {
FreeProcessFd(procFd0);
FreeProcessFd(procFd1);
......@@ -832,15 +865,17 @@ int SysPipe(int pipefd[2]) /* 2 : pipe fds for read and write */
}
return ret;
}
//按指定条件复制文件描述字
/********************************************************
/复制文件描述符
********************************************************/
int SysDup2(int fd1, int fd2)
{
int ret;
int sysfd1 = GetAssociatedSystemFd(fd1);
int sysfd2 = GetAssociatedSystemFd(fd2);
//检查参数是否有效,注意:socket fd不支持dup2
/* Check if the param is valid, note that: socket fd is not support dup2 */
if ((sysfd1 < 0) || (sysfd1 >= CONFIG_NFILE_DESCRIPTORS) || (CheckProcessFd(fd2) != OK)) {
if ((sysfd1 < 0) || (sysfd1 >= CONFIG_NFILE_DESCRIPTORS) || (CheckProcessFd(fd2) != OK)) {//socket的fd必大于CONFIG_NFILE_DESCRIPTORS
return -EBADF;
}
......
......@@ -38,7 +38,7 @@
#include "los_signal.h"
#include "los_strncpy_from_user.h"
/********************************************************
本文说明:系统调用|IPC
本文说明:包含消息队列和信号两部分内容,是实现IPC的其中两种方式
IPC(Inter-Process Communication,进程间通信)
每个进程各自有不同的用户地址空间,进程之间地址保护,相互隔离,任何一个进程的信息在另一个进程中都看不到,
所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,
......@@ -84,7 +84,12 @@ int SysMqClose(mqd_t personal)
}
return ret;
}
/******************************************************
封装posix的标准接口,获取和设置消息队列的属性
new:来判断是否是获取还是设置功能
new==null 获取
否则为设置
******************************************************/
int SysMqGetSetAttr(mqd_t mqd, const struct mq_attr *new, struct mq_attr *old)
{
int ret;
......@@ -108,7 +113,14 @@ int SysMqGetSetAttr(mqd_t mqd, const struct mq_attr *new, struct mq_attr *old)
}
return ret;
}
/******************************************************
从内核中删除名为mqName的消息队列
如果该函数被调用了,但是仍然有进程已经打开了这个消息队列,那么这个消息队列
的销毁会被推迟到所有的引用都被关闭时执行.并且函数 mq_unlink() 不需要阻塞
到所有的引用都被关闭为止,它会立即返回.函数 mq_unlink()调用成功后, 如果在
随后调用 mq_open() 时重用这个消息队列名字,效果就像这个名字的消息队列不存在,
如果没有设置O_CREAT标志,函数mq_open() 会返回失败,否则会创建一个新的消息队列.
******************************************************/
int SysMqUnlink(const char *mqName)
{
int ret;
......@@ -126,7 +138,9 @@ int SysMqUnlink(const char *mqName)
}
return ret;
}
/******************************************************
定时时间发送消息,任务将被阻塞,等待被唤醒写入消息
******************************************************/
int SysMqTimedSend(mqd_t personal, const char *msg, size_t msgLen, unsigned int msgPrio,
const struct timespec *absTimeout)
{
......@@ -152,14 +166,16 @@ int SysMqTimedSend(mqd_t personal, const char *msg, size_t msgLen, unsigned int
free(msgIntr);
return -EFAULT;
}
ret = mq_timedsend(personal, msgIntr, msgLen, msgPrio, absTimeout ? &timeout : NULL);
ret = mq_timedsend(personal, msgIntr, msgLen, msgPrio, absTimeout ? &timeout : NULL);//posix 接口的实现
free(msgIntr);
if (ret < 0) {
return -get_errno();
}
return ret;
}
/******************************************************
定时接收消息,任务将被阻塞,等待被唤醒读取
******************************************************/
ssize_t SysMqTimedReceive(mqd_t personal, char *msg, size_t msgLen, unsigned int *msgPrio,
const struct timespec *absTimeout)
{
......@@ -181,7 +197,7 @@ ssize_t SysMqTimedReceive(mqd_t personal, char *msg, size_t msgLen, unsigned int
if (msgIntr == NULL) {
return -ENOMEM;
}
receiveLen = mq_timedreceive(personal, msgIntr, msgLen, &kMsgPrio, absTimeout ? &timeout : NULL);
receiveLen = mq_timedreceive(personal, msgIntr, msgLen, &kMsgPrio, absTimeout ? &timeout : NULL);//posix 接口的实现
if (receiveLen < 0) {
free(msgIntr);
return -get_errno();
......
git add -A
git commit -m '完善虚拟内存模块注解
git commit -m '字符图说明posix消息队列内在运行逻辑
搜索 @note_pic 可以查看全部字符图
搜索 @note_why 是注者尚未看明白的地方,如果您看明白了,请告诉注者完善
搜索 @note_thinking 是注者的思考和吐槽的地方
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册