鸿蒙内核源码分析(线程概念篇) | 是谁在不断的折腾CPU

本篇说清楚任务的问题

在鸿蒙内核线程(thread)就是任务(task),也可以叫作业.线程是对外的说法,对内就叫任务.跟王二毛一样, 在公司叫你王董,回到家里还有领导,就叫二毛啊.这多亲切.在鸿蒙内核是大量的task,很少看到thread,只出现在posix层.当一个东西理解就行.

鸿蒙内核源码分析定位为深挖内核地基,构筑底层网图.就要见真身,剖真人.任务(LosTaskCB)原始真身如下,本篇一一剖析它,看看它的五脏六腑里到底是个啥.

typedef struct {
    VOID            *stackPointer;      /**< Task stack pointer */ //内核态栈指针,SP位置,切换任务时先保存上下文并指向TaskContext位置
    UINT16          taskStatus;         /**< Task status */   //各种状态标签,可以拥有多种标签,按位标识
    UINT16          priority;           /**< Task priority */  //任务优先级[0:31],默认是31级
    UINT16          policy;    //任务的调度方式(三种 .. LOS_SCHED_RR )
    UINT16          timeSlice;          /**< Remaining time slice *///剩余时间片
    UINT32          stackSize;          /**< Task stack size */  //非用户模式下栈大小
    UINTPTR         topOfStack;         /**< Task stack top */  //非用户模式下的栈顶 bottom = top + size
    UINT32          taskID;             /**< Task ID */    //任务ID,任务池本质是一个大数组,ID就是数组的索引,默认 < 128
    TSK_ENTRY_FUNC  taskEntry;          /**< Task entrance function */ //任务执行入口函数
    VOID            *joinRetval;        /**< pthread adaption */ //用来存储join线程的返回值
    VOID            *taskSem;           /**< Task-held semaphore */ //task在等哪个信号量
    VOID            *taskMux;           /**< Task-held mutex */  //task在等哪把锁
    VOID            *taskEvent;         /**< Task-held event */  //task在等哪个事件
    UINTPTR         args[4];            /**< Parameter, of which the maximum number is 4 */ //入口函数的参数 例如 main (int argc,char *argv[])
    CHAR            taskName[OS_TCB_NAME_LEN]; /**< Task name */ //任务的名称
    LOS_DL_LIST     pendList;           /**< Task pend node */  //如果任务阻塞时就通过它挂到各种阻塞情况的链表上,比如OsTaskWait时
    LOS_DL_LIST     threadList;         /**< thread list */   //挂到所属进程的线程链表上
    SortLinkList    sortList;           /**< Task sortlink node */ //挂到cpu core 的任务执行链表上
    UINT32          eventMask;          /**< Event mask */   //事件屏蔽
    UINT32          eventMode;          /**< Event mode */   //事件模式
    UINT32          priBitMap;          /**< BitMap for recording the change of task priority, //任务在执行过程中优先级会经常变化,这个变量用来记录所有曾经变化
                                             the priority can not be greater than 31 */   //过的优先级,例如 ..01001011 曾经有过 0,1,3,6 优先级
    INT32           errorNo;            /**< Error Num */
    UINT32          signal;             /**< Task signal */ //任务信号类型,(SIGNAL_NONE,SIGNAL_KILL,SIGNAL_SUSPEND,SIGNAL_AFFI)
    sig_cb          sig;    //信号控制块,这里用于进程间通讯的信号,类似于 linux singal模块
#if (LOSCFG_KERNEL_SMP == YES)
    UINT16          currCpu;            /**< CPU core number of this task is running on */ //正在运行此任务的CPU内核号
    UINT16          lastCpu;            /**< CPU core number of this task is running on last time */ //上次运行此任务的CPU内核号
    UINT16          cpuAffiMask;        /**< CPU affinity mask, support up to 16 cores */ //CPU亲和力掩码,最多支持16核,亲和力很重要,多核情况下尽量一个任务在一个CPU核上运行,提高效率
    UINT32          timerCpu;           /**< CPU core number of this task is delayed or pended */ //此任务的CPU内核号被延迟或挂起
#if (LOSCFG_KERNEL_SMP_TASK_SYNC == YES)
    UINT32          syncSignal;         /**< Synchronization for signal handling */ //用于CPU之间 同步信号
#endif
#if (LOSCFG_KERNEL_SMP_LOCKDEP == YES) //死锁检测开关
    LockDep         lockDep;
#endif
#if (LOSCFG_KERNEL_SCHED_STATISTICS == YES) //调度统计开关,显然打开这个开关性能会受到影响,鸿蒙默认是关闭的
    SchedStat       schedStat;          /**< Schedule statistics */ //调度统计
#endif
#endif
    UINTPTR         userArea;   //使用区域,由运行时划定,根据运行态不同而不同
    UINTPTR         userMapBase;  //用户模式下的栈底位置
    UINT32          userMapSize;        /**< user thread stack size ,real size : userMapSize + USER_STACK_MIN_SIZE */
    UINT32          processID;          /**< Which belong process *///所属进程ID
    FutexNode       futex;    //实现快锁功能
    LOS_DL_LIST     joinList;           /**< join list */ //联结链表,允许任务之间相互释放彼此
    LOS_DL_LIST     lockList;           /**< Hold the lock list */ //拿到了哪些锁链表
    UINT32          waitID;             /**< Wait for the PID or GID of the child process */ //等待孩子的PID或GID进程
    UINT16          waitFlag;           /**< The type of child process that is waiting, belonging to a group or parent,
                                             a specific child process, or any child process */
#if (LOSCFG_KERNEL_LITEIPC == YES)
    UINT32          ipcStatus;   //IPC状态
    LOS_DL_LIST     msgListHead;  //消息队列头结点,上面挂的都是任务要读的消息
    BOOL            accessMap[LOSCFG_BASE_CORE_TSK_LIMIT];//访问图,指的是task之间是否能访问的标识,LOSCFG_BASE_CORE_TSK_LIMIT 为任务池总数
#endif
} LosTaskCB;

