【Ucos-III源码分析】——信号量
目录
一、信号量的创建
二、发布信号量
三、挂起等待信号量
四、放弃等待信号量
一、信号量的创建
首先要了解信号量在内存里是存的些什么东西,上图
void OSSemCreate (OS_SEM *p_sem,CPU_CHAR *p_name,OS_SEM_CTR cnt,OS_ERR *p_err)
{CPU_SR_ALLOC();#ifdef OS_SAFETY_CRITICALif (p_err == (OS_ERR *)0) {OS_SAFETY_CRITICAL_EXCEPTION();return;}
#endif#ifdef OS_SAFETY_CRITICAL_IEC61508if (OSSafetyCriticalStartFlag == DEF_TRUE) {*p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;return;}
#endif#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0uif (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* Not allowed to be called from an ISR */*p_err = OS_ERR_CREATE_ISR;return;}
#endif#if OS_CFG_ARG_CHK_EN > 0uif (p_sem == (OS_SEM *)0) { /* Validate 'p_sem' */*p_err = OS_ERR_OBJ_PTR_NULL;return;}
#endifCPU_CRITICAL_ENTER();p_sem->Type = OS_OBJ_TYPE_SEM; /* Mark the data structure as a semaphore */p_sem->Ctr = cnt; /* Set semaphore value */p_sem->TS = (CPU_TS)0;p_sem->NamePtr = p_name; /* Save the name of the semaphore */OS_PendListInit(&p_sem->PendList); /* Initialize the waiting list */#if OS_CFG_DBG_EN > 0uOS_SemDbgListAdd(p_sem);
#endifOSSemQty++;CPU_CRITICAL_EXIT();*p_err = OS_ERR_NONE;
}
所以创建信号量的过程其实很简单,难的是信号量这个东西里面有些什么,创建的时候出了常规的参数检查之后,给信号量赋值(你传的什么)
有一个关键的函数OS_PendListInit(&p_sem->PendList);
void OS_PendListInit (OS_PEND_LIST *p_pend_list)
{p_pend_list->HeadPtr = (OS_PEND_DATA *)0;p_pend_list->TailPtr = (OS_PEND_DATA *)0;p_pend_list->NbrEntries = (OS_OBJ_QTY )0;
}
这里可以对比上图就是第三个方块,它里面管理的是一个双向链表,和一个数字,这里把初始值都附上。这个工作完成了之后基本上就完成了一个信号量的创建。
二、发布信号量
S_SEM_CTR OSSemPost (OS_SEM *p_sem,OS_OPT opt,OS_ERR *p_err)
{OS_SEM_CTR ctr;CPU_TS ts;#ifdef OS_SAFETY_CRITICALif (p_err == (OS_ERR *)0) {OS_SAFETY_CRITICAL_EXCEPTION();return ((OS_SEM_CTR)0);}
#endif#if OS_CFG_ARG_CHK_EN > 0uif (p_sem == (OS_SEM *)0) { /* Validate 'p_sem' */*p_err = OS_ERR_OBJ_PTR_NULL;return ((OS_SEM_CTR)0);}
#endif#if OS_CFG_OBJ_TYPE_CHK_EN > 0uif (p_sem->Type != OS_OBJ_TYPE_SEM) { /* Make sure semaphore was created */*p_err = OS_ERR_OBJ_TYPE;return ((OS_SEM_CTR)0);}
#endifts = OS_TS_GET(); /* Get timestamp */#if OS_CFG_ISR_POST_DEFERRED_EN > 0uif (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* See if called from an ISR */OS_IntQPost((OS_OBJ_TYPE)OS_OBJ_TYPE_SEM, /* Post to ISR queue */(void *)p_sem,(void *)0,(OS_MSG_SIZE)0,(OS_FLAGS )0,(OS_OPT )opt,(CPU_TS )ts,(OS_ERR *)p_err);return ((OS_SEM_CTR)0);}
#endifctr = OS_SemPost(p_sem, /* Post to semaphore */opt,ts,p_err);return (ctr);
}
这里进行的操作就是,把你需要往哪个信号量发布传进来,这个函数其实做的事情很少,主要就是获取了一个时间(当前的系统时间ts)然后再传递给OS_SemPost()这个函数
OS_SEM_CTR OS_SemPost (OS_SEM *p_sem,OS_OPT opt,CPU_TS ts,OS_ERR *p_err)
{OS_OBJ_QTY cnt;OS_SEM_CTR ctr;OS_PEND_LIST *p_pend_list;OS_PEND_DATA *p_pend_data;OS_PEND_DATA *p_pend_data_next;OS_TCB *p_tcb;CPU_SR_ALLOC();CPU_CRITICAL_ENTER();p_pend_list = &p_sem->PendList;if (p_pend_list->NbrEntries == (OS_OBJ_QTY)0) { /* Any task waiting on semaphore? */switch (sizeof(OS_SEM_CTR)) {case 1u:if (p_sem->Ctr == DEF_INT_08U_MAX_VAL) {CPU_CRITICAL_EXIT();*p_err = OS_ERR_SEM_OVF;return ((OS_SEM_CTR)0);}break;case 2u:if (p_sem->Ctr == DEF_INT_16U_MAX_VAL) {CPU_CRITICAL_EXIT();*p_err = OS_ERR_SEM_OVF;return ((OS_SEM_CTR)0);}break;case 4u:if (p_sem->Ctr == DEF_INT_32U_MAX_VAL) {CPU_CRITICAL_EXIT();*p_err = OS_ERR_SEM_OVF;return ((OS_SEM_CTR)0);}break;default:break;}p_sem->Ctr++; /* No */ctr = p_sem->Ctr;p_sem->TS = ts; /* Save timestamp in semaphore control block */CPU_CRITICAL_EXIT();*p_err = OS_ERR_NONE;return (ctr);}OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT();if ((opt & OS_OPT_POST_ALL) != (OS_OPT)0) { /* Post message to all tasks waiting? */cnt = p_pend_list->NbrEntries; /* Yes */} else {cnt = (OS_OBJ_QTY)1; /* No */}p_pend_data = p_pend_list->HeadPtr;while (cnt > 0u) {p_tcb = p_pend_data->TCBPtr;p_pend_data_next = p_pend_data->NextPtr;OS_Post((OS_PEND_OBJ *)((void *)p_sem),p_tcb,(void *)0,(OS_MSG_SIZE)0,ts);p_pend_data = p_pend_data_next;cnt--;}ctr = p_sem->Ctr;OS_CRITICAL_EXIT_NO_SCHED();if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0) {OSSched(); /* Run the scheduler */}*p_err = OS_ERR_NONE;return (ctr);
}
1、先把等待列表取出来看,里面是否有正在等待信号量的任务(这里也需要看你是发给所有任务还是单个等待的任务)
2、如果没有任务,那么进入判断你传递的信号量是什么类型的,char?int?,来判断你发布的信号量是否到达了最大值,如果到了就直接GG,如果没有就对计数值++,还把传递进来的ts保存在了信号量里面。
3、如果有任务在等待,先判断你的opt是发给所有任务还是发布给一个任务。
如果是发给所有任务:先把等待列表里面的任务个数传递出来给cnt,再循环遍历所有等待任务并且把他们从等待列表中踢出去(OS_Post())
void OS_Post (OS_PEND_OBJ *p_obj,OS_TCB *p_tcb,void *p_void,OS_MSG_SIZE msg_size,CPU_TS ts)
{switch (p_tcb->TaskState) {case OS_TASK_STATE_RDY: /* Cannot Pend Abort a task that is ready */case OS_TASK_STATE_DLY: /* Cannot Pend Abort a task that is delayed */case OS_TASK_STATE_SUSPENDED: /* Cannot Post a suspended task */case OS_TASK_STATE_DLY_SUSPENDED: /* Cannot Post a suspended task that was also dly'd */break;case OS_TASK_STATE_PEND:case OS_TASK_STATE_PEND_TIMEOUT:if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {OS_Post1(p_obj, /* Indicate which object was posted to */p_tcb,p_void,msg_size,ts);} else {
#if (OS_MSG_EN > 0u)p_tcb->MsgPtr = p_void; /* Deposit message in OS_TCB of task waiting */p_tcb->MsgSize = msg_size; /* ... assuming posting a message */
#endifp_tcb->TS = ts;}if (p_obj != (OS_PEND_OBJ *)0) {OS_PendListRemove(p_tcb); /* Remove task from wait list(s) */
#if OS_CFG_DBG_EN > 0uOS_PendDbgNameRemove(p_obj,p_tcb);
#endif}OS_TaskRdy(p_tcb); /* Make task ready to run */p_tcb->TaskState = OS_TASK_STATE_RDY;p_tcb->PendStatus = OS_STATUS_PEND_OK; /* Clear pend status */p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */break;case OS_TASK_STATE_PEND_SUSPENDED:case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {OS_Post1(p_obj, /* Indicate which object was posted to */p_tcb,p_void,msg_size,ts);} else {
#if (OS_MSG_EN > 0u)p_tcb->MsgPtr = p_void; /* Deposit message in OS_TCB of task waiting */p_tcb->MsgSize = msg_size; /* ... assuming posting a message */
#endifp_tcb->TS = ts;}OS_TickListRemove(p_tcb); /* Cancel any timeout */if (p_obj != (OS_PEND_OBJ *)0) {OS_PendListRemove(p_tcb); /* Remove task from wait list(s) */
#if OS_CFG_DBG_EN > 0uOS_PendDbgNameRemove(p_obj,p_tcb);
#endif}p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;p_tcb->PendStatus = OS_STATUS_PEND_OK; /* Clear pend status */p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */break;default:break;}
}
这里进来就是判断你任务的状态是否符合要求,比如你一个被挂起的任务不能是准备状态这种,并且将这个ts保存在任务的tcb中然后就调用 OS_PendListRemove(p_tcb);
void OS_PendListRemove (OS_TCB *p_tcb)
{OS_OBJ_QTY n_pend_list; /* Number of pend lists */OS_PEND_DATA *p_pend_data;OS_PEND_LIST *p_pend_list;OS_PEND_OBJ *p_obj;p_pend_data = p_tcb->PendDataTblPtr; /* Point to the first OS_PEND_DATA to remove */n_pend_list = p_tcb->PendDataTblEntries; /* Get number of entries in the table */while (n_pend_list > (OS_OBJ_QTY)0) {p_obj = p_pend_data->PendObjPtr; /* Get pointer to pend list */p_pend_list = &p_obj->PendList;OS_PendListRemove1(p_pend_list,p_pend_data);p_pend_data++;n_pend_list--;}p_tcb->PendDataTblEntries = (OS_OBJ_QTY )0;p_tcb->PendDataTblPtr = (OS_PEND_DATA *)0;
}
进入到这个里面就是把任务从等待列表中移除出去,第一个pend_data基本上就是任务的实体,第二个是任务被几个内核对象等待。 p_tcb->PendDataTblPtr; p_tcb->PendDataTblEntries;这两个变量是在任务调用挂起等待信号量的函数的时候被输出化(意思就是任务被加入这个挂起等待列表的时候就初始化这两个变量)移除操作就是一个双线链表,然后把挂起的任务从这个双向链表中移除但是这里的双向链表不是直接管理的任务tcb,是管理的pen_Data,
是这样一个结构,并不是直接将tcb进行双向连接的。
这里还有一个调试的代码也顺便讲解一下。
void OS_PendDbgNameRemove (OS_PEND_OBJ *p_obj,OS_TCB *p_tcb)
{OS_PEND_LIST *p_pend_list;OS_PEND_DATA *p_pend_data;OS_TCB *p_tcb1;p_tcb->DbgNamePtr = (CPU_CHAR *)((void *)" "); /* Remove name of object pended on for readied task */p_pend_list = &p_obj->PendList;p_pend_data = p_pend_list->HeadPtr;if (p_pend_data != (OS_PEND_DATA *)0) {p_tcb1 = p_pend_data->TCBPtr;p_obj->DbgNamePtr = p_tcb1->NamePtr;} else {p_obj->DbgNamePtr = (CPU_CHAR *)((void *)" "); /* No other task pending on object */}
}
这里其实就是这样的任务tcb下还有一个任务名字,这个debug就指向第一个tcb的名字这里,如果移除的话就需要把这个debug指向下一个名字,通过这个方式来调试,任务是否被成功移除了这个列表。
void OS_TaskRdy (OS_TCB *p_tcb)
{OS_TickListRemove(p_tcb); /* Remove from tick list */if ((p_tcb->TaskState & OS_TASK_STATE_BIT_SUSPENDED) == (OS_STATE)0) {OS_RdyListInsert(p_tcb); /* Insert the task in the ready list */}
}
这里把任务从列表中移除了之后就是把你刚刚移除的任务tcb重新的改成就绪态。
后续还有一个点,等待多个内核对象,这里相当于做的处理就是,我一个任务在等待多个内核对象,然后因为其中一个等待的内核对象释放了,所以我任务移除了等待列表,此时任务里就会保存一个变量,相当于是一个标志,是因为哪个内核对象释放了,然后让任务脱离了等待列表(相当于你被人抓了,然后谁来救的你,我就保存谁)后续移除等待列表的操作都是一样的利用OS_PendListRemove这个函数。
三、挂起等待信号量
1、常规参数检查(略过了太无聊)
2、查看信号量是否大于1
(1)、大于1,这时候是最简单的,相当于你想拿信号量,此时也有,信号量-1,接着运行工作,此时如果你设置的需要时间检查,就把信号量发布时候的保存的ts返回出去
if (p_sem->Ctr > (OS_SEM_CTR)0) { /* Resource available? */p_sem->Ctr--; /* Yes, caller may proceed */if (p_ts != (CPU_TS *)0) {*p_ts = p_sem->TS; /* get timestamp of last post */}ctr = p_sem->Ctr;CPU_CRITICAL_EXIT();*p_err = OS_ERR_NONE;return (ctr);}
(2)、信号量为0,
此时又要分你是等待信号量是阻塞还是非阻塞,如果是非阻塞,那么直接退出,告诉你当前没有信号量给你用
if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { /* Caller wants to block if not available? */ctr = p_sem->Ctr; /* No */CPU_CRITICAL_EXIT();*p_err = OS_ERR_PEND_WOULD_BLOCK;return (ctr);
}
如果此时是阻塞等待,就需要把任务挂起到等待列表中。调用OS_Pend去挂起,去挂起任务
else { /* Yes */if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* Can't pend when the scheduler is locked */CPU_CRITICAL_EXIT();*p_err = OS_ERR_SCHED_LOCKED;return ((OS_SEM_CTR)0);}}
这里详细看一下这个OS_Pend这个函数,这里可以看作利用传参,来初始化这个需要挂载的pen_data,然后再把这个pen_data经行初始化后挂载到等待信号量的双向链表里面去(依旧是按照优先级来插入有顺序的),但是这里还要注意,任务等待信号量如果添加了超时选项,那么任务就会被插入到时钟基准的列表里去。等待匹配值到来在移除出去。
void OS_Pend (OS_PEND_DATA *p_pend_data,OS_PEND_OBJ *p_obj,OS_STATE pending_on,OS_TICK timeout)
{OS_PEND_LIST *p_pend_list;OSTCBCurPtr->PendOn = pending_on; /* Resource not available, wait until it is */OSTCBCurPtr->PendStatus = OS_STATUS_PEND_OK;OS_TaskBlock(OSTCBCurPtr, /* Block the task and add it to the tick list if needed */timeout);if (p_obj != (OS_PEND_OBJ *)0) { /* Add the current task to the pend list ... */p_pend_list = &p_obj->PendList; /* ... if there is an object to pend on */p_pend_data->PendObjPtr = p_obj; /* Save the pointer to the object pending on */OS_PendDataInit((OS_TCB *)OSTCBCurPtr, /* Initialize the remaining field */(OS_PEND_DATA *)p_pend_data,(OS_OBJ_QTY )1);OS_PendListInsertPrio(p_pend_list, /* Insert in the pend list in priority order */p_pend_data);} else {OSTCBCurPtr->PendDataTblEntries = (OS_OBJ_QTY )0; /* If no object being pended on the clear these fields */OSTCBCurPtr->PendDataTblPtr = (OS_PEND_DATA *)0; /* ... in the TCB */}
#if OS_CFG_DBG_EN > 0uOS_PendDbgNameAdd(p_obj,OSTCBCurPtr);
#endif
}
最后在进行任务的调度,重新选择优先级高的任务去运行。
OSSched();
当任务拿到信号量的时候,程序又从此处继续执行。
CPU_CRITICAL_ENTER();switch (OSTCBCurPtr->PendStatus) {case OS_STATUS_PEND_OK: /* We got the semaphore */if (p_ts != (CPU_TS *)0) {*p_ts = OSTCBCurPtr->TS;}*p_err = OS_ERR_NONE;break;case OS_STATUS_PEND_ABORT: /* Indicate that we aborted */if (p_ts != (CPU_TS *)0) {*p_ts = OSTCBCurPtr->TS;}*p_err = OS_ERR_PEND_ABORT;break;case OS_STATUS_PEND_TIMEOUT: /* Indicate that we didn't get semaphore within timeout */if (p_ts != (CPU_TS *)0) {*p_ts = (CPU_TS )0;}*p_err = OS_ERR_TIMEOUT;break;case OS_STATUS_PEND_DEL: /* Indicate that object pended on has been deleted */if (p_ts != (CPU_TS *)0) {*p_ts = OSTCBCurPtr->TS;}*p_err = OS_ERR_OBJ_DEL;break;default:*p_err = OS_ERR_STATUS_INVALID;CPU_CRITICAL_EXIT();return ((OS_SEM_CTR)0);}ctr = p_sem->Ctr;CPU_CRITICAL_EXIT();return (ctr);
四、放弃等待信号量
void OS_PendAbort (OS_PEND_OBJ *p_obj,OS_TCB *p_tcb,CPU_TS ts)
{switch (p_tcb->TaskState) {case OS_TASK_STATE_RDY: /* Cannot Pend Abort a task that is ready */case OS_TASK_STATE_DLY: /* Cannot Pend Abort a task that is delayed */case OS_TASK_STATE_SUSPENDED: /* Cannot Pend Abort a suspended task */case OS_TASK_STATE_DLY_SUSPENDED: /* Cannot Pend Abort a suspended task that was also dly'd */break;case OS_TASK_STATE_PEND:case OS_TASK_STATE_PEND_TIMEOUT:if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {OS_PendAbort1(p_obj, /* Indicate which object was pend aborted */p_tcb,ts);}
#if (OS_MSG_EN > 0u)p_tcb->MsgPtr = (void *)0;p_tcb->MsgSize = (OS_MSG_SIZE)0u;
#endifp_tcb->TS = ts;if (p_obj != (OS_PEND_OBJ *)0) {OS_PendListRemove(p_tcb); /* Remove task from all pend lists */}OS_TaskRdy(p_tcb);p_tcb->TaskState = OS_TASK_STATE_RDY; /* Task will be ready */p_tcb->PendStatus = OS_STATUS_PEND_ABORT; /* Indicate pend was aborted */p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */break;case OS_TASK_STATE_PEND_SUSPENDED:case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {OS_PendAbort1(p_obj, /* Indicate which object was pend aborted */p_tcb,ts);}
#if (OS_MSG_EN > 0u)p_tcb->MsgPtr = (void *)0;p_tcb->MsgSize = (OS_MSG_SIZE)0u;
#endifp_tcb->TS = ts;if (p_obj != (OS_PEND_OBJ *)0) {OS_PendListRemove(p_tcb); /* Remove task from all pend lists */}OS_TickListRemove(p_tcb); /* Cancel the timeout */p_tcb->TaskState = OS_TASK_STATE_SUSPENDED; /* Pend Aborted task is still suspended */p_tcb->PendStatus = OS_STATUS_PEND_ABORT; /* Indicate pend was aborted */p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */break;default:break;}
}
就说白了就是你一直等待一个信号量,然后你在逻辑里面不想等了,就把这玩意儿调一下就可以不等了。原理也是去看你的等待列表里移除出去。解除和内核对象的关联,然后记录时间。
【Ucos-III源码分析】——信号量相关推荐
- v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 | 百篇博客分析HarmonyOS源码
子曰:"笃信好学,守死善道.危邦不入,乱邦不居,天下有道则见,无道则隐.邦有道,贫且贱焉,耻也.邦无道,富且贵焉,耻也." <论语>:泰伯篇 百篇博客系列篇.本篇为: ...
- v79.01 鸿蒙内核源码分析(用户态锁篇) | 如何使用快锁Futex(上) | 百篇博客分析OpenHarmony源码
百篇博客分析|本篇为:(用户态锁篇) | 如何使用快锁Futex(上) 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁) ...
- 鸿蒙轻内核源码分析:掌握信号量使用差异
摘要:本文带领大家一起剖析鸿蒙轻内核的信号量模块的源代码,包含信号量的结构体.信号量池初始化.信号量创建删除.申请释放等. 本文分享自华为云社区<鸿蒙轻内核M核源码分析系列十一 信号量Semap ...
- BERT源码分析(PART III)
写在前面 继续之前没有介绍完的 Pre-training 部分,在上一篇中(BERT源码分析(PART II))我们已经完成了对输入数据的处理,接下来看看 BERT 是怎么完成「Masked LM」和 ...
- uboot源码分析笔记
前几天看了ucos的源码,后面开始学习uboot的源码 网上看到一些uboot的文章,当然都是牛人写出来的,不过基本版本有点老,我这越是初学者,越想学习新版本的代码 我下载的是u-boot-2014. ...
- golang源码分析-启动过程概述
golang源码分析-启动过程概述 golang语言作为根据CSP模型实现的一种强类型的语言,本文主要就是通过简单的实例来分析一下golang语言的启动流程,为深入了解与学习做铺垫. golang代码 ...
- Nginx源码分析:master/worker工作流程概述
nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> Nginx的master与worker工作模式 在生成环境中的Nginx启动模式基本都是以m ...
- Python3.5源码分析-sys模块及site模块导入
Python3源码分析 本文环境python3.5.2. 参考书籍<<Python源码剖析>> python官网 Python3的sys模块初始化 根据分析完成builtins ...
- supervisor源码分析
Supervisor分析 1.运行原理概述: Supervisor生成主进程并将主进程变成守护进程,supervisor依次生成配置文件中的工作进程,然后依次监控工作进程的工作状态,并且主进程负责与s ...
- linux内存源码分析 - 内存压缩(同步关系)
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 概述 最近在看内存回收,内存回收在进行同步的一些情况非常复杂,然后就想,不会内存压缩的页面迁移过程中的同步关系也 ...
最新文章
- 列执行MSSQL Server 处理Null
- kubernetes node节点失效 调度
- CVPR!你凭什么收录我3篇论文!?
- 几款xshell绝佳配色方案
- boost::core模块实现分配const void指针
- Sql Server 按格式输出日期
- 网络是怎样连接的-UDP协议的收发操作
- 某个元素的距离页面的左边距_在机检测圆心距
- 信息学奥赛一本通(1042:奇偶ASCII值判断)
- [机器学习-实践篇]学习之线性回归、岭回归、Lasso回归,tensorflow实现的线性回归
- 紫书 习题 8-15 UVa 1617 (贪心)
- 三菱socket通信实例_三菱QUnCPU内置以太网Socket通信(TCP篇)
- 《统计学习方法》代码全解析——第四部分朴素贝叶斯
- 海南航空宁波到重庆的变态机票
- 加拿大教授 武 计算机,加拿大卡尔加里大学Yingxu Wang教授访问计算机学院
- 基于51单片机俄罗斯方块游戏电路设计
- 网站实现qq登录(springboot后台)
- 我读经典(5):读《大话重构》迷你书有感
- 视频音乐如何转换成mp3?
- 基于颜色分割的盲道识别算法