RTT(RT-Thread)线程管理(1.2W字详细讲解)

news2024/11/10 18:43:12

目录

RTT线程管理

 线程管理特点

线程工作机制

线程控制块

线程属性

线程状态之间切换

线程相关操作

创建和删除线程

创建线程

删除线程

动态创建线程实例

启动线程

初始化和脱离线程

初始化线程

脱离线程

静态创建线程实例 

线程辅助函数

获得当前线程

让出处理器资源

线程睡眠

控制线程函数

设置和删除idle线程hook函数

设置钩子函数

删除钩子函数

设置调度器hook函数

线程调度器hook函数实例


 RTT线程管理

        RT-Thread是支持多任务的操作系统,多任务是通过多线程的方式实现。线程是任务的载体,是RTT中最基本的调度单位。

        线程在运行的时候,它自己会认为独占CPU运行

        线程执行时的运行环境称为上下文(与之相对应的有中断上下文),具体来说就是各个变量和数据,包括所有的寄存器变量、堆栈、内存信息等。

 线程管理特点

        RT-Thread 线程管理的主要功能是对线程进行管理和调度,系统中总共存在两类线程,分别是系统线程用户线程,系统线程是由 RT-Thread 内核创建的线程,用户线程是由应用程序创建的线程,这两类线程都会从内核对象容器中分配线程对象,当线程被删除时,也会被从对象容器中删除。

        RT-Thread 的线程调度器是抢占式的,主要的工作就是从就绪线程列表中查找最高优先级线程,保证最高优先级的线程能够被运行,最高优先级的任务一旦就绪,总能得到 CPU 的使用权。

        当调度器调度线程切换时,先将当前线程上下文保存起来,当再切回到这个线程时,线程调度器将该线程的上下文信息恢复。(我们在切换完以后,当再次回到之前执行的线程时候,要从被打断的地方重新开始执行,所以我们要将线程上下文先保存起来)线程再被调度之前,我们要保存现场,保存完现场之后再执行高优先级的线程,在我们再调度回来的时候,再恢复现场。但这些不需要我们应用层手动来做。

线程工作机制

线程控制块

        线程控制块由结构体 struct rt_thread 表示,线程控制块是操作系统用于管理线程的一个数据结构,它会存放线程的一些信息,例如优先级、线程名称、线程状态等,也包含线程与线程之间连接用的链表结构(内核在管理多个线程的时候,将描述每个线程的结构体采用一个列表的形式来管理起来,为了方便内核后期来查找和管理),线程等待事件集合等。

struct rt_thread
{
   /* rt object */
   char       name[RT_NAME_MAX];       /**<the name of thread */
   rt_uint8_t  type;               /**< type of object */
   rt_uint8_t  flags;               /**< thread's flags */

   rt_list_t   list;                    /**< the object list */
   rt_list_t   tlist;                   /**< the thread list */

   /* stack point and entry */
   void       *sp;                     /**< stack point 栈指针*/ 
   void       *entry;                 /**< entry */
   void       *parameter;          /**< parameter */
   void       *stack_addr;         /**< stack address point 栈地址指针*/
   rt_uint32_t stack_size;      /**< stack size */

   /* error code */
     rt_err_t    error;                   /**< error code */
     rt_uint8_t  stat;                    /**< thread status 线程状态*/

    /* priority */
    rt_uint8_t  current_priority;        /**< current priority */
    rt_uint8_t  init_priority;           /**< initialized priority */

    rt_uint32_t number_mask;

    ...

    rt_ubase_t  init_tick;                          /**< thread's initialized tick */
    rt_ubase_t  remaining_tick;                /**< remaining tick */

    struct rt_timer thread_timer;              /**< built-in thread timer */

    void (*cleanup)(struct rt_thread *tid);    /**< cleanup function when thread exit */

    rt_uint32_t user_data;                          /**< private user data beyond this thread */
};