结构体还是比较复杂,虽一一都做了注解,但还是不够清晰,没有模块化.这里把它分解成以下六大块逐一分析:

第一大块:多核CPU相关块

#if (LOSCFG_KERNEL_SMP == YES) //多CPU核支持
    UINT16          currCpu;            /**< CPU core number of this task is running on */ //正在运行此任务的CPU内核号
    UINT16          lastCpu;            /**< CPU core number of this task is running on last time */ //上次运行此任务的CPU内核号
    UINT16          cpuAffiMask;        /**< CPU affinity mask, support up to 16 cores */ //CPU亲和力掩码,最多支持16核,亲和力很重要,多核情况下尽量一个任务在一个CPU核上运行,提高效率
    UINT32          timerCpu;           /**< CPU core number of this task is delayed or pended */ //此任务的CPU内核号被延迟或挂起
#if (LOSCFG_KERNEL_SMP_TASK_SYNC == YES)
    UINT32          syncSignal;         /**< Synchronization for signal handling */ //用于CPU之间 同步信号
#endif
#if (LOSCFG_KERNEL_SMP_LOCKDEP == YES) //死锁检测开关
    LockDep         lockDep;
#endif
#if (LOSCFG_KERNEL_SCHED_STATISTICS == YES) //调度统计开关,显然打开这个开关性能会受到影响,鸿蒙默认是关闭的
    SchedStat       schedStat;          /**< Schedule statistics */ //调度统计
#endif
#endif

鸿蒙内核支持多CPU,谁都知道多CPU当然好,效率高,快嘛,但凡事有两面性,在享受一个东西带来好处的同时,也得承担伴随它一起带来的麻烦和风险.多核有哪些的好处和麻烦,这里不展开说,后续有专门的文章和视频说明.任务可叫线程,或叫作业.CPU就是做作业的,多个CPU就是有多个能做作业的,一个作业能一鼓作气做完吗?

答案是:往往不行,因为现实不允许,作业可以有N多,而CPU数量非常有限,所以经常做着A作业被老板打断让去做B作业.这老板就是调度算法.A作业被打断回来接着做的还会是原来那个CPU吗?

