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

1.注解系统调用是如何发生的 2.完善对消息队列的注解

搜索 @note_pic 可以查看全部字符图
搜索 @note_why 是注者尚未看明白的地方,如果您看明白了,请告诉注者完善
搜索 @note_thinking 是注者的思考和吐槽的地方
上级 169574a2
......@@ -944,7 +944,7 @@ STATIC VOID OsCheckCpuStatus(UINTPTR taskStackPointer)
g_currHandleExcCpuID = ArchCurrCpuid();
#endif
}
//执行期间的优先处置 excBufAddr为CPU硬件上下文,
//执行期间的优先处理 excBufAddr为CPU异常上下文,
LITE_OS_SEC_TEXT VOID STATIC OsExcPriorDisposal(ExcContext *excBufAddr)
{
#if (LOSCFG_KERNEL_SMP == YES)
......
......@@ -194,7 +194,7 @@ _osExceptSwiHdl:
MOV FP, #0 @ Init frame pointer
CPSIE I
BLX OsArmA32SyscallHandle
BLX OsArmA32SyscallHandle /*处理系统调用*/
CPSID I
POP_FPU_REGS R1
......
......@@ -67,24 +67,24 @@ extern "C" {
/* CONSTANTS */
#define MQ_USE_MAGIC 0x89abcdef
#define MQ_USE_MAGIC 0x89abcdef //消息队列的魔法数字
/* not suppurt prio */
#define MQ_PRIO_MAX 1
/* TYPE DEFINITIONS */
struct mqarray {
UINT32 mq_id : 31;
UINT32 unlinkflag : 1;
char *mq_name;
LosQueueCB *mqcb;
struct mqpersonal *mq_personal;
struct mqarray { //消息队列管理的实体结构
UINT32 mq_id : 31; //消息队列ID
UINT32 unlinkflag : 1; //链接标记
char *mq_name; //消息队列的名称
LosQueueCB *mqcb; //内核消息队列控制块
struct mqpersonal *mq_personal; //
};
struct mqpersonal {
struct mqarray *mq_posixdes;
struct mqpersonal *mq_next;
struct mqpersonal *mq_next; //下一个
int mq_flags;
UINT32 mq_status;
UINT32 mq_status; //状态,初始为魔法数字 MQ_USE_MAGIC
};
/**
......@@ -100,9 +100,9 @@ struct mq_attr {
/**
* @ingroup mqueue
* Handle type of a message queue //消息队列的句柄类型
* Handle type of a message queue
*/
typedef UINTPTR mqd_t;
typedef UINTPTR mqd_t;//被称为消息队列描述符,也称消息队列句柄,其本质就是一个数字凭证
/**
* @ingroup mqueue
......
......@@ -43,11 +43,14 @@
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
/****************************************************
本文说明:鸿蒙对POSIX消息队列各接口的实现
****************************************************/
#define FNONBLOCK O_NONBLOCK
/* GLOBALS */
STATIC struct mqarray g_queueTable[LOSCFG_BASE_IPC_QUEUE_LIMIT];//posix ipc 队列全局表
STATIC struct mqarray g_queueTable[LOSCFG_BASE_IPC_QUEUE_LIMIT];//消息队列内存池,可等同进程池,任务池理解.
STATIC pthread_mutex_t g_mqueueMutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
/* LOCAL FUNCTIONS */
......@@ -234,7 +237,7 @@ STATIC struct mqpersonal *DoMqueueOpen(struct mqarray *mqueueCB, INT32 openFlag)
ERROUT:
return (struct mqpersonal *)-1;
}
//posix ipc 标准接口之 创建或打开消息队列,参数是可变参数
//创建或打开一个消息队列,参数是可变参数,返回值是mqd_t类型的值,被称为消息队列描述符
mqd_t mq_open(const char *mqName, int openFlag, ...)
{
struct mqarray *mqueueCB = NULL;
......
......@@ -570,7 +570,7 @@ STATIC INLINE VOID OsTaskSyncDestroy(UINT32 syncSignal)
#endif
}
/******************************************
等待任务的同步信号,
等待任务的同步信号,
A --发送syncSignal-- > B
B --回一个syncSignal-- > A
如此A就知道B此时还在
......@@ -587,7 +587,7 @@ LITE_OS_SEC_TEXT UINT32 OsTaskSyncWait(const LosTaskCB *taskCB)
* triggered right at the timeout has reached, we set the timeout as double
* of the gc peroid.
*/
if (LOS_SemPend(taskCB->syncSignal, OS_MP_GC_PERIOD * 2) != LOS_OK) {//发送同步信号
if (LOS_SemPend(taskCB->syncSignal, OS_MP_GC_PERIOD * 2) != LOS_OK) {//发送同步信号
ret = LOS_ERRNO_TSK_MP_SYNC_FAILED;
}
......@@ -1364,7 +1364,7 @@ LITE_OS_SEC_TEXT_MINOR UINT32 LOS_CurTaskPriSet(UINT16 taskPrio)
* taskStatus --- task status
* timeOut --- Expiry time
* Return : LOS_OK on success or LOS_NOK on failure
*///任务等待,将当前任务挂到参数 list上
*/ //任务等待,将当前任务挂到参数 list上
UINT32 OsTaskWait(LOS_DL_LIST *list, UINT32 timeout, BOOL needSched)
{
LosTaskCB *runTask = NULL;
......@@ -1381,7 +1381,7 @@ UINT32 OsTaskWait(LOS_DL_LIST *list, UINT32 timeout, BOOL needSched)
}
if (needSched == TRUE) {//是否需要调度
OsSchedResched();//申请调度,里面直接切换了任务上下文,此次任务不再往下执行了.
OsSchedResched();//申请调度,里面直接切换了任务上下文,至此任务不再往下执行了.
if (runTask->taskStatus & OS_TASK_STATUS_TIMEOUT) {//这条语句是被调度再次选中时执行的,和上面的语句可能隔了很长时间,所以很可能已经超时了
runTask->taskStatus &= ~OS_TASK_STATUS_TIMEOUT;//如果任务有timeout的标签,那么就去掉那个标签
return LOS_ERRNO_TSK_TIMEOUT;
......
......@@ -68,13 +68,15 @@ typedef enum {
typedef struct {
UINT8 *queueHandle; /**< Pointer to a queue handle */ //指向队列句柄的指针
UINT16 queueState; /**< Queue state */ //队列状态
UINT16 queueLen; /**< Queue length */ //队列长度
UINT16 queueSize; /**< Node size */ //队列大小
UINT16 queueLen; /**< Queue length */ //队列中消息总数的上限值,由创建时确定,不再改变
UINT16 queueSize; /**< Node size */ //消息节点大小,由创建时确定,不再改变,即定义了每个消息长度的上限.
UINT32 queueID; /**< queueID */ //队列ID
UINT16 queueHead; /**< Node head */ //队列头部节点
UINT16 queueTail; /**< Node tail */ //队列尾部节点
UINT16 queueHead; /**< Node head */ //消息头节点位置(数组下标)
UINT16 queueTail; /**< Node tail */ //消息尾节点位置(数组下标)
UINT16 readWriteableCnt[OS_QUEUE_N_RW]; /**< Count of readable or writable resources, 0:readable, 1:writable */
//队列中可写或可读消息数,0表示可读,1表示可写
LOS_DL_LIST readWriteList[OS_QUEUE_N_RW]; /**< the linked list to be read or written, 0:readlist, 1:writelist */
//挂的都是等待读/写消息的任务链表,0表示读消息的链表,1表示写消息的任务链表
LOS_DL_LIST memList; /**< Pointer to the memory linked list */ //@note_why 这里尚未搞明白是啥意思 ,是共享内存吗?
} LosQueueCB;//读写队列分离
......@@ -83,13 +85,13 @@ typedef struct {
* @ingroup los_queue
* Message queue state: not in use.
*/
#define OS_QUEUE_UNUSED 0
#define OS_QUEUE_UNUSED 0 //队列没有使用
/**
* @ingroup los_queue
* Message queue state: used.
*/
#define OS_QUEUE_INUSED 1
#define OS_QUEUE_INUSED 1 //队列被使用
/**
* @ingroup los_queue
......
......@@ -45,7 +45,11 @@ extern "C" {
#endif /* __cplusplus */
/********************************************
https://www.cnblogs.com/hoys/archive/2012/08/19/2646377.html
信号本质:用于进程之间的异步通信
信号是Linux系统中用于进程间互相通信或者操作的一种机制,信号可以在任何时候发给某一进程,而无需知道该进程的状态。
如果该进程当前并未处于执行状态,则该信号就由内核保存起来,直到该进程被调度执行并传递给它为止。
如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程。
软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。在软件层次上是对中断机制的一种模拟,
在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是进程间通信机制中唯一
的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。
......@@ -181,7 +185,7 @@ struct sigpendq {
};
typedef struct sigpendq sigpendq_t;
struct sq_queue_s {
struct sq_queue_s {//信号队列
sq_entry_t *head;
sq_entry_t *tail;
};
......@@ -204,19 +208,19 @@ typedef struct {//任务中断上下文
typedef struct {//信号切换上下文
TASK_IRQ_CONTEXT
unsigned int R7;
unsigned int count;
unsigned int R7; //存放系统调用的ID
unsigned int count; //信号上下文的数量
} sig_switch_context;
typedef struct {//信号控制块(描述符)
sigset_t sigFlag;
sigset_t sigPendFlag;
sigset_t sigPendFlag; //信号阻塞标签集,记录因哪些信号被阻塞
sigset_t sigprocmask; /* Signals that are blocked */ //进程屏蔽了哪些信号
sq_queue_t sigactionq; //信号捕捉队列
LOS_DL_LIST waitList; //等待链表,上面挂的可是等待信号到来的任务, 请查找 OsTaskWait(&sigcb->waitList, timeout, TRUE) 理解
sigset_t sigwaitmask; /* Waiting for pending signals */ //等待挂起的信号,意思就是位信号来了都要处理,比如 SIGKILL,SIGSTOP信号
sigset_t sigwaitmask; /* Waiting for pending signals */ //任务在等待某某信号的掩码
siginfo_t sigunbinfo; /* Signal info when task unblocked */ //任务解除阻止时的信号信息
sig_switch_context context; //信号切换上下文,用于保存切换现场
sig_switch_context context; //信号切换上下文, 用于保存切换现场, 比如发生系统调用时的返回,涉及同一个任务的两个栈进行切换
} sig_cb;
#define SIGEV_THREAD_ID 4
......
......@@ -41,20 +41,37 @@
extern "C" {
#endif
#endif /* __cplusplus */
/************************************************
https://support.huaweicloud.com/kernelmanual-LiteOS/zh-cn_topic_0145350145.html
队列又称消息队列,是一种常用于任务间通信的数据结构,实现了接收来自任务或中断的不固定长度的消息,
并根据不同的接口选择传递消息是否存放在自己空间。任务能够从队列里面读取消息,当队列中的消息是空时,
挂起读取任务;当队列中有新消息时,挂起的读取任务被唤醒并处理新消息。
用户在处理业务时,消息队列提供了异步处理机制,允许将一个消息放入队列,但并不立即处理它,同时队列还能起到缓冲消息作用。
鸿蒙 LiteOS中使用队列数据结构实现任务异步通信工作,具有如下特性:
消息以先进先出方式排队,支持异步读写工作方式。
读队列和写队列都支持超时机制。
发送消息类型由通信双方约定,可以允许不同长度(不超过队列节点最大值)消息。
一个任务能够从任意一个消息队列接收和发送消息。
多个任务能够从同一个消息队列接收和发送消息。
当队列使用结束后,如果是动态申请的内存,需要通过释放内存函数回收。
************************************************/
#if (LOSCFG_BASE_IPC_QUEUE == YES)
#if (LOSCFG_BASE_IPC_QUEUE_LIMIT <= 0)
#error "queue maxnum cannot be zero"
#endif /* LOSCFG_BASE_IPC_QUEUE_LIMIT <= 0 */
LITE_OS_SEC_BSS LosQueueCB *g_allQueue = NULL;//管理所有IPC队列
LITE_OS_SEC_BSS STATIC LOS_DL_LIST g_freeQueueList;//IPC空闲队列链表,管分配的,需要队列就从free中申请
LITE_OS_SEC_BSS LosQueueCB *g_allQueue = NULL;//消息队列池
LITE_OS_SEC_BSS STATIC LOS_DL_LIST g_freeQueueList;//空闲队列链表,管分配的,需要队列从这里申请
/*
* Description : queue initial
* Return : LOS_OK on success or error code on failure
*/
LITE_OS_SEC_TEXT_INIT UINT32 OsQueueInit(VOID)//队列初始化
LITE_OS_SEC_TEXT_INIT UINT32 OsQueueInit(VOID)//消息队列模块初始化
{
LosQueueCB *queueNode = NULL;
UINT32 index;
......@@ -68,18 +85,18 @@ LITE_OS_SEC_TEXT_INIT UINT32 OsQueueInit(VOID)//队列初始化
}
(VOID)memset_s(g_allQueue, size, 0, size);//清0
LOS_ListInit(&g_freeQueueList);//初始化空闲链表
for (index = 0; index < LOSCFG_BASE_IPC_QUEUE_LIMIT; index++) {//循环
queueNode = ((LosQueueCB *)g_allQueue) + index;//取item
queueNode->queueID = index;//记录队列index
LOS_ListTailInsert(&g_freeQueueList, &queueNode->readWriteList[OS_QUEUE_WRITE]);//挂入空闲队列链表上
for (index = 0; index < LOSCFG_BASE_IPC_QUEUE_LIMIT; index++) {//循环初始化每个消息队列
queueNode = ((LosQueueCB *)g_allQueue) + index;//一个一个来
queueNode->queueID = index;//这可是 队列的身份证
LOS_ListTailInsert(&g_freeQueueList, &queueNode->readWriteList[OS_QUEUE_WRITE]);//通过写节点挂到空闲队列链表上
}//这里要注意是用 readWriteList 挂到 g_freeQueueList链上的,所以要通过 GET_QUEUE_LIST 来找到 LosQueueCB
if (OsQueueDbgInitHook() != LOS_OK) {
if (OsQueueDbgInitHook() != LOS_OK) {//调试队列使用的.
return LOS_ERRNO_QUEUE_NO_MEMORY;
}
return LOS_OK;
}
//创建一个队列 maxMsgSize有时会 = sizeof(CHAR *)
//创建一个队列,根据用户传入队列长度和消息节点大小来开辟相应的内存空间以供该队列使用,参数queueID带走队列ID
LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueCreate(CHAR *queueName, UINT16 len, UINT32 *queueID,
UINT32 flags, UINT16 maxMsgSize)
{
......@@ -124,9 +141,9 @@ LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueCreate(CHAR *queueName, UINT16 len, UINT32
unusedQueue = LOS_DL_LIST_FIRST(&g_freeQueueList);//找到一个没有被使用的队列
LOS_ListDelete(unusedQueue);//将自己从g_freeQueueList中摘除, unusedQueue只是个 LOS_DL_LIST 结点.
queueCB = GET_QUEUE_LIST(unusedQueue);//通过unusedQueue找到LosQueueCB
queueCB->queueLen = len; //队列长度 这个值是不会变的
queueCB->queueSize = msgSize;//消息大小 这个值也是不会变的
queueCB = GET_QUEUE_LIST(unusedQueue);//通过unusedQueue找到整个消息队列(LosQueueCB)
queueCB->queueLen = len; //队列中消息的总个数,注意这个一旦创建是不能变的.
queueCB->queueSize = msgSize;//消息节点的大小,注意这个一旦创建也是不能变的.
queueCB->queueHandle = queue; //队列句柄,队列内容存储区.
queueCB->queueState = OS_QUEUE_INUSED; //队列状态使用中
queueCB->readWriteableCnt[OS_QUEUE_READ] = 0;//可读资源计数,OS_QUEUE_READ(0):可读.
......@@ -135,7 +152,7 @@ LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueCreate(CHAR *queueName, UINT16 len, UINT32
queueCB->queueTail = 0;//队列尾节点
LOS_ListInit(&queueCB->readWriteList[OS_QUEUE_READ]);//初始化可读队列链表
LOS_ListInit(&queueCB->readWriteList[OS_QUEUE_WRITE]);//初始化可写队列链表
LOS_ListInit(&queueCB->memList);//初始化内存链表队列
LOS_ListInit(&queueCB->memList);//
OsQueueDbgUpdateHook(queueCB->queueID, OsCurrTaskGet()->taskEntry);//在创建或删除队列调试信息时更新任务条目
SCHEDULER_UNLOCK(intSave);
......@@ -150,7 +167,7 @@ STATIC LITE_OS_SEC_TEXT UINT32 OsQueueReadParameterCheck(UINT32 queueID, const V
if (GET_QUEUE_INDEX(queueID) >= LOSCFG_BASE_IPC_QUEUE_LIMIT) {//队列ID不能超上限
return LOS_ERRNO_QUEUE_INVALID;
}
if ((bufferAddr == NULL) || (bufferSize == NULL)) {//缓存地址不能为NULL
if ((bufferAddr == NULL) || (bufferSize == NULL)) {//缓存地址和大小参数判断
return LOS_ERRNO_QUEUE_READ_PTR_NULL;
}
......@@ -160,9 +177,9 @@ STATIC LITE_OS_SEC_TEXT UINT32 OsQueueReadParameterCheck(UINT32 queueID, const V
OsQueueDbgTimeUpdateHook(queueID);
if (timeout != LOS_NO_WAIT) {
if (OS_INT_ACTIVE) {
return LOS_ERRNO_QUEUE_READ_IN_INTERRUPT;
if (timeout != LOS_NO_WAIT) {//等待一定时间再读取
if (OS_INT_ACTIVE) {//如果碰上了硬中断
return LOS_ERRNO_QUEUE_READ_IN_INTERRUPT;//意思是:硬中断发生时是不能读消息队列的
}
}
return LOS_OK;
......@@ -232,7 +249,7 @@ STATIC VOID OsQueueBufferOperate(LosQueueCB *queueCB, UINT32 operateType, VOID *
}
*bufferSize = msgDataSize;//通过入参 带走消息的大小
} else {//只有读写两种操作,这里就是写队列了.写也分两步走 , @@@@@ 这里建议鸿蒙加上 OS_QUEUE_IS_WRITE 判断
} else {//只有读写两种操作,这里就是写队列了.写也分两步走 , @note_thinking 这里建议鸿蒙加上 OS_QUEUE_IS_WRITE 判断
if (memcpy_s(queueNode, queueCB->queueSize, bufferAddr, *bufferSize) != EOK) {//1.写入消息内容
PRINT_ERR("store message failed\n");//表示把外面数据写进来,所以相当于queueNode接着了bufferAddr的数据
return;
......@@ -259,13 +276,17 @@ STATIC UINT32 OsQueueOperateParamCheck(const LosQueueCB *queueCB, UINT32 queueID
}
return LOS_OK;
}
//队列操作.是读是写由operateType定
/************************************************
队列操作.是读是写由operateType定
本函数是消息队列最重要的一个函数,可以分析出读取消息过程中
发生的细节,涉及任务的唤醒和阻塞,阻塞链表任务的相互提醒.
************************************************/
UINT32 OsQueueOperate(UINT32 queueID, UINT32 operateType, VOID *bufferAddr, UINT32 *bufferSize, UINT32 timeout)
{
LosQueueCB *queueCB = NULL;
LosTaskCB *resumedTask = NULL;
UINT32 ret;
UINT32 readWrite = OS_QUEUE_READ_WRITE_GET(operateType);//获取读写操作标识
UINT32 readWrite = OS_QUEUE_READ_WRITE_GET(operateType);//获取读/写操作标识
UINT32 intSave;
SCHEDULER_LOCK(intSave);
......@@ -275,37 +296,37 @@ UINT32 OsQueueOperate(UINT32 queueID, UINT32 operateType, VOID *bufferAddr, UINT
goto QUEUE_END;
}
if (queueCB->readWriteableCnt[readWrite] == 0) {//没有数据
if (queueCB->readWriteableCnt[readWrite] == 0) {//根据readWriteableCnt判断队列是否有消息读/写
if (timeout == LOS_NO_WAIT) {//不等待直接退出
ret = OS_QUEUE_IS_READ(operateType) ? LOS_ERRNO_QUEUE_ISEMPTY : LOS_ERRNO_QUEUE_ISFULL;
goto QUEUE_END;
}
if (!OsPreemptableInSched()) {//不能抢占式调度
if (!OsPreemptableInSched()) {//不支持抢占式调度
ret = LOS_ERRNO_QUEUE_PEND_IN_LOCK;
goto QUEUE_END;
}
ret = OsTaskWait(&queueCB->readWriteList[readWrite], timeout, TRUE);//任务等待,这里很重要啊,说明下把当前任务从就绪列表摘除,
if (ret == LOS_ERRNO_TSK_TIMEOUT) {//并挂在readWriteList[readWrite]上,如此一来readWriteList[readWrite]上挂的都是task
ret = LOS_ERRNO_QUEUE_TIMEOUT;//通过OS_TCB_FROM_PENDLIST就能找到task实体
goto QUEUE_END;
//任务等待,这里很重要啊,将自己从就绪列表摘除,让出了CPU并发起了调度,并挂在readWriteList[readWrite]上,挂的都等待读/写消息的task
ret = OsTaskWait(&queueCB->readWriteList[readWrite], timeout, TRUE);//任务被唤醒后会回到这里执行,什么时候会被唤醒?当然是有消息的时候!
if (ret == LOS_ERRNO_TSK_TIMEOUT) {//唤醒后如果超时了,返回读/写消息失败
ret = LOS_ERRNO_QUEUE_TIMEOUT;
goto QUEUE_END;//
}
} else {
queueCB->readWriteableCnt[readWrite]--;//对应队列中计数器--
queueCB->readWriteableCnt[readWrite]--;//对应队列中计数器--,说明一条消息只能被读/写一次
}
OsQueueBufferOperate(queueCB, operateType, bufferAddr, bufferSize);//发起读或写队列操作
if (!LOS_ListEmpty(&queueCB->readWriteList[!readWrite])) {//另外的operateType中还有其他消息时,如果 operateType=read,这时去查write队列,读写交互操作
resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&queueCB->readWriteList[!readWrite]));//拿到拥有这个队列节点的任务 ?????
OsTaskWake(resumedTask);//唤醒任务去处理队列的值
if (!LOS_ListEmpty(&queueCB->readWriteList[!readWrite])) {//如果还有任务在排着队等待读/写入消息(当时不能读/写的原因有可能当时队列满了==)
resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&queueCB->readWriteList[!readWrite]));//取出要读/写消息的任务
OsTaskWake(resumedTask);//唤醒任务去读/写消息啊
SCHEDULER_UNLOCK(intSave);
LOS_MpSchedule(OS_MP_CPU_ALL);//让所有CPU参与调度
LOS_MpSchedule(OS_MP_CPU_ALL);//让所有CPU发出调度申请,因为很可能那个要读/写消息的队列是由其他CPU执行
LOS_Schedule();//申请调度
return LOS_OK;
} else {
queueCB->readWriteableCnt[!readWrite]++;
queueCB->readWriteableCnt[!readWrite]++;//对应队列读/写中计数器++
}
QUEUE_END:
......@@ -363,12 +384,21 @@ LITE_OS_SEC_TEXT UINT32 LOS_QueueWriteCopy(UINT32 queueID,
operateType = OS_QUEUE_OPERATE_TYPE(OS_QUEUE_WRITE, OS_QUEUE_TAIL);//从尾部开始写
return OsQueueOperate(queueID, operateType, bufferAddr, &bufferSize, timeout);//执行写操作
}
//外部接口 读一个队列数据
/********************************************************
外部接口 读一个队列数据
读队列时,根据Head找到最先写入队列中的消息节点进行读取。如果Head已经指向队列尾则采用回卷方式。
根据usReadableCnt判断队列是否有消息读取,对全部空闲(usReadableCnt为0)队列进行读队列操作会引起任务挂起。
********************************************************/
LITE_OS_SEC_TEXT UINT32 LOS_QueueRead(UINT32 queueID, VOID *bufferAddr, UINT32 bufferSize, UINT32 timeout)
{
return LOS_QueueReadCopy(queueID, bufferAddr, &bufferSize, timeout);
}
//外部接口 写一个队列数据
/********************************************************
外部接口 写一个队列数据
根据Tail找到被占用消息节点末尾的空闲节点作为数据写入对象。如果Tail已经指向队列尾则采用回卷方式。
根据usWritableCnt判断队列是否可以写入,不能对已满(usWritableCnt为0)队列进行写队列操作
********************************************************/
LITE_OS_SEC_TEXT UINT32 LOS_QueueWrite(UINT32 queueID, VOID *bufferAddr, UINT32 bufferSize, UINT32 timeout)
{
if (bufferAddr == NULL) {
......@@ -377,7 +407,11 @@ LITE_OS_SEC_TEXT UINT32 LOS_QueueWrite(UINT32 queueID, VOID *bufferAddr, UINT32
bufferSize = sizeof(CHAR *);
return LOS_QueueWriteCopy(queueID, &bufferAddr, bufferSize, timeout);
}
//外部接口 从头部写入
/********************************************************
外部接口 从头部写入
写队列时,根据Tail找到被占用消息节点末尾的空闲节点作为数据写入对象。如果Tail已经指向队列尾则采用回卷方式。
根据usWritableCnt判断队列是否可以写入,不能对已满(usWritableCnt为0)队列进行写队列操作
********************************************************/
LITE_OS_SEC_TEXT UINT32 LOS_QueueWriteHead(UINT32 queueID,
VOID *bufferAddr,
UINT32 bufferSize,
......@@ -389,7 +423,11 @@ LITE_OS_SEC_TEXT UINT32 LOS_QueueWriteHead(UINT32 queueID,
bufferSize = sizeof(CHAR *);
return LOS_QueueWriteHeadCopy(queueID, &bufferAddr, bufferSize, timeout);
}
//外部接口 删除队列,队列中有内容则不能删除.
/********************************************************
外部接口 删除队列,还有任务要读/写消息时不能删除
删除队列时,根据传入的队列ID寻找到对应的队列,把队列状态置为未使用,
释放原队列所占的空间,对应的队列控制头置为初始状态。
********************************************************/
LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueDelete(UINT32 queueID)
{
LosQueueCB *queueCB = NULL;
......@@ -402,18 +440,18 @@ LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueDelete(UINT32 queueID)
}
SCHEDULER_LOCK(intSave);
queueCB = (LosQueueCB *)GET_QUEUE_HANDLE(queueID);//拿到队列数据实体
queueCB = (LosQueueCB *)GET_QUEUE_HANDLE(queueID);//拿到队列实体
if ((queueCB->queueID != queueID) || (queueCB->queueState == OS_QUEUE_UNUSED)) {
ret = LOS_ERRNO_QUEUE_NOT_CREATE;
goto QUEUE_END;
}
if (!LOS_ListEmpty(&queueCB->readWriteList[OS_QUEUE_READ])) {//读队列还有数据
if (!LOS_ListEmpty(&queueCB->readWriteList[OS_QUEUE_READ])) {//尚有任务要读数据
ret = LOS_ERRNO_QUEUE_IN_TSKUSE;
goto QUEUE_END;
}
if (!LOS_ListEmpty(&queueCB->readWriteList[OS_QUEUE_WRITE])) {//写队列还有数据
if (!LOS_ListEmpty(&queueCB->readWriteList[OS_QUEUE_WRITE])) {//尚有任务要写数据
ret = LOS_ERRNO_QUEUE_IN_TSKUSE;
goto QUEUE_END;
}
......@@ -429,16 +467,16 @@ LITE_OS_SEC_TEXT_INIT UINT32 LOS_QueueDelete(UINT32 queueID)
goto QUEUE_END;
}
queue = queueCB->queueHandle;
queueCB->queueHandle = NULL;
queueCB->queueState = OS_QUEUE_UNUSED;
queueCB->queueID = SET_QUEUE_ID(GET_QUEUE_COUNT(queueCB->queueID) + 1, GET_QUEUE_INDEX(queueCB->queueID));
queue = queueCB->queueHandle; //队列buf
queueCB->queueHandle = NULL; //
queueCB->queueState = OS_QUEUE_UNUSED;//重置队列状态
queueCB->queueID = SET_QUEUE_ID(GET_QUEUE_COUNT(queueCB->queueID) + 1, GET_QUEUE_INDEX(queueCB->queueID));//@note_why 这里需要这样做吗?
OsQueueDbgUpdateHook(queueCB->queueID, NULL);
LOS_ListTailInsert(&g_freeQueueList, &queueCB->readWriteList[OS_QUEUE_WRITE]);//回收,通过write节点挂入链表,这里用OS_QUEUE_READ也没问题,
SCHEDULER_UNLOCK(intSave); //但从链表中取出的时候也要用OS_QUEUE_READ,成对出现就行。
LOS_ListTailInsert(&g_freeQueueList, &queueCB->readWriteList[OS_QUEUE_WRITE]);//回收,将节点挂入可分配链表,等待重新被分配再利用
SCHEDULER_UNLOCK(intSave);
ret = LOS_MemFree(m_aucSysMem1, (VOID *)queue);
ret = LOS_MemFree(m_aucSysMem1, (VOID *)queue);//释放队列句柄
return ret;
QUEUE_END:
......
......@@ -86,11 +86,11 @@ int OsTcbDispatch(LosTaskCB *stcb, siginfo_t *info)
masked = (bool)OsSigIsMember(&sigcb->sigprocmask, info->si_signo);//@note_thinking 这里还有 masked= -1的情况要处理!!!
if (masked) {//集合中已有该信号
/* If signal is in wait list and mask list, need unblock it */ //如果信号在等待列表和掩码列表中,需要解除阻止
if (!LOS_ListEmpty(&sigcb->waitList) && OsSigIsMember(&sigcb->sigwaitmask, info->si_signo)) {//等待
OsTaskWake(stcb);
OsSigEmptySet(&sigcb->sigwaitmask);
if (!LOS_ListEmpty(&sigcb->waitList) && OsSigIsMember(&sigcb->sigwaitmask, info->si_signo)) {//waitList上挂的都是task,sigwaitmask表示是否在等si_signo
OsTaskWake(stcb);//唤醒任务,从阻塞状态变成就绪态
OsSigEmptySet(&sigcb->sigwaitmask);//将sigwaitmask清空,都已经变成就绪状态了,当然就没有要等待的信号了.
} else {
OsSigAddSet(&sigcb->sigPendFlag, info->si_signo);
OsSigAddSet(&sigcb->sigPendFlag, info->si_signo);//
}
} else {//
/* unmasked signal actions */
......@@ -409,7 +409,7 @@ EXIT:
SCHEDULER_UNLOCK(intSave);
return ret;
}
//添加信号到指定信号集
int OsSigAddSet(sigset_t *set, int signo)
{
/* Verify the signal */
......@@ -419,11 +419,11 @@ int OsSigAddSet(sigset_t *set, int signo)
/* In musl, sig No bits 00000100 present sig No 3, but 1<< 3 = 00001000, so signo needs minus 1 */
signo -= 1;
/* Add the signal to the set */
*set |= SIGNO2SET((unsigned int)signo);
*set |= SIGNO2SET((unsigned int)signo);//填充信号集
return LOS_OK;
}
}
//
//获取当前进程的阻塞信号集
int OsSigPending(sigset_t *set)
{
LosTaskCB *tcb = NULL;
......@@ -435,7 +435,7 @@ int OsSigPending(sigset_t *set)
SCHEDULER_LOCK(intSave);
tcb = OsCurrTaskGet();
*set = tcb->sig.sigPendFlag;
*set = tcb->sig.sigPendFlag;//因何而被阻塞
SCHEDULER_UNLOCK(intSave);
return LOS_OK;
}
......@@ -484,7 +484,7 @@ int OsSigTimedWaitNoLock(sigset_t *set, siginfo_t *info, unsigned int timeout)
}
return ret;
}
//
//让当前任务等待的信号
int OsSigTimedWait(sigset_t *set, siginfo_t *info, unsigned int timeout)
{
int ret;
......@@ -492,12 +492,12 @@ int OsSigTimedWait(sigset_t *set, siginfo_t *info, unsigned int timeout)
SCHEDULER_LOCK(intSave);
ret = OsSigTimedWaitNoLock(set, info, timeout);
ret = OsSigTimedWaitNoLock(set, info, timeout);//以不加锁的方式等待
SCHEDULER_UNLOCK(intSave);
return ret;
}
//让当前任务暂停的信号
int OsPause(void)
{
LosTaskCB *spcb = NULL;
......@@ -507,7 +507,7 @@ int OsPause(void)
oldSigprocmask = spcb->sig.sigprocmask;
return OsSigSuspend(&oldSigprocmask);
}
//暂停信号
//让当前任务暂停的信号
int OsSigSuspend(const sigset_t *set)
{
unsigned int intSave;
......@@ -568,7 +568,9 @@ int OsSigAction(int sig, const sigaction_t *act, sigaction_t *oact)
return LOS_OK;
}
//保存信号现场
/**********************************************
产生系统调用时,也就是软中断时,保存用户栈寄存器现场信息
**********************************************/
void OsSaveSignalContext(unsigned int *sp)
{
UINTPTR sigHandler;
......@@ -581,15 +583,15 @@ void OsSaveSignalContext(unsigned int *sp)
OS_RETURN_IF_VOID(sp == NULL);
cpsr = OS_SYSCALL_GET_CPSR(sp);
OS_RETURN_IF_VOID(((cpsr & CPSR_MASK_MODE) != CPSR_USER_MODE));
OS_RETURN_IF_VOID(((cpsr & CPSR_MASK_MODE) != CPSR_USER_MODE));//必须在用户模式下保存
SCHEDULER_LOCK(intSave);
task = OsCurrTaskGet();
process = OsCurrProcessGet();
sigcb = &task->sig;
sigcb = &task->sig;//获取任务的信号控制块
if ((sigcb->context.count == 0) && ((sigcb->sigFlag != 0) || (process->sigShare != 0))) {
if ((sigcb->context.count == 0) && ((sigcb->sigFlag != 0) || (process->sigShare != 0))) {//未保存上下文且关注了信号
sigHandler = OsGetSigHandler();
if (sigHandler == 0) {
if (sigHandler == 0) {//信号没有注册
sigcb->sigFlag = 0;
process->sigShare = 0;
SCHEDULER_UNLOCK(intSave);
......@@ -597,10 +599,10 @@ void OsSaveSignalContext(unsigned int *sp)
return;
}
/* One pthread do the share signal */
sigcb->sigFlag |= process->sigShare;
sigcb->sigFlag |= process->sigShare;//记录由一个线程执行可进程的共享信号,这些恢复上下文时就找到对应的任务
unsigned int signo = (unsigned int)FindFirstSetedBit(sigcb->sigFlag) + 1;
OsProcessExitCodeSignalSet(process, signo);
sigcb->context.CPSR = cpsr;
sigcb->context.CPSR = cpsr; //保存当前各寄存器的信息
sigcb->context.PC = sp[REG_PC];
sigcb->context.USP = sp[REG_SP];
sigcb->context.ULR = sp[REG_LR];
......@@ -610,17 +612,18 @@ void OsSaveSignalContext(unsigned int *sp)
sigcb->context.R3 = sp[REG_R3];
sigcb->context.R7 = sp[REG_R7];
sigcb->context.R12 = sp[REG_R12];
sp[REG_PC] = sigHandler;
sp[REG_R0] = signo;
sp[REG_R1] = (unsigned int)(UINTPTR)(sigcb->sigunbinfo.si_value.sival_ptr);
sp[REG_PC] = sigHandler;//下一个要执行的函数,信号注册函数
sp[REG_R0] = signo; //信号注册函数参数
sp[REG_R1] = (unsigned int)(UINTPTR)(sigcb->sigunbinfo.si_value.sival_ptr); //@note_why 这里看明白,是干啥子用的?
/* sig No bits 00000100 present sig No 3, but 1<< 3 = 00001000, so signo needs minus 1 */
sigcb->sigFlag ^= 1ULL << (signo - 1);
sigcb->context.count++;
sigcb->context.count++; //代表已保存
}
SCHEDULER_UNLOCK(intSave);
}
//保存信号中断现场
//发生硬中断时,需保存用户态的用户栈现场,多了一个参数 R7寄存器
//汇编调用 见于 los_dispatch.S | 254行: BL OsSaveSignalContextIrq
void OsSaveSignalContextIrq(unsigned int *sp, unsigned int r7)
{
UINTPTR sigHandler;
......@@ -633,7 +636,7 @@ void OsSaveSignalContextIrq(unsigned int *sp, unsigned int r7)
OS_RETURN_IF_VOID(sp == NULL);
cpsr = context->CPSR;
OS_RETURN_IF_VOID(((cpsr & CPSR_MASK_MODE) != CPSR_USER_MODE));
OS_RETURN_IF_VOID(((cpsr & CPSR_MASK_MODE) != CPSR_USER_MODE));//必须在用户模式下保存用户栈信息
SCHEDULER_LOCK(intSave);
task = OsCurrTaskGet();
......@@ -651,7 +654,7 @@ void OsSaveSignalContextIrq(unsigned int *sp, unsigned int r7)
sigcb->sigFlag |= process->sigShare;
unsigned int signo = (unsigned int)FindFirstSetedBit(sigcb->sigFlag) + 1;
OsProcessExitCodeSignalSet(process, signo);
(VOID)memcpy_s(&sigcb->context.R0, sizeof(TaskIrqDataSize), &context->R0, sizeof(TaskIrqDataSize));
(VOID)memcpy_s(&sigcb->context.R0, sizeof(TaskIrqDataSize), &context->R0, sizeof(TaskIrqDataSize));//note_why 为何此处和OsSaveSignalContext的处理不一致?
sigcb->context.R7 = r7;
context->PC = sigHandler;
context->R0 = signo;
......@@ -662,7 +665,13 @@ void OsSaveSignalContextIrq(unsigned int *sp, unsigned int r7)
}
SCHEDULER_UNLOCK(intSave);
}
//恢复信号上下文
/****************************************************
恢复信号上下文,由系统调用之__NR_sigreturn产生,这是一个内部产生的系统调用.
为什么要恢复呢?
因为系统调用的执行由任务内核态完成,使用的栈也是内核栈,CPU相关寄存器记录的都是内核栈的内容,
而系统调用完成后,需返回任务的用户栈执行,这时需将CPU各寄存器回到用户态现场
所以函数的功能就变成了还原寄存器的值
****************************************************/
void OsRestorSignalContext(unsigned int *sp)
{
LosTaskCB *task = NULL; /* Do not adjust this statement */
......@@ -672,28 +681,28 @@ void OsRestorSignalContext(unsigned int *sp)
SCHEDULER_LOCK(intSave);
task = OsCurrTaskGet();
sigcb = &task->sig;
sigcb = &task->sig;//获取当前任务信号控制块
if (sigcb->context.count != 1) {
if (sigcb->context.count != 1) {//必须之前保存过,才能被恢复
SCHEDULER_UNLOCK(intSave);
PRINT_ERR("sig error count : %d\n", sigcb->context.count);
return;
}
process = OsCurrProcessGet();
sp[REG_PC] = sigcb->context.PC;
OS_SYSCALL_SET_CPSR(sp, sigcb->context.CPSR);
sp[REG_SP] = sigcb->context.USP;
sp[REG_LR] = sigcb->context.ULR;
process = OsCurrProcessGet();//获取当前进程
sp[REG_PC] = sigcb->context.PC;//指令寄存器
OS_SYSCALL_SET_CPSR(sp, sigcb->context.CPSR);//重置程序状态寄存器
sp[REG_SP] = sigcb->context.USP;//堆栈指针, USP指的是 用户态的堆栈,即将回到用户栈继续运行
sp[REG_LR] = sigcb->context.ULR;//连接寄存器
sp[REG_R0] = sigcb->context.R0;
sp[REG_R1] = sigcb->context.R1;
sp[REG_R2] = sigcb->context.R2;
sp[REG_R3] = sigcb->context.R3;
sp[REG_R7] = sigcb->context.R7;
sp[REG_R12] = sigcb->context.R12;
sigcb->context.count--;
process->sigShare = 0;
OsProcessExitCodeSignalClear(process);
sigcb->context.count--; //信号上下文的数量回到减少
process->sigShare = 0; //回到用户态,信号共享清0
OsProcessExitCodeSignalClear(process);//清空进程退出码
SCHEDULER_UNLOCK(intSave);
}
......
......@@ -46,6 +46,7 @@
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
//定义所有的系统调用的ID ..\third_party\musl\kernel\obj\include\bits\syscall.h
/**
* @ingroup los_sys
......
......@@ -37,7 +37,26 @@
#include "user_copy.h"
#include "los_signal.h"
#include "los_strncpy_from_user.h"
//创建和打开一个posix消息队列
/********************************************************
本文说明:系统调用|IPC
IPC(Inter-Process Communication,进程间通信)
每个进程各自有不同的用户地址空间,进程之间地址保护,相互隔离,任何一个进程的信息在另一个进程中都看不到,
所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,
进程B再从内核缓冲区把数据读走,
IPC实现方式之消息队列:
消息队列特点总结:
(1)消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识.
(2)消息队列允许一个或多个进程向它写入与读取消息.
(3)管道和消息队列的通信数据都是先进先出的原则。
(4)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比FIFO更有优势。
(5)消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
(6)目前主要有两种类型的消息队列:POSIX消息队列以及System V消息队列,System V消息队列是随内核持续的,
只有在内核重起或者人工删除时,该消息队列才会被删除。
鸿蒙liteos 支持POSIX消息队列并加入了一种自研的消息队列 liteipc,此处重点讲 posix消息队列
********************************************************/
//打开一个消息队列,封装了posix接口
mqd_t SysMqOpen(const char *mqName, int openFlag, mode_t mode, struct mq_attr *attr)
{
mqd_t ret;
......@@ -48,7 +67,7 @@ mqd_t SysMqOpen(const char *mqName, int openFlag, mode_t mode, struct mq_attr *a
if (retValue < 0) {
return retValue;
}
ret = mq_open(kMqName, openFlag, mode, attr);
ret = mq_open(kMqName, openFlag, mode, attr);//posix
if (ret == -1) {
return (mqd_t)-get_errno();
}
......@@ -251,7 +270,7 @@ int SysSigTimedWait(const sigset_t_l *setl, siginfo_t *info, const struct timesp
}
return (ret == 0 ? infoIntr.si_signo : ret);
}
//IPC系统调用之暂停任务
int SysPause(void)
{
return OsPause();
......
......@@ -65,20 +65,21 @@
#endif
#include "sys/shm.h"
#define SYS_CALL_NUM (__NR_syscallend + 1)
#define NARG_BITS 4
#define NARG_MASK 0x0F
#define NARG_PER_BYTE 2
typedef UINT32 (*SyscallFun1)(UINT32);
typedef UINT32 (*SyscallFun3)(UINT32, UINT32, UINT32);
//500以后是LiteOS自定义的系统调用,与ARM EABI不兼容
#define SYS_CALL_NUM (__NR_syscallend + 1) // __NR_syscallend = 500 + customized syscalls
#define NARG_BITS 4 //参数占用位,这里可以看出系统调用的最大参数个数为 2^4 = 16个参数.
#define NARG_MASK 0x0F//参数掩码
#define NARG_PER_BYTE 2 //每个字节两个参数
typedef UINT32 (*SyscallFun1)(UINT32);//一个参数的注册函数
typedef UINT32 (*SyscallFun3)(UINT32, UINT32, UINT32);//1,3,5,7 代表参数的个数
typedef UINT32 (*SyscallFun5)(UINT32, UINT32, UINT32, UINT32, UINT32);
typedef UINT32 (*SyscallFun7)(UINT32, UINT32, UINT32, UINT32, UINT32, UINT32, UINT32);
static UINTPTR g_syscallHandle[SYS_CALL_NUM] = {0};
static UINT8 g_syscallNArgs[(SYS_CALL_NUM + 1) / NARG_PER_BYTE] = {0};
static UINTPTR g_syscallHandle[SYS_CALL_NUM] = {0}; //系统调用入口函数注册
static UINT8 g_syscallNArgs[(SYS_CALL_NUM + 1) / NARG_PER_BYTE] = {0};//保存系统调用对应的参数数量
//系统调用初始化,完成对系统调用的注册
void SyscallHandleInit(void)
{
#define SYSCALL_HAND_DEF(id, fun, rType, nArg) \
......@@ -92,40 +93,46 @@ void SyscallHandleInit(void)
}
/* The SYSCALL ID is in R7 on entry. Parameters follow in R0..R6 */
/******************************************************************
由汇编调用,见于 los_hw_exc.s 第197行 BLX OsArmA32SyscallHandle
SYSCALL是产生系统调用时触发的信号,R7寄存器存放具体的系统调用ID,也叫系统调用号
regs:参数就是所有寄存器
注意:本函数在用户态和内核态下都可能被调用到
******************************************************************/
LITE_OS_SEC_TEXT UINT32 *OsArmA32SyscallHandle(UINT32 *regs)
{
UINT32 ret;
UINT8 nArgs;
UINTPTR handle;
UINT32 cmd = regs[REG_R7];
UINT32 cmd = regs[REG_R7];//C7寄存器记录了触发了具体哪个系统调用
if (cmd >= SYS_CALL_NUM) {
if (cmd >= SYS_CALL_NUM) {//系统调用的总数
PRINT_ERR("Syscall ID: error %d !!!\n", cmd);
return regs;
}
if (cmd == __NR_sigreturn) {
OsRestorSignalContext(regs);
if (cmd == __NR_sigreturn) {//此时运行在内核栈,程序返回的调用,从内核态返回用户态时触发
OsRestorSignalContext(regs);//恢复信号上下文,执行完函数后,切到了用户栈
return regs;
}
handle = g_syscallHandle[cmd];
handle = g_syscallHandle[cmd];//拿到系统调用的注册函数,类似 SysRead
nArgs = g_syscallNArgs[cmd / NARG_PER_BYTE]; /* 4bit per nargs */
nArgs = (cmd & 1) ? (nArgs >> NARG_BITS) : (nArgs & NARG_MASK);
if ((handle == 0) || (nArgs > ARG_NUM_7)) {
nArgs = (cmd & 1) ? (nArgs >> NARG_BITS) : (nArgs & NARG_MASK);//获取参数个数
if ((handle == 0) || (nArgs > ARG_NUM_7)) {//系统调用必须有参数且参数不能大于8个
PRINT_ERR("Unsupport syscall ID: %d nArgs: %d\n", cmd, nArgs);
regs[REG_R0] = -ENOSYS;
return regs;
}
//regs[0-6] 记录系统调用的参数
switch (nArgs) {
case ARG_NUM_0:
case ARG_NUM_1:
ret = (*(SyscallFun1)handle)(regs[REG_R0]);
ret = (*(SyscallFun1)handle)(regs[REG_R0]);//执行系统调用,类似 SysUnlink(pathname);
break;
case ARG_NUM_2:
case ARG_NUM_2://@note_thinking 如何是两个参数的系统调用,这里传的确是三个参数,任务栈中会出现怎样的情况呢?
case ARG_NUM_3:
ret = (*(SyscallFun3)handle)(regs[REG_R0], regs[REG_R1], regs[REG_R2]);
ret = (*(SyscallFun3)handle)(regs[REG_R0], regs[REG_R1], regs[REG_R2]);//类似 SysExecve(fileName, argv, envp);
break;
case ARG_NUM_4:
case ARG_NUM_5:
......@@ -137,12 +144,12 @@ LITE_OS_SEC_TEXT UINT32 *OsArmA32SyscallHandle(UINT32 *regs)
regs[REG_R4], regs[REG_R5], regs[REG_R6]);
}
regs[REG_R0] = ret;
regs[REG_R0] = ret;//R0保存系统调用返回值
OsSaveSignalContext(regs);
OsSaveSignalContext(regs);//保存用户栈现场
/* Return the last value of curent_regs. This supports context switches on return from the exception.
* That capability is only used with theSYS_context_switch system call.
*/
return regs;
return regs;//返回寄存器的值
}
......@@ -52,7 +52,31 @@
#include "sys/socket.h"
#include "dirent.h"
#endif
/*********************************************************
https://blog.csdn.net/piyongduo3393/article/details/89378243
什么情况下会发生从用户态向内核态切换。这里细分为3种情况:
1、发生系统调用时
这是处于用户态的进程主动请求切换到内核态的一种方式。用户态的进程通过系统调用
申请使用操作系统提供的系统调用服务例程来处理任务。而系统调用的机制,其核心仍
是使用了操作系统为用户特别开发的一个中断机制来实现的,即软中断。
2、产生异常时
当CPU执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前
运行的进程切换到处理此异常的内核相关的程序中,也就是转到了内核态,如缺页异常。
3、外设产生中断时
当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下
一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是
用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。
比如硬盘读写操作的完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。
可以看到上述三种由用户态切换到内核态的情况中,只有系统调用是进程主动请求发生切换的,
中断和异常都是被动的。
*********************************************************/
/* process */
extern unsigned int SysGetGroupId(void);
extern unsigned int SysGetTid(void);
......
git add -A
git commit -m '如何跨CPU删除任务?注解一个任务是如何自杀和被他杀的.
git commit -m '1.注解系统调用是如何发生的 2.完善对消息队列的注解
搜索 @note_pic 可以查看全部字符图
搜索 @note_why 是注者尚未看明白的地方,如果您看明白了,请告诉注者完善
搜索 @note_thinking 是注者的思考和吐槽的地方
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册