其中有个对称多核处理器的功能,如果属于cortex-M系列的处理器,因为是单核,所以不使用对称多核处理器,但cortex-A系统多核支持

 注:cleanup函数指针指向的函数,会在线程退出的时候,被idle线程回调一次,执行用户设置的清理现场等工作。 

线程属性

  • 线程栈

        RT-Thread 线程具有独立的栈,当进行线程切换时,会将当前线程的上下文存在栈中,当线程要恢复运行时,再从栈中读取上下文信息,进行恢复。

  • 线程状态

可以通过查看线程控制块结构体的flags成员来判断当前线程所处状态

  • 线程优先级

        RT-Thread 最大支持 256 个线程优先级 (0~255),数值越小的优先级越高,0 为最高优先级。在一些资源比较紧张的系统中,可以根据实际情况选择只支持 8 个或 32 个优先级的系统配置;对于 ARM Cortex-M系列,普遍采用 32 个优先级。最低优先级默认分配给空闲线程使用,用户一般不使用。在系统中,当有比当前线程优先级更高的线程就绪时,当前线程将立刻被换出,高优先级线程抢占处理器运行

  • 时间片

每个线程都有时间片这个参数,但时间片仅对优先级相同的就绪态线程有效。(如果多个线程的优先级相同,对于同一优先级的线程,调度的时候根据时间片进行调度,即每个线程调度一段时间然后切换另一个线程来调度执行)

注意:

        作为一个实时系统,一个优先级明确的实时系统,如果一个线程中的程序陷入了死循环操作,那么比它优先级低的线程都将不能够得到执行。所以在实时操作系统中必须注意的一点就是:线程中不能陷入死循环操作,必须要有让出 CPU使用权的动作循环中调用延时函数或者主动挂起(两种方法都能使线程处于挂起态,挂起态是不参加线程调度的,这时我们的系统就可以调度其它线程去执行)

线程状态之间切换

 

注:就绪状态和运行状态是等同的,区别在于谁拥有线程的执行权。

就绪状态通过主动挂起等待或者主动延时一段时间,进入挂起状态,不参与线程调度

处于挂起状态的线程通过序号2中的函数可以恢复到就绪状态(如线程重启、信号释放、锁释放等等)

处于运行状态的线程通过序号1中的函数可以进入到挂起状态,这些函数与序号2中是相对的

线程相关操作

        线程相关的操作包括:创建(动态)/初始化(静态)、启动、运行、删除/脱离。

        动态线程与静态线程的区别是:动态线程是系统自动从动态内存堆上分配栈空间与线程句柄(初始化 heap 之后才能使用 create 创建动态线程),静态线程是由用户分配栈空间与线程句柄。

创建和删除线程

创建线程

参数1:用于给当前线程起名字

参数2:函数指针,指向了线程处理函数

参数3:用于给线程处理函数传递参数

参数4:要创建的线程对应线程栈的大小(指定大小之后,系统会自动动态分配空间)

参数5:线程优先级0-31(由高到低)

参数6:如果优先级相同的线程都处于就绪态,线程调度由tick来决定,按照时间片轮流调度

/**
 * This function will create a thread objectand allocate thread object memory
 * and stack.
 *
 * @param name the name of thread, which shallbe unique
 * @param entry the entry function of thread
 * @param parameter the parameter of threadenter function
 * @param stack_size the size of thread stack
 * @param priority the priority of thread
 * @param tick the time slice if there are samepriority thread
 *
 * @return the created thread object
 */
rt_thread_t rt_thread_create(constchar *name,
                             void (*entry)(void*parameter),
                             void       *parameter,
                             rt_uint32_tstack_size,
                             rt_uint8_t  priority,
                             rt_uint32_t tick)

返回值类型为线程控制块,目的是将初始化好的线程信息写到线程控制块的每个成员变量中,后期再根据线程的状态来修改线程控制块中对应信息的值

删除线程

删除线程函数对应创建线程函数