答案是:不一定. 变量cpuAffiMask叫CPU亲和力,它的作用是可以指定A的作业始终是同一个CPU来完成, 也可以随便,交给调度算法,分到谁就谁来,这方面可以不挑.

第二大块:栈空间

    VOID            *stackPointer;      /**< Task stack pointer */  //内核态栈指针,SP位置,切换任务时先保存上下文并指向TaskContext位置.
    UINT32          stackSize;          /**< Task stack size */     //内核态栈大小
    UINTPTR         topOfStack;         /**< Task stack top */      //内核态栈顶 bottom = top + size

    UINTPTR         userArea;       //使用区域,由运行时划定,根据运行态不同而不同
    UINTPTR         userMapBase;    //用户态下的栈底位置
    UINT32          userMapSize;    /**< user thread stack size ,real size : userMapSize + USER_STACK_MIN_SIZE */

进程分内核态进程和用户态进程,这个区别表现在线程(任务)层面上就是

  • 内核态进程下创建的任务只有内核态的栈空间,OsTaskStackAlloc负责内核态栈空间的分配.OsTaskStackInit负责对内核态栈的初始化.
//任务栈初始化,非常重要的函数,返回任务上下文
  LITE_OS_SEC_TEXT_INIT VOID *OsTaskStackInit(UINT32 taskID, UINT32 stackSize, VOID *topStack, BOOL initFlag)
  {
      UINT32 index = 1;
      TaskContext *taskContext = NULL;

      if (initFlag == TRUE) {
          OsStackInit(topStack, stackSize);
      }
      taskContext = (TaskContext *)(((UINTPTR)topStack + stackSize) - sizeof(TaskContext));//上下文存放在栈的底部

      /* initialize the task context */ //初始化任务上下文
  #ifdef LOSCFG_GDB
      taskContext->PC = (UINTPTR)OsTaskEntrySetupLoopFrame;
  #else
      taskContext->PC = (UINTPTR)OsTaskEntry;//程序计数器,CPU首次执行task时跑的第一条指令位置
  #endif
      taskContext->LR = (UINTPTR)OsTaskExit;  /* LR should be kept, to distinguish it's THUMB or ARM instruction */
      taskContext->resved = 0x0;
      taskContext->R[0] = taskID;             /* R0 */
      taskContext->R[index++] = 0x01010101;   /* R1, 0x01010101 : reg initialed magic word */ //0x55
      for (; index < GEN_REGS_NUM; index++) {//R2 - R12的初始化很有意思,为什么要这么做?
          taskContext->R[index] = taskContext->R[index - 1] + taskContext->R[1]; /* R2 - R12 */
      }//R[2]=R[2]<<1=0xAA

  #ifdef LOSCFG_INTERWORK_THUMB // 16位模式
      taskContext->regPSR = PSR_MODE_SVC_THUMB; /* CPSR (Enable IRQ and FIQ interrupts, THUMNB-mode) */
  #else //用于设置CPSR寄存器
      taskContext->regPSR = PSR_MODE_SVC_ARM;   /* CPSR (Enable IRQ and FIQ interrupts, ARM-mode) */
  #endif

  #if !defined(LOSCFG_ARCH_FPU_DISABLE)
      /* 0xAAA0000000000000LL : float reg initialed magic word */
      for (index = 0; index < FP_REGS_NUM; index++) {
          taskContext->D[index] = 0xAAA0000000000000LL + index; /* D0 - D31 */
      }
      taskContext->regFPSCR = 0;
      taskContext->regFPEXC = FP_EN;
  #endif

      return (VOID *)taskContext;
  }

可以看到,初始化了任务上下文(TaskContext),并将任务上下文放在了栈底,初始化任务上下文目的是为了在运行阶段先初始化R0~R15,CPSR寄存器的值.
保存上下文和恢复上下文都是针对寄存器值而言的.这个工作是在内核态的栈中完成的,也就是说一个任务的上下文就是保存在任务的内核态栈中.

OsTaskStackInit的返回值将赋给stackPointer,即寄存器SP

用户态进程下创建的任务除了有内核态的栈空间外,还有用户态栈空间.

  //用户任务使用栈初始化
  LITE_OS_SEC_TEXT_INIT VOID OsUserTaskStackInit(TaskContext *context, TSK_ENTRY_FUNC taskEntry, UINTPTR stack)
  {
      LOS_ASSERT(context != NULL);

  #ifdef LOSCFG_INTERWORK_THUMB
      context->regPSR = PSR_MODE_USR_THUMB;
  #else
      context->regPSR = PSR_MODE_USR_ARM;//工作模式:用户模式 + 工作状态:arm
  #endif
      context->R[0] = stack;//栈指针给r0寄存器
      context->SP = TRUNCATE(stack, LOSCFG_STACK_POINT_ALIGN_SIZE);//异常模式所专用的堆栈 segment fault 输出回溯信息
      context->LR = 0;//保存子程序返回地址 例如 a call b ,在b中保存 a地址
      context->PC = (UINTPTR)taskEntry;//入口函数
  }

注意看里面的内容用户栈的初始化时修改了任务的上下文内容,任务的上下文内容是始终保存在内核栈中,注意这个不要搞混了.OsUserTaskStackInit只是修改上下文地址中的内容.

context->SP的值被修改了,这个修改意味着任务被调度后首先是恢复上下文,即要重置SP寄存器的值,SP的值将被变成context->SP,由此就指向了用户栈空间运行
context->PC也被改变了,这意味着入口地址(代码段位置)也改变了.

context->LR默认是为0,不跳转到任务地方.
在后续每次调度上下文切换过程中,context的内容将不断的变化.

第三大块:资源竞争/同步

    VOID            *taskSem;           /**< Task-held semaphore */ //task在等哪个信号量
    VOID            *taskMux;           /**< Task-held mutex */  //task在等哪把锁
    VOID            *taskEvent;         /**< Task-held event */  //task在等哪个事件
    UINT32          eventMask;          /**< Event mask */   //事件屏蔽
    UINT32          eventMode;          /**< Event mode */   //事件模式
    FutexNode       futex;    //实现快锁功能
    LOS_DL_LIST     joinList;           /**< join list */ //联结链表,允许任务之间相互释放彼此
    LOS_DL_LIST     lockList;           /**< Hold the lock list */ //拿到了哪些锁链表
    UINT32          signal;             /**< Task signal */ //任务信号类型,(SIGNAL_NONE,SIGNAL_KILL,SIGNAL_SUSPEND,SIGNAL_AFFI)
    sig_cb          sig;

公司的资源是有限的,CPU自己也是公司的资源,除了它还有其他的设备,比如做作业用的黑板,用户A,B,C都可能用到,狼多肉少,咋搞?

互斥量(taskMux,futex)能解决这个问题,办事前先拿锁,拿到了锁的爽了,没有拿到的就需要排队,在lockList上排队,注意lockList是个双向链表,它是内核最重要的结构体,开篇就提过,没印象的看 (双向链表篇),上面挂都是等锁进房间的西门大官人.这是互斥量的原理,解决任务间资源紧张的竞争性问题.

另外一个是用于任务的同步的信号量(sig_cb),任务和任务之间是会有关联的,现实生活中公司的A,B用户之间本身有业务往来的正常,CPU在帮B做作业的时候发现前置条件是需要A完成某项作业才能进行,这时B就需要主动让出CPU先办完A的事.这就是信号量的原理,解决的是任务间的同步问题.