参数为线程控制块结构体的指针

返回值为错误码,如果删除成功返回RT_EOK、失败返回 -RT_ERROR

/**
 * This function will delete a thread. Thethread object will be removed from
 * thread queue and deleted from system objectmanagement in the idle thread.
 *
 * @param thread the thread to be deleted
 *
 * @return the operation status, RT_EOK on OK,-RT_ERROR on error
 */
rt_err_t rt_thread_delete(rt_thread_tthread)

动态创建线程实例

首先创建一个通过线程块结构体指针创建一个线程结构体指针变量;调用线程创建函数,并用刚刚定义的指针变量来接收返回值

然后定义线程处理函数,并给线程创建函数填入参数,其中处理函数参数设置为NULL,栈空间设置为1024,优先级20,时间片为5(后期定时器细论)。一定要注意参数类型一一对应。

接着通过返回值判断线程是否创建成功,成功用LOG_D打印调试信息,否则用LOG_E打印失败信息。其中RT_NULL的宏值为0

最后编写完线程处理函数。设置一个while循环,在循环中通过rt_kprintf(相当于串口printf)打印输出信息。接着用rt_thread_mdelay函数进行延时1s,用于释放当前CPU资源,让线程调度器调度其它线程。

现象

通过打开串口终端,发现线程被创建成功,但是没有打印运行信息

通过输入list_thread命令查看当前存在线程,发现我们创建的线程已经存在,是处于初始状态的,如果想要运行线程,必须调用线程启动函数rt_thread_startup启动

注意:线程删除函数,我们尽量不要人为去调用。因为我们创建好线程以后,就希望线程能够一直去处理相关的事务。如果线程处理函数里面的处理操作被执行完了(函数运行结束),系统会自动调用线程删除函数来回收资源

启动线程

参数为线程控制块结构体的指针

返回值为错误码,如果删除成功返回RT_EOK、失败返回 -RT_ERROR

/**
 * This function will start a thread and put itto system ready queue
 *
 * @param thread the thread to be started
 *
 * @return the operation status, RT_EOK on OK,-RT_ERROR on error
 */
rt_err_t rt_thread_startup(rt_thread_t thread)

注:当调用这个函数时,将把线程的状态更改为就绪状态,并放到相应优先级队列中等待调度。如果新启动的线程优先级比当前线程优先级高,将立刻切换到这个线程。

在创建线程后,我们如果失败就返回RT_ENOMEM,表示可能由于没有空间导致了创建线程失败。

如果创建成功,就启动线程

运行结果

通过查看线程状态,可发现当前线程处于suspend挂起的状态

这是由于我们在线程处理函数中,大部分的时间都处于休眠(延时函数),休眠就是从运行态或者就绪态切换到挂起状态

初始化和脱离线程

初始化线程

线程的初始化可以使用下面的函数接口完成,来初始化静态线程对象:

参数1:是线程控制块结构体,以指针的形式传入参数

参数2:用于给当前线程起名字

参数3:函数指针,指向了线程处理函数

参数4:用于给线程处理函数传递参数

参数5:栈的起始地址(分配好空间的线程栈的首地址)

参数6:要创建的线程对应线程栈的大小

参数7:线程优先级0-31(由高到低)

参数8:如果优先级相同的线程都处于就绪态,线程调度由tick来决定,按照时间片轮流调度

返回值与动态创建不同,如果错误这里返回的是负数错误码

注:structrt_thread *即rt_thread_t

/**
 * This function will initialize a thread,normally it's used to initialize a
 * static thread object.
 *
 * @param thread the static thread object
 * @param name the name of thread, which shallbe unique
 * @param entry the entry function of thread
 * @param parameter the parameter of threadenter function
 * @param stack_start the start address ofthread stack
 * @param stack_size the size of thread stack
 * @param priority the priority of thread
 * @param tick the time slice if there are samepriority thread
 *
 * @return the operation status, RT_EOK on OK,-RT_ERROR on error
 */