第四大块:任务调度
前面说过了作业N多,做作业的只有几个人,单核CPU等于只有一个人干活.那要怎么分配CPU,就需要调度算法.

    UINT16          taskStatus;         /**< Task status */   //各种状态标签,可以拥有多种标签,按位标识
    UINT16          priority;           /**< Task priority */  //任务优先级[0:31],默认是31级
    UINT16          policy;    //任务的调度方式(三种 .. LOS_SCHED_RR )
    UINT16          timeSlice;          /**< Remaining time slice *///剩余时间片
    CHAR            taskName[OS_TCB_NAME_LEN]; /**< Task name */ //任务的名称
    LOS_DL_LIST     pendList;           /**< Task pend node */  //如果任务阻塞时就通过它挂到各种阻塞情况的链表上,比如OsTaskWait时
    LOS_DL_LIST     threadList;         /**< thread list */   //挂到所属进程的线程链表上
    SortLinkList    sortList;           /**< Task sortlink node */ //挂到cpu core 的任务执行链表上    

是简单的先来后到(FIFO)吗? 当然也支持这个方式.鸿蒙内核用的是抢占式调度(policy),就是可以插队,比优先级(priority)大小,[0,31]级,数字越大的优先级越低,跟考试一样,排第一才是最牛的.

鸿蒙排0的最牛! 想也想得到内核的任务优先级都是很高的,比如资源回收任务排第5,定时器任务排第0.够牛了吧.普通老百姓排多少呢?默认28级,惨!!!

另外任务有时间限制timeSlice,叫时间片,默认20ms,用完了会给你重置,发起重新调度,找出优先级高的执行,阻塞的任务(比如没拿到锁的,等信号量同步的,等读写消息队列的)都挂到pendList上,方便管理.

第五大块:任务间通讯

#if (LOSCFG_KERNEL_LITEIPC == YES)
    UINT32          ipcStatus;   //IPC状态
    LOS_DL_LIST     msgListHead;  //消息队列头结点,上面挂的都是任务要读的消息
    BOOL            accessMap[LOSCFG_BASE_CORE_TSK_LIMIT];//访问图,指的是task之间是否能访问的标识,LOSCFG_BASE_CORE_TSK_LIMIT 为任务池总数
#endif

这个很重要,解决任务间通讯问题,要知道进程负责的是资源的管理功能,什么意思?就是它不并负责内容的生产和消费,它只负责管理确保你的内容到达率和完整性.生产者和消费者始终是任务.进程管了哪些东西系列篇有专门的文章,请自行翻看.

liteipc是鸿蒙专有的通讯消息队列实现.简单说它是基于文件的,而传统的ipc消息队列是基于内存的.有什么区别也不在这里讨论,已有专门的文章分析.

第六大块:辅助工具
要知道任务对内核来说太重要了,是任务让CPU忙里忙外的,那中间出差错了怎么办,怎么诊断你问题出哪里了,就需要一些工具,比如死锁检测,比如占用CPU,内存监控 如下:

#if (LOSCFG_KERNEL_SMP_LOCKDEP == YES) //死锁检测开关
    LockDep         lockDep;
#endif
#if (LOSCFG_KERNEL_SCHED_STATISTICS == YES) //调度统计开关,显然打开这个开关性能会受到影响,鸿蒙默认是关闭的
    SchedStat       schedStat;          /**< Schedule statistics */ //调度统计
#endif

以上就是任务的五脏六腑,看清楚它鸿蒙内核的影像会清晰很多!

也为了积极培养鸿蒙生态人才,让大家都能学习到鸿蒙开发最新的技术,针对一些在职人员、0基础小白、应届生/计算机专业、鸿蒙爱好者等人群,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线。大家可以进行参考学习:https://qr21.cn/FV7h05

①全方位,更合理的学习路径
路线图包括ArkTS基础语法、鸿蒙应用APP开发、鸿蒙能力集APP开发、次开发多端部署开发、物联网开发等九大模块,六大实战项目贯穿始终,由浅入深,层层递进,深入理解鸿蒙开发原理!

②多层次,更多的鸿蒙原生应用
路线图将包含完全基于鸿蒙内核开发的应用,比如一次开发多端部署、自由流转、元服务、端云一体化等,多方位的学习内容让学生能够高效掌握鸿蒙开发,少走弯路,真正理解并应用鸿蒙的核心技术和理念。

③实战化,更贴合企业需求的技术点
学习路线图中的每一个技术点都能够紧贴企业需求,经过多次真实实践,每一个知识点、每一个项目,都是码牛课堂鸿蒙研发团队精心打磨和深度解析的成果,注重对学生的细致教学,每一步都确保学生能够真正理解和掌握。

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:https://qr21.cn/FV7h05

如何快速入门:

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr21.cn/FV7h05

大厂鸿蒙面试题::https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/583534.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux系列】 离线安装vnc 可视化桌面

离线安装vnc 可视化桌面 缘下载安装vnc初始化链接 缘 项目需要下载 下载地址&#xff1a; http://mirror.centos.org/centos/7/updates/x86_64/Packages/tigervnc-license-1.8.0-31.el7_9.noarch.rpm http://mirror.centos.org/centos/7/os/x86_64/Packages/libXfont2-2.0.…

Word插件开发

VSTO是Visual Studio Tools for Office的简称&#xff0c;它是Microsoft Visual Studio的一个扩展&#xff0c;用于开发基于Microsoft Office平台的应用程序。VSTO提供了一套API和工具&#xff0c;使开发人员能够利用Visual Studio IDE来开发定制的Office解决方案。 在 Visual…

DiffusionGAN ——最快的小波扩散模型应用研究

介绍 扩散模型最近出现并迅速发展&#xff0c;吸引了许多研究人员的兴趣。这些模型能从随机的噪声输入生成高质量的图像。在图像生成任务中&#xff0c;它们的表现尤其优于最先进的生成模型&#xff08;GANs&#xff09;。扩散模型可以灵活地处理各种条件输入&#xff0c;从而…

knife4j swagger 使用笔记

1.接口访问的端口跟后台设置的不一致&#xff0c;接口请求无反应 处理办法 2.响应参数不显示问题 &#xff08;1&#xff09;返回的参数里面一定要有响应的参数对象&#xff0c;如下&#xff1a; &#xff08;2&#xff09;TableDataInfo 定义成泛型类 TableDataInfo package…

移动应用安全

移动应用安全 移动应用安全主要关注Android、iOS、Windows Phone等平台上移动应用软件安全状态。它涉及应用程序在其设计运行的平台上下文中的安全问题、它们使用的框架以及预期的用户集。所有主流的移动平台都提供大量可选的安全控制&#xff0c;旨在帮助软件开发人员构建安全…

浅析扩散模型与图像生成【应用篇】(十八)——ControlNet

18. Adding Conditional Control to Text-to-Image Diffusion Models 现有的文生图模型如Stable Diffusion通常需要人工输入非常准确的提示词&#xff0c;而且生成的结果还是完全随机不可控制的&#xff0c;只能通过生成多个结果&#xff0c;再从中选取最佳方案。而ControlNet的…

竞争分析:波特五力模型

波特五力模型是分析企业竞争环境的一个分析模型。 根据波特的观点&#xff0c;每家企业都受到“直接竞争对手、顾客、供应商、潜在新进公司和替代性产品”这五个“竞争作用力”的影响。 我们用波特五力模型试着分析下实体书店竞争是否激励。 直接竞争对手&#xff1a;如果直接…

料堆体积测量新方案:激光雷达

激光雷达测量料堆体积是一种高效且精确的方法。激光雷达的工作原理与雷达相似&#xff0c;通过发射激光束探测目标的位置、速度等特征量。在测量料堆体积时&#xff0c;激光雷达系统向料堆发射激光束&#xff0c;然后接收从料堆表面反射回来的信号。通过对这些反射信号的处理和…

Linux网络之DNS域名解析

一、DNS概述 1.1什么是DNS 域名解析协议&#xff0c;将域名转换成IP地址 1.2为什么要用DNS IP地址不便于记忆&#xff0c;DNS使用户可以通过易记的域名快速访问各种网络资源。 192.168.0.0—— ip地址过长而且都是数字&#xff0c;不方便记忆就出现了域名 www.baidu.com—…

记一次线上日志堆栈不打印问题排查(附:高并发系统日志打印方案可收藏)