rt_err_t rt_thread_init(structrt_thread *thread,
                        const char       *name,
                        void (*entry)(void *parameter),
                        void             *parameter,
                        void             *stack_start,
                        rt_uint32_t       stack_size,
                        rt_uint8_t        priority,
                       rt_uint32_t       tick)

脱离线程

作用是将线程从当前的线程队列里移除出去

参数是线程控制块结构体,以指针的形式传入参数

返回值为错误码

/**
 * This function will detach a thread. Thethread object will be removed from
 * thread queue and detached/deleted fromsystem object management.
 *
 * @param thread the thread to be deleted
 *
 * @return the operation status, RT_EOK on OK,-RT_ERROR on error
 */
rt_err_t rt_thread_detach(rt_thread_t thread)

静态创建线程实例 

首先创建好线程结构体对象,开辟好栈空间,编写线程处理函数

调用线程初始化函数

运行结果

可以发现两个线程都被创建成功并且处于初始状态,此外tshell为当前终端线程,tidle0为空闲线程,timer为定时器

我们同时启动两个线程,可以发现两个线程交替运行

我们将线程2处理函数中的延时关闭,重新编译下载

成功发现优先级高的线程2运行完之后,才调度线程1

线程辅助函数

获得当前线程

        在程序的运行过程中,相同的一段代码可能会被多个线程执行,在执行的时候可以通过下面的函数接口获得当前执行的线程句柄

/**
 * This function will return self thread object
 *
 * @return the self thread object , failedRT_NULL
 */
rt_thread_t rt_thread_self(void)

让出处理器资源

        该函数的作用让当前运行的线程让出CPU的使用权,恢复到就绪态,调度器将会选择其它处于就绪态中优先级最高的线程去调度

/**
 * This function will let current thread yieldprocessor, and scheduler will
 * choose a highest thread to run. After yieldprocessor, the current thread
 * is still in READY state.
 *
 * @return RT_EOK
 */
rt_err_t rt_thread_yield(void)

线程睡眠

        下面两个函数的作用相同,都是延时一定的时钟节拍数,让我们当前的线程处于睡眠态(阻塞态)

/**
 * This function will let current thread sleepfor some ticks.
 *
 * @param tick the sleep ticks
 *
 * @return RT_EOK
 */
rt_err_t rt_thread_sleep(rt_tick_ttick)
rt_err_t rt_thread_delay(rt_tick_ttick)

第三个函数,延时一段时间ms,让我们当前的线程处于睡眠态(阻塞态)

/**
 * This function will let current thread delayfor some milliseconds.
 *
 * @param tick the delay time
 *
 * @return RT_EOK
 */
rt_err_t rt_thread_mdelay(rt_int32_tms)

控制线程函数

该函数的作用是根据控制命令来控制某个线程的行为

参数1:线程句柄结构体指针

参数2:控制命令

参数3:控制命令所传入的参数;比如我们参数2传入的命令为改变线程优先级,我们就需要传入优先级数值(取地址传入),并强转为(void *)

/**
 * This function will control thread behaviors according to control command.
 *
 * @param thread the specified thread to becontrolled
 * @param cmd the control command, whichincludes
 * RT_THREAD_CTRL_CHANGE_PRIORITY for changing priority level of thread;
 * RT_THREAD_CTRL_STARTUP for starting a thread;  == rt_thread_startup()
 * RT_THREAD_CTRL_CLOSE for delete a thread;      == rt_thread_delete()
 * RT_THREAD_CTRL_BIND_CPU for bind the thread to a CPU.
 * @param arg the argument of control command
 *
 * @return RT_EOK
 */
rt_err_trt_thread_control(rt_thread_t thread, int cmd, void *arg)

控制命令包括:

  • RT_THREAD_CTRL_CHANGE_PRIORITY for changing priority level of thread;
  • RT_THREAD_CTRL_STARTUP for starting a thread;  等同于 rt_thread_startup();
  • RT_THREAD_CTRL_CLOSE for delete a thread;      等同于 rt_thread_delete();
  • RT_THREAD_CTRL_BIND_CPU for bind the thread to a CPU.(将线程绑定在固定的某个CPU来执行,只支持对称多处理器的MCU)

设置和删除idle线程hook函数

RT-Thread向用户提供了一种可以设置用户自定义的空闲线程,也称钩子线程。

一般钩子函数有两个作用:

1. 在线程进行调度切换时,会执行调度,我们可以设置一个调度器钩子,这样可以在线程切换时,做一些额外的事情,这个例子是在调度器钩子函数中打印线程间的切换信息。

2. 在主线程空闲的时候做一些空闲时监控的事情,指示灯闪烁等非紧急的任务。

        在使用钩子函数时候必须保证空闲线程都不会被挂起,也就是说,rt_thread_delay()和re_sem_take() 等会导致线程挂起阻塞的函数都不能被使用在钩子函数中

传入的参数为一个函数指针,传入我们自己定义的钩子函数名

设置钩子函数
/**
 * @ingroup Hook
 * This function sets a hook function to idlethread loop. When the system performs
 * idle loop, this hook function should beinvoked.
 *
 * @param hook the specified hook function
 *
 * @return RT_EOK: set OK
 *        -RT_EFULL: hook list is full
 *
 * @note the hook function must be simple andnever be blocked or suspend.
 */
rt_err_t rt_thread_idle_sethook(void(*hook)(void))
删除钩子函数
/**
 * delete the idle hook on hook list
 *
 * @param hook the specified hook function
 *
 * @return RT_EOK: delete OK
 *        -RT_ENOSYS: hook was not found
 */
rt_err_t rt_thread_idle_delhook(void(*hook)(void))

注意:空闲线程是一个线程状态永远为就绪态的线程,因此设置的钩子函数必须保证空闲线程在任何时刻都不会处于挂起状态,例如 rt_thread_delay(),rt_sem_take() 等可能会导致线程挂起的函数都不能使用。

设置调度器hook函数

        在整个系统的运行时,系统都处于线程运行、中断触发 - 响应中断、切换到其他线程,甚至是线程间的切换过程中,或者说系统的上下文切换是系统中最普遍的事件。有时用户可能会想知道在一个时刻发生了什么样的线程切换,可以通过调用下面的函数接口设置一个相应的钩子函数。在系统线程切换时,这个钩子函数将被调用

/**
 * This function will set a hook function,which will be invoked when thread
 * switch happens.
 *
 * @param hook the hook function
 */void
rt_scheduler_sethook(void (*hook)(struct rt_thread *from, struct rt_thread *to))

通过该函数我们可以知道线程的切换关系,通过我们定义一个回调函数,在回调函数中打印相关信息。当线程进行切换的时候,会自动调用我们设置的回调函数,并将原线程和目标线程的结构体句柄传入到我们的回调函数中,从而执行相关信息操作。

线程调度器hook函数实例

我们先将之前两个线程处理函数的运行打印都设置为5次,方便待会儿查看调度效果

编写自定义hook函数,在函数中打印调度关系

设置hook函数,传入我们自定义的hook函数

运行结果

从中可以看出线程的调度关系为:首先从用户主线程切换到th2线程,然后从th2线程切换到tshell线程,接着从tshell线程切换到idle线程,最终从idle线程切换到th1线程。之后就一直是th1->idle->th2的来回切换。但最终th1线程和th2线程执行完后,会调度idle线程,此时终端继续不显示

此时键盘输入,比如输入“l”,此时终端会显示从idle线程切换到tshell,然后再从tshell切换回idle

更多的hook函数请查看官方参考手册。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

redisson分布式锁学习