目录 一.线上的日志堆栈不打印了二.一步一步仔细排查三.最后搞定四.聊一聊线上日志到底应该怎么打印4.1 日志打印的诉求4.2 常见的系统日志上报方案4.2.1 ELK 方案4.2.2 自定义log appender 完成应用日志采集. 4.3 日志常见框架傻傻分不清4.4 日志在高并发系统中需要注意的 tip…

神仙级Python入门教程,手把手教你从0到精通,学不会算我输!

亲爱的朋友们&#xff0c;你是否对编程充满好奇&#xff0c;却觉得它遥不可及&#xff1f; 你是否想学习一门强大的编程语言&#xff0c;却不知从何下手&#xff1f; 那么&#xff0c;这篇“神仙级”Python入门教程就是为你量身打造的&#xff01;不论你是编程小白还是有一定…

linux笔记4--shell命令1

文章目录 一. 目录1.说明2.盘符3.linux根目录(以Ubuntu为例)①说明②根目录下一些文件夹的解析/home/root/mnt/media/var/cdrom/etc/lib (/lib32--32位的&#xff0c;/lib64-64位的)/lostfound/boot/proc/bin/sbin/snap/srv/usr/opt/dev/run/tmp 二. ls命令--操作文件夹1.说明2…

探索阿里巴巴商品详情API接口:开启电商数据之旅

阿里巴巴商品详情API接口是阿里巴巴开放平台提供的一项服务&#xff0c;它允许开发者通过调用接口获取指定商品的详细信息&#xff0c;包括商品标题、价格、库存、描述、图片等。这些数据对于电商从业者来说具有极高的价值&#xff0c;可以帮助他们更好地了解市场动态&#xff…

层次分析法(AHP)计算原理解释

AHP层次分析法是一种解决多目标复杂问题的定性和定量相结合进行计算决策权重的研究方法。该方法将定量分析与定性分析结合起来&#xff0c;用决策者的经验判断各衡量目标之间能否实现的标准之间的相对重要程度&#xff0c;并合理地给出每个决策方案的每个标准的权数&#xff0c…

vue下载文件时显示进度条

1.单个下载&#xff08;开始是导出按钮 下载显示进度条&#xff09; html <el-button click.stop"exportReport(scope.row, scope.index)" v-if"!scope.row.schedule" icon"el-icon-download"size"small" type"text"styl…

Linux第十五章

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

系统思考—企业辅导咨询

从2004年、2014年到2024年&#xff0c;国九条政策的发布与变迁不仅影响了行业趋势&#xff0c;更深刻地改变了企业的风险预估和策略辅导。彼得杜鲁克曾经说过&#xff1a;“必须系统地抛弃旧知识。”这不仅是企业领导者的挑战&#xff0c;也是我们每个人的难题。难点不在于我们…

GITEE 基于OAuth2的API V5版本

为了构建更好的码云生态环境&#xff0c;我们推出了基于OAuth2的API V5版本。 API V5接口使用方式以及Url都参照GitHub&#xff0c;为了各位开发者更好的兼容已经存在的第三方应用。 API 使用条款 OSCHINA 用户是资源的拥有者&#xff0c;需尊重和保护用户的权益。不能在应用…

Spring AI 抢先体验,5 分钟玩转 Java AI 应用开发

作者&#xff1a;刘军 Spring AI 是 Spring 官方社区项目&#xff0c;旨在简化 Java AI 应用程序开发&#xff0c;让 Java 开发者像使用 Spring 开发普通应用一样开发 AI 应用。 Spring Cloud Alibaba AI 以 Spring AI 为基础&#xff0c;并在此基础上提供阿里云通义系列大模…

XY_RE复现(二)

一&#xff0c;何须相思煮余年 0x55 0x8b 0xec 0x81 0xec 0xa8 0x0 0x0 0x0 0xa1 0x0 0x40 0x41 0x0 0x33 0xc5 0x89 0x45 0xfc 0x68 0x9c 0x0 0x0 0x0 0x6a 0x0 0x8d 0x85 0x60 0xff 0xff 0xff 0x50 0xe8 0x7a 0xc 0x0 0x0 0x83 0xc4…