什么是分布式锁? 当有多个线程并发访问同一共享数据时,如果多个线程同时都去修改这个共享数据,且修改操作不是原子操作,就很有可能出现线程安全问题&#xff0c;而产生线程安全问题的根本原因是缺乏对共享数据访问的同步和互斥。 为了解决这个问题&#xff0c;通常我们的做法…

pgsql 查看某个表建立了那些索引sql

执行以下sql&#xff1a; SELECTns.nspname as schema_name,tab.relname as table_name,cls.relname as index_name,am.amname as index_type,idx.indisprimary as is_primary,idx.indisunique as is_unique FROMpg_index idx INNER JOIN pg_class cls ON cls.oididx.indexrel…

系统集成|第八章(笔记)

目录 第八章 进度管理8.1 主要过程8.1.1 规划进度管理8.1.2 定义活动8.1.3 排列活动顺序8.1.4 估算活动资源8.1.5 估算活动持续时间8.1.6 制定进度计划8.1.7 控制进度 8.2 注意与问题 上篇&#xff1a;第七章、范围管理 第八章 进度管理 8.1 主要过程 包括&#xff1a; 规划进…

0day用友-NC-Cloud远程代码执行漏洞(全版本通杀)

漏洞描述 用友NC Cloud大型企业数字化平台,深度应用新一代数字智能技术,完全基于云原生架构,打造开放、互联、融合、智能的一体化云平台,聚焦数智化管理、数智化经营、数智化商业等三大企业数智化转型战略方向,提供涵盖数字营销、财务共享、全球司库、智能制造、敏捷供应…

强化学习QLearning 进行迷宫游戏和代码

强化学习是机器学习里面的一个分支。它强调基于环境而探索行动、学习&#xff0c;以取得最大化的预期收益。其灵感来源于心理学中的行为主义理论&#xff0c;既有机体如何在环境给予的奖励或者惩罚的刺激下&#xff0c;逐步形成对刺激的预期&#xff0c;产生能够最大利益的习惯…

计算机视觉实验:图像增强应用实践

本次实验主要从基于统计、函数映射的图像增强方法和基于滤波的图像增强方法两种方法中对一些图像增强的算法进行实现。主要的编程语言为python&#xff0c;调用了python自带的PIL图像库用于读取图像&#xff0c;利用numpy进行图像运算&#xff0c;最后使用opencv第三方库进行对…

奇富科技联合哈银消金获《亚洲银行家》中国最佳信贷项目奖

7月28日&#xff0c;全球金融领域最具含金量的奖项之一《亚洲银行家》颁布2023年度奖项&#xff0c;奇富科技荣获“中国最佳信贷项目”殊荣。值得关注的是&#xff0c;该奖项由奇富科技与哈银消金联合获得&#xff0c;双方联合获奖&#xff0c;是主办方对奇富科技全面赋能金融机…

【限时优惠】红帽openstack管理课程(CL210) 即将开课

课程介绍 通过实验室操作练习&#xff0c;学员将能够深入学习红帽企业 Linux OpenStack 平台各服务的手动安装方法&#xff0c;还将了解 OpenStack 开发社区的未来发展计划。 培训地点&#xff1a; 线下面授&#xff1a;苏州市姑苏区干将东路666号401室&#xff1b; 远程…

Java平台标准版 8 文档

Java 平台标准版 8 文档 (oracle.com)https://docs.oracle.com/javase/8/docs/ JDK 8 是 JRE 8 的超集&#xff0c;包含 JRE 8&#xff0c;以及编译器和调试器等工具&#xff0c;如 开发小程序和应用程序。JRE 8 提供了库&#xff0c; Java 虚拟机 &#xff08;JVM&#xff09;…

ElasticSearch学习之ElasticSearch快速入门实战

1.先“分词” 2.倒排索引&#xff08;前提是分词&#xff09; ElasticSearch官网地址&#xff1a;欢迎来到 Elastic — Elasticsearch 和 Kibana 的开发者 | Elastichttps://www.elastic.co/cn/ 一、下载 下载地址&#xff1a;https://www.elastic.co/cn/downloads/past-re…

C++ ------ 模板初阶

文章目录 泛型编程模板函数模板概念原理函数模板的实例化类模板 泛型编程 我们在实现交换函数的时候&#xff0c;只能实现一个数据类型的交换函数&#xff0c;想要在C中完成对应类型数据的交换一种方法是使用函数重载&#xff0c;就像下面这样 void Swap(int& left, int&am…

MyBatis源码剖析之延迟加载源码细节

文章目录 什么是延迟加载&#xff1f;实现局部延迟加载全局延迟加载 延迟加载原理实现延迟加载原理&#xff08;源码剖析)Setting 配置加载&#xff1a;延迟加载代理对象创建注意事项 什么是延迟加载&#xff1f; 在开发过程中很多时候我们并不需要总是在加载⽤户信息时就⼀定…

Git分布式版本控制工具和GitHub(二)--Git指令入门

一.指令入门前的准备 1.Git全局设置 2.获取Git仓库 例如&#xff1a;将我GitHub上的first_resp仓库克隆到本地。 点击进入first_rep&#xff0c;后面本地仓库操作的学习就是在这个界面右键打开Git Bash 3.工作区&#xff0c;暂存区&#xff0c;版本库概念 注&#xff1a;如果空…

案例研究|康明斯中国通过JumpServer搭建统一的运维安全审计平台

作为全球动力技术先行者&#xff0c;康明斯&#xff08;中国&#xff09;投资有限公司&#xff08;以下简称为康明斯中国&#xff09;设计、制造、分销多元的动力解决方案&#xff0c;并提供服务支持。公司产品囊括柴油及天然气发动机、发电机组、交流发电机、排放处理系统、涡…

使用AOP切面对返回的数据进行脱敏的问题

1.注解类 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/*** Author: xiaoxin* Date: 2023/7/21 17:15*/ Retention(RetentionPolicy.RUNTIME) Targe…

java连接sftp服务器实现上传下载

一、准备SFTP服务器 我目前使用的是freeSSHd.exe,下载后按照步骤一步步安装&#xff0c;最后俩弹窗&#xff0c;第一个选是&#xff0c;第二个选否。 二、基础配置 双击打开安装好的程序&#xff0c;在右下角找到图标&#xff0c;右键&#xff0c;setting 按照步骤配置 …

根据端口号查找服务位置

已知服务的IP和端口&#xff0c;查找该服务所在位置 1、打开命令提示符&#xff08;CMD&#xff09; WINR快捷键打开运行对话框&#xff0c;输入CMD&#xff0c;回车即可。 2、找到对应的PID或程序名称 输入netstat -ano|findstr 端口号&#xff0c;回车找到对应的PID&…

msvcp140.dll丢失怎么修复?《绝地求生》报错msvcp140.dll丢失修复方法

在我运行《绝地求生》游戏的时候&#xff0c;出现msvcp140.dll丢失的错误提示时&#xff0c;感到有些困扰和不安&#xff0c;不知道该如何解决这个问题。同时&#xff0c;我也会担心这个问题会对我使用电脑产生什么不利影响。在尝试解决这个问题之前&#xff0c;我开始意识到我…

ScrumMaster认证课-PSM,了解一下

在敏捷学习的道路上继续前行&#xff0c;Leangoo领歌的PSM课程已经开启&#xff0c;认证全球认可&#xff0c;还不用续证&#xff0c;可以了解一下。 Scrum是目前运用最为广泛的敏捷开发方法&#xff0c;是一个轻量级的项目管理和产品研发管理框架&#xff0c;旨在最短时间内交…

Java课题笔记~数据库连接池

一、数据库连接池 1.1 数据库连接池简介 数据库连接池是个容器&#xff0c;负责分配、管理数据库连接(Connection) 它允许应用程序重复使用一个现有的数据库连接&#xff0c;而不是再重新建立一个&#xff1b; 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数…