RT-Thread--内核基础
内核介绍
- 内核处于硬件层之上,内核部分包括内核库、实时内核实现。
实时内核的实现包括:对象管理、线程管理及调度器、线程间通信管理、时钟管理及内存管理等等,内核最小的资源占用情况是 3KB ROM,1.2KB RAM。
线程调度
- 线程是 RT-Thread 操作系统中最小的调度单位,线程调度算法是基于优先级的全抢占式多线程调度算法,即在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身。
- 支持 256 个线程优先级(也可通过配置文件更改为最大支持 32 个或 8 个线程优先级,针对 STM32 默认配置是 32 个线程优先级),0 优先级代表最高优先级,最低优先级留给空闲线程使用;
- 同时它也支持创建多个具有相同优先级的线程,相同优先级的线程间采用时间片的轮转调度算法进行调度,使每个线程运行相同时间;
- 系统也不限制线程数量的多少,线程数目只和硬件平台的具体内存相关。
时钟管理
- RT-Thread 的时钟管理以时钟节拍为基础,时钟节拍是 RT-Thread 操作系统中最小的时钟单位。
- RT-Thread 的定时器提供两类定时器机制:第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止。第二类是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动的停止定时器否则将永远持续执行下去。
线程间同步
- RT-Thread 采用信号量、互斥量与事件集实现线程间同步。
- 互斥量采用优先级继承的方式解决了实时系统常见的优先级翻转问题。
- 线程同步机制支持线程按优先级等待或按先进先出方式获取信号量或互斥量。线程通过对事件的发送与接收进行同步;事件集支持多事件的 “或触发” 和 “与触发”,适合于线程等待多个事件的情况。
线程间通信
- RT-Thread 支持邮箱和消息队列等通信机制。邮箱中一封邮件的长度固定为 4 字节大小;消息队列能够接收不固定长度的消息,并把消息缓存在自己的内存空间中。
- 邮箱效率较消息队列更为高效。邮箱和消息队列的发送动作可安全用于中断服务例程中。
- 通信机制支持线程按优先级等待或按先进先出方式获取。
内存管理
- RT-Thread 支持静态内存池管理及动态内存堆管理。
- 当静态内存池具有可用内存时,系统对内存块分配的时间将是恒定的;当静态内存池为空时,系统将申请内存块的线程挂起或阻塞掉 (即线程等待一段时间后仍未获得内存块就放弃申请并返回,或者立刻返回等待的时间取决于申请内存块时设置的等待时间参数),当其他线程释放内存块到内存池时,如果有挂起的待分配内存块的线程存在的话,则系统会将这个线程唤醒。
- 动态内存堆管理模块在系统资源不同的情况下,分别提供了面向小内存系统的内存管理算法及面向大内存系统的 SLAB 内存管理算法。
I/O 设备管理
- RT-Thread 将 PIN、I2C、SPI、USB、UART 等作为外设设备,统一通过设备注册完成。实现了按名称访问的设备管理子系统,可按照统一的 API 界面访问硬件设备。
- 对不同的设备可以挂接相应的事件。当设备事件触发时,由驱动程序通知给上层的应用程序。
RT-Thread 启动流程
- MDK裸机启动流程:系统启动后,先从汇编代码startup_stm32f103xe.s 开始运行,然后跳转到 C 代码的main()函数进行运行
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP- RT-Thread在进入main()之前会进行RT-Thread系统功能初始化,
我们使用了 MDK 的扩展功能 $Sub$$ 和$Super$$ 。可以给 main 添加 $Sub$$ 的前缀符号作为一个新功能函数 $Sub$$main ,这个 $Sub$$main 可以先调用一些要补充在 main 之前的功能函数(这里添加 RT-Thread 系统初始化功能),再调用 $Super$$main
转到 main() 函数执行,这样可以让用户不用去管 main() 之前的系统初始化操作。T-Thread 支持多种平台和多种编译器,而 rtthread_startup() 函数是 RT-Thread 规定的统一入口点,所以 $Sub$$main 函数只需调用rtthread_startup() 函数即可;
在 components.c 的代码中找到rtthread_startup() 函数,我们看到 RT-Thread 的启动流程如下图所示:
/* components.c 中定义的这段代码*/
/* re-define main function */
int $Sub$$main(void)
{
rt_hw_interrupt_disable();
rtthread_startup();
return ;
}int rtthread_startup(void)
{
rt_hw_interrupt_disable(); /* board level initialization
* NOTE: please initialize heap inside board initialization.
*/
rt_hw_board_init(); /* show RT-Thread version */
rt_show_version(); /* timer system initialization */
rt_system_timer_init(); /* scheduler system initialization */
rt_system_scheduler_init(); #ifdef RT_USING_SIGNALS
/* signal system initialization */
rt_system_signal_init();
#endif /* create init_thread */
rt_application_init(); /* timer thread initialization */
rt_system_timer_thread_init(); /* idle thread initialization */
rt_thread_idle_init(); /* start scheduler */
rt_system_scheduler_start(); /* never reach here */
return ;
}/* the system main thread */
void main_thread_entry(void *parameter)
{
extern int main(void);
extern int $Super$$main(void); /* RT-Thread components initialization */
rt_components_init(); /* invoke system main function */
#if defined(__CC_ARM) || defined(__CLANG_ARM)
$Super$$main(); /* for ARMCC. */
#elif defined(__ICCARM__) || defined(__GNUC__)
main();
#endif
} void rt_application_init(void)
{
rt_thread_t tid; #ifdef RT_USING_HEAP
tid = rt_thread_create("main", main_thread_entry, RT_NULL,
*, , );
RT_ASSERT(tid != RT_NULL);
- rt_hw_board_init() 中完成系统时钟设置,为系统提供心跳、串口初始化,将系统输入输出终端绑定到这个串口,后续系统运行信息就会从串口打印出来。
- main() 函数是 RT-Thread 的用户代码入口,用户可以在 main() 函数里添加自己的应用。
int main(void)
{
/* user app entry */
return ;
}
RT-Thread 程序内存分布
- 一般 MCU 包含的存储空间有:片内 Flash 与片内 RAM,RAM 相当于内存,Flash 相当于硬盘。编译器会将一个程序分类为好几个部分,分别存储在 MCU 不同的存储区。
- Code:代码段,存放程序的代码部分;
- RO-data:只读数据段,存放程序中定义的常量;
- RW-data:读写数据段,存放初始化为非 0 值的全局变量;
- ZI-data:0 数据段,存放未初始化的全局变量及初始化为 0 的变量;
- RO Size 包含了 Code 及 RO-data,表示程序占用 Flash 空间的大小;
- RW Size 包含了 RW-data 及 ZI-data,表示运行时占用的 RAM 的大小;
- ROM Size 包含了 Code、RO Data 以及 RW Data,表示烧写程序所占用的 Flash 空间的大小;
程序运行之前,需要有文件实体被烧录到 STM32 的 Flash 中,一般是 bin 或者 hex 文件,该被烧录文件称为可执行映像文件。如图下图中左图所示,是可执行映像文件烧录到 STM32 后的内存分布,它包含 RO 段和 RW 段两个部分:其中 RO 段中保存了 Code、RO-data 的数据,RW 段保存了 RW-data 的数据,由于 ZI-data 都是 0,所以未包含在映像文件中。
- STM32 在上电启动之后默认从 Flash 启动,启动之后会将 RW 段中的 RW-data(初始化的全局变量)搬运到 RAM 中,但不会搬运 RO 段,即 CPU 的执行代码从 Flash 中读取,另外根据编译器给出的 ZI 地址和大小分配出 ZI 段,并将这块 RAM 区域清零。
RT-Thread 自动初始化机制
- 自动初始化机制是指初始化函数不需要被显式调用,只需要在函数定义处通过宏定义的方式进行申明,就会在系统启动过程中被执行。
int stm32_hw_usart_init(void)
{
struct stm32_uart *uart;
struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; #ifdef RT_USING_UART1
uart = &uart1;
uart->UartHandle.Instance = USART1; serial1.ops = &stm32_uart_ops;
serial1.config = config; /* register UART1 device */
rt_hw_serial_register(&serial1,
"uart1",
RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
uart);
#endif /* RT_USING_UART1 */
......
......
}
INIT_BOARD_EXPORT(stm32_hw_usart_init);代码最后的 INIT_BOARD_EXPORT(stm32_hw_usart_init) 表示使用自动初始化功能,按照这种方式,stm32_hw_usart_init() 函数就会被系统自动调用
系统启动流程图中,有两个函数:rt_components_board_init() 与 rt_components_init(),其后的带底色方框内部的函数表示被自动初始化的函数
- “board init functions” 为所有通过 INIT_BOARD_EXPORT(fn) 申明的初始化函数。
- “pre-initialization functions” 为所有通过 INIT_PREV_EXPORT(fn) 申明的初始化函数。
- “device init functions” 为所有通过 INIT_DEVICE_EXPORT(fn) 申明的初始化函数。
- “components init functions” 为所有通过 INIT_COMPONENT_EXPORT(fn) 申明的初始化函数。
- “enviroment init functions” 为所有通过 INIT_ENV_EXPORT(fn) 申明的初始化函数。
- “application init functions” 为所有通过 INIT_APP_EXPORT(fn) 申明的初始化函数。
- rt_components_board_init() 函数执行的比较早,主要初始化相关硬件环境,执行这个函数时将会遍历通过 INIT_BOARD_EXPORT(fn) 申明的初始化函数表,并调用各个函数。
- rt_components_init() 函数会在操作系统运行起来之后创建的 main 线程里被调用执行,这个时候硬件环境和操作系统已经初始化完成,可以执行应用相关代码。
- RT-Thread 的自动初始化机制使用了自定义 RTI 符号段,将需要在启动时进行初始化的函数指针放到了该段中,形成一张初始化函数表,在系统启动过程中会遍历该表,并调用表中的函数,达到自动初始化的目的。
- 用来实现自动初始化功能的宏接口定义详细描述如下
- 初始化函数主动通过这些宏接口进行申明,如 INIT_BOARD_EXPORT(rt_hw_usart_init),链接器会自动收集所有被申明的初始化函数,放到 RTI 符号段中,该符号段位于内存分布的 RO 段中,该 RTI符号段中的所有函数在系统初始化时会被自动调用。
RT-Thread 内核对象模型
静态对象和动态对象
- 静态内核对象和动态内核对象,静态内核对象通常放在RW 段和 ZI 段中,在系统启动后在程序中初始化;动态内核对象则是从内存堆中创建的,而后手工做初始化。
static rt_uint8_t main_stack[];
struct rt_thread main_thread; void rt_application_init(void)
{
rt_thread_t tid; #ifdef RT_USING_HEAP
/*
* 线程名:main
* 线程入口:main_thread_entry
* 参数:RT_NULL
* 站空间大小:1024*100
* 优先级:0
* 时间片:20个OS Tick
*/
tid = rt_thread_create("main", main_thread_entry, RT_NULL, //动态线程
*, , );
RT_ASSERT(tid != RT_NULL);
#else
rt_err_t result; tid = &main_thread;
/*
* 静态线程对象:main_thread
* 线程名:main
* 线程入口:main_thread_entry
* 参数:RT_NULL
* 线程起始地址:main_stack
* 线程堆栈大小:sizeof(main_stack)
* 优先级:RT_MAIN_THREAD_PRIORITY
* 时间片:20个OS Tick
*/
result = rt_thread_init(tid, "main", main_thread_entry, RT_NULL, //静态线程
main_stack, sizeof(main_stack), RT_MAIN_THREAD_PRIORITY, );
RT_ASSERT(result == RT_EOK); /* if not define RT_USING_HEAP, using to eliminate the warning */
(void)result;
#endif rt_thread_startup(tid); //启动线程
}
- main_thread 是一个静态线程对象,线程控制块 main_thread 与栈空间 main_stack 都是编译时决定的;
- mian是一个动态线程对象,用到的空间是动态分配的,包括线程控制块和栈空间;
- 静态对象会占用 RAM 空间,不依赖于内存堆管理器,内存分配时间确定。动态对象则依赖于内存堆管理器,运行时申请 RAM 空间,当对象被删除后,占用的 RAM 空间被释放;
内核对象管理架构
- RT-Thread 采用内核对象管理系统来访问 / 管理所有内核对象,内核对象包含了内核中绝大部分设施,这些内核对象可以是静态分配的静态对象,也可以是从系统内存堆中分配的动态对象。
- RT-Thread 内核对象包括:线程,信号量,互斥量,事件,邮箱,消息队列和定时器,内存池,设备驱动等。对象容器中包含了每类内核对象的信息,包括对象类型,大小等。对象容器给每类内核对象分配了一个链表,所有的内核对象都被链接到该链表上
- RT-Thread 中各类内核对象的派生和继承关系
rt_object 派生出来的有:线程对象、内存池对象、定时器对象、设备对象和 IPC对象(IPC:Inter-Process Communication,进程间通信。在 RT-Thread 实时操作系统中,IPC 对象的作用是进行线程间同步与通信);由 IPC 对象派生出信号量、互斥量、事件、邮箱与消息队列、信号等对象。
- 对于每一种具体内核对象和对象控制块,除了基本结构外,还有自己的扩展属性(私有属性),例如,对于线程控制块,在基类对象基础上进行扩展,增加了线程状态、优先级等属性。这些属性在基类对象的操作中不会用到,只有在与具体线程相关的操作中才会使用。因此从面向对象的观点,可以认为每一种具体对象是抽象对象的派生,继承了基本对象的属性并在此基础上扩展了与自己相关的属性。
对象控制块
- 内核对象控制块的数据结构
/**
* Base structure of Kernel object
*/
struct rt_object
{
char name[RT_NAME_MAX]; /**< name of kernel object 内核对象名称*/
rt_uint8_t type; /**< type of kernel object 内核对象类型*/
rt_uint8_t flag; /**< flag of kernel object 内核对象的参数*/ #ifdef RT_USING_MODULE
void *module_id; /**< id of application module 内核对象模型id*/
#endif
rt_list_t list; /**< list node of kernel object 内核对象管理链表*/
};
typedef struct rt_object *rt_object_t; /**< Type for kernel objects. */- 内核对象支持的类型:
enum rt_object_class_type
{
RT_Object_Class_Thread = , /**< The object is a thread. 线程类型*/
RT_Object_Class_Semaphore, /**< The object is a semaphore. 信号量类型*/
RT_Object_Class_Mutex, /**< The object is a mutex. 互斥量类型*/
RT_Object_Class_Event, /**< The object is a event. 时间类型*/
RT_Object_Class_MailBox, /**< The object is a mail box. 邮箱类型*/
RT_Object_Class_MessageQueue, /**< The object is a message queue. 消息队列类型*/
RT_Object_Class_MemHeap, /**< The object is a memory heap 内存堆类型*/
RT_Object_Class_MemPool, /**< The object is a memory pool. 内存池类型*/
RT_Object_Class_Device, /**< The object is a device 设备类型*/
RT_Object_Class_Timer, /**< The object is a timer. 定时器类型*/
RT_Object_Class_Module, /**< The object is a module. 模块*/
RT_Object_Class_Unknown, /**< The object is unknown. 未知类型*/
RT_Object_Class_Static = 0x80 /**< The object is a static object. 静态类型*/
};如果是静态对象,那么对象类型的最高位将是 1(是RT_Object_Class_Static 与其他对象类型的与操作),否则就是动态对象,系统最多能够容纳的对象类别数目是 127 个。
内核对象管理方式
- 内核对象容器的数据结构
/**
* The information of the kernel object
*/
struct rt_object_information
{
enum rt_object_class_type type; /**< object class type */
rt_list_t object_list; /**< object list */
rt_size_t object_size; /**< object size */
};一类对象由一个 rt_object_information 结构体来管理,每一个这类对象的具体实例都通过链表的形式挂接在 object_list 上。而这一类对象的内存块尺寸由 object_size 标识出来
初始化对象
- 在使用一个未初始化的静态对象前必须先对其进行初始化。初始化对象使用接口:
void rt_object_init(struct rt_object *object, //需要初始化的对象指针,它必须指向具体的对象内存块,而不能是空指针或野指针
enum rt_object_class_type type, //对象的类型,必须是 rt_object_class_type 枚举类型中列出的除RT_Object_Class_Static 以外的类型
const char *name) //对象的名字。每个对象可以设置一个名字,这个名字的最大长度由 RT_NAME_MAX指定
脱离对象
- 从内核对象管理器中脱离一个对象。脱离对象使用以下接口:
/**
* This function will detach a static object from object system,
* and the memory of static object is not freed.
*
* @param object the specified object to be detached.
*/
void rt_object_detach(rt_object_t object)
分配对象
- 对象初始化、脱离的接口,都是面向对象内存块已经有的情况下,而动态的对象则可以在需要时申请,不需要时释放出内存空间给其他应用使用。申请分配新的对象可以使用以下接口:
/**
* This function will allocate an object from object system
*
* @param type the type of object
* @param name the object name. In system, the object's name must be unique.
*
* @return object
*/
rt_object_t rt_object_allocate(enum rt_object_class_type type, const char *name)
删除对象
- 对于一个动态对象,当不再使用时,可以调用如下接口删除对象,并释放相应的系统资源:
/**
* This function will delete an object and release object memory.
*
* @param object the specified object to be deleted.
*/
void rt_object_delete(rt_object_t object)- 当调用以上接口时,首先从对象容器链表中脱离对象,然后释放对象所占用的内存。
辨别对象
- 判断指定对象是否是系统对象(静态内核对象)。辨别对象使用以下接口:
/**
* This function will judge the object is system object or not.
* Normally, the system object is a static object and the type
* of object set to RT_Object_Class_Static.
*
* @param object the specified object to be judged.
*
* @return RT_TRUE if a system object, RT_FALSE for others.
*/
rt_bool_t rt_object_is_systemobject(rt_object_t object)在 RT-Thread 操作系统中,一个系统对象也就是一个静态对象,对象类型标识上 RT_Object_Class_Static 位置位。通常使用 rt_object_init() 方式初始化的对象都是系统对象。
RT-Thread 内核配置示例
- RT-Thread 的一个重要特性是高度可裁剪性,支持对内核进行精细调整,对组件进行灵活拆卸。配置主要是通过修改工程目录下的 rtconfig.h 文件来进行,用户可以通过打开 / 关闭该文件中的宏定义来对代码进行条件编译,最终达到系统配置和裁剪的目的。
- 在实际应用中,系统配置文件 rtconfig.h 是由配置工具自动生成的,无需手动更改。
/* RT-Thread 内核部分 */ #define RT_NAME_MAX 8 // 表示内核对象的名称的最大长度,若代码中对象名称的最大长度大于宏定义的长度,*多余的部分将被截掉。
#define RT_ALIGN_SIZE 4 // 字节对齐时设定对齐的字节个数。常使用 ALIGN(RT_ALIGN_SIZE) 进行字节对齐。
#define RT_THREAD_PRIORITY_32
#define RT_THREAD_PRIORITY_MAX 32 // 定义系统线程优先级数;通常用 RT_THREAD_PRIORITY_MAX-1 定义空闲线程的优先级
#define RT_TICK_PER_SECOND 1000 // 定义时钟节拍,为 1000 时表示 1000 个 tick 每秒,一个 tick 为 10ms
#define RT_USING_OVERFLOW_CHECK // 检查栈是否溢出,未定义则关闭
#define RT_USING_HOOK // 定义该宏使用 HOOK
#define RT_IDEL_HOOK_LIST_SIZE 4 // 定义HOOK链表大小
#define IDLE_THREAD_STACK_SIZE 1024 // 定义线程栈大小
#define RT_DEBUG // 定义该宏开启 debug 模式,未定义则关闭 /* 线程间同步与通信部分,该部分会使用到的对象有信号量、互斥量、事件、邮箱、消息队列、信号等。 */ #define RT_USING_SEMAPHORE // 定义该宏可开启信号量的使用,未定义则关闭
#define RT_USING_MUTEX // 定义该宏可开启互斥量的使用,未定义则关闭
#define RT_USING_EVENT // 定义该宏可开启事件集的使用,未定义则关闭
#define RT_USING_MAILBOX // 定义该宏可开启邮箱的使用,未定义则关闭
#define RT_USING_MESSAGEQUEUE // 定义该宏可开启消息队列的使用,未定义则关闭 /* 内存管理部分 */ #define RT_USING_MEMPOOL // 开启静态内存池的使用
#define RT_USING_MEMHEAP // 定义该宏可开启两个或以上内存堆拼接的使用,未定义则关闭
#define RT_USING_SMALL_MEM // 开启小内存管理算法
#define RT_USING_HEAP // 开启堆的使用 /* 内核设备对象 */ #define RT_USING_DEVICE // 表示开启了系统设备的使用
#define RT_USING_CONSOLE // 定义该宏可开启系统控制台设备的使用,未定义则关闭
#define RT_CONSOLEBUF_SIZE 128 // 定义控制台设备的缓冲区大小
#define RT_CONSOLE_DEVICE_NAME "uart1" // 控制台设备的名称 /* 自动初始化方式 */ #define RT_USING_COMPONENTS_INIT // 定义该宏开启自动初始化机制,未定义则关闭 /* Command shell */ #define RT_USING_FINSH // 定义该宏可开启系统 FinSH 调试工具的使用,未定义则关闭
#define FINSH_THREAD_NAME "tshell" // 开启系统 FinSH 时:将该线程名称定义为 tshell
#define FINSH_USING_HISTORY // 开启系统 FinSH 时:使用历史命令
#define FINSH_HISTORY_LINES 5 // 开启系统 FinSH 时:对历史命令行数的定义
#define FINSH_USING_SYMTAB // 开启系统 FinSH 时:定义该宏开启使用 Tab 键,未定义则关闭
#define FINSH_USING_DESCRIPTION // 开启系统 FinSH 时:定义该宏使用描述,未定义则关闭
#define FINSH_THREAD_PRIORITY 20 // 开启系统 FinSH 时:定义该线程的优先级
#define FINSH_THREAD_STACK_SIZE 2048 // 开启系统 FinSH 时:定义该线程的栈大小
#define FINSH_CMD_SIZE 80 // 开启系统 FinSH 时:定义命令字符长度 /* Device virtual file system */ #define RT_USING_DFS // 使用DFS文件系统
#define DFS_USING_WORKDIR // 使用工作目录
#define DFS_FILESYSTEMS_MAX 4 // 文件系统
#define DFS_FILESYSTEM_TYPES_MAX 4 // 文件系统类型
#define DFS_FD_MAX 8 // DF容器大小
#define RT_USING_DFS_ELMFAT // 使用elmfat文件系统
常见宏定义说明
- RT-Thread 中经常使用一些宏定义,举例 Keil 编译环境下一些常见的宏定义:rtdef.h
rt_inline,定义如下,static 关键字的作用是令函数只能在当前的文件中使用;inline 表示内联,用static 修饰后在调用函数时会建议编译器进行内联展开。
#define rt_inline static __inline
RT_USED,定义如下,该宏的作用是向编译器说明这段代码有用,即使函数中没有调用也要保留编译。例如 RT-Thread 自动初始化功能使用了自定义的段,使用 RT_USED 会将自定义的代码段保留。
#define RT_USED __attribute__((used))
- RT_UNUSED,定义如下,表示函数或变量可能不使用,这个属性可以避免编译器产生警告信息。
#define RT_UNUSED __attribute__((unused))
RT_WEAK,定义如下,常用于定义函数,编译器在链接函数时会优先链接没有该关键字前缀的函数,如果找不到则再链接由 weak 修饰的函数
#define RT_WEAK __weak
ALIGN(n),定义如下,作用是在给某对象分配地址空间时,将其存放的地址按照 n 字节对齐,这里 n 可取 2 的幂次方。字节对齐的作用不仅是便于 CPU 快速访问,同时合理的利用字节对齐可以有效地节省存储空间。
#define ALIGN(n) __attribute__((aligned(n)))
RT_ALIGN(size,align),定义如下,作用是将 size 提升为 align 定义的整数的倍数,例如,RT_ALIGN(13,4) 将返回 16。
#define RT_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1))
参考
- 《RT-Thread 编程指南》
RT-Thread--内核基础的更多相关文章
- RT Thread 通过ENV来配置SFUD,操作SPI Flash
本实验基于正点原子stm32f4探索者板子 请移步我的RT Thread论坛帖子. https://www.rt-thread.org/qa/forum.php?mod=viewthread& ...
- Windows内核基础知识-8-监听进程、线程和模块
Windows内核基础知识-8-监听进程.线程和模块 Windows内核有一种强大的机制,可以在重大事件发送时得到通知,比如这里的进程.线程和模块加载通知. 本次采用链表+自动快速互斥体来实现内核的主 ...
- linux内核基础(系统调用,简明)
内核基础(系统调用) 在说系统调用之前.先来说说内核是怎么和我们交互的.或者说是怎么和我们产生交集的. 首先,内核是用来控制硬件的仅仅有内核才干直接控制硬件,所以说内核非常重要,假设内核被控制那么电脑 ...
- Linux内核基础优化
Linux内核基础优化 net.ipv4.ip_forward = 1 #开启网络转发 net.ipv4.conf.default.rp_filter = 0 #开启代理功能 net.ipv4.con ...
- Windows内核基础知识-1-段寄存器
Windows内核基础知识-1-段寄存器 学过汇编的应该都知道段寄存器,在Windows里段寄存器有很多,之前可能只接触了ds数据段,cs 代码段这种,今天这个博客就介绍Windows一些比较常用的段 ...
- Windows内核基础知识-2-段描述符
Windows内核基础知识-2-段描述符 比如: ES 002B 0(FFFFFFFF) 意思就是es段寄存器,段选择子/段选择符 为002B, 起始地址base为0, 限制范围Limit地址最大能寻 ...
- Windows内核基础知识-5-调用门(32-Bit Call Gate)
Windows内核基础知识-5-调用门(32-Bit Call Gate) 调用门有一个关键的作用,就是用来提权.调用门其实就是一个段. 调用门: 这是段描述符的结构体,里面的s字段用来标记是代码段还 ...
- windows内核基础与异常处理
前两日碰到了用异常处理来做加密的re题目 所以系统学习一下windows内核相关 windows内核基础 权限级别 内核层:R0 零环 核心态工作区域 大多数驱动程序 应用层:R3 用户态工作区域 只 ...
- STM32 + RT Thread OS 学习笔记[二]
串口通讯例程 通过上面的练习,对STM32项目开发有了一个直观印象,接下来尝试对串口RS232进行操作. 1. 目标需求: 开机打开串口1,侦听上位机(使用电脑串口测试软件)发送的信息,然后原样输 ...
- STM32 + RT Thread OS 串口通讯
1. 创建项目 a) 禁用Finsh和console b) 默认情况下,项目文件包含了finsh,它使用COM1来通讯,另外,console输出(rt_kprintf)也使用了COM1.因 ...
随机推荐
- VMware VSAN 设计规则
1.集群节点数量:3-64台主机(生产环境最少4节点起,5.5版本支持32节点,6.0版本支持64节点),配置万兆网卡,主机规格应满足VSAN兼容性要求. 2.每台主机需配置磁盘组,每台主机的磁盘组数 ...
- 移动端自动化测试之adb常用命令
今天我们来聊聊自动化测试过程中常用的adb命令. 简介 首先介绍下什么是adb,adb全称叫“android debug bridge”,翻译过来就叫调试桥,通过命令行指令,可让你与移动端设备进行相互 ...
- 解决jdk卸载出错2502、2503
之前装的jdk1.6,后来软件要求用1.8,就卸载了1.6,卸载的时候出现了这个问题.后来又有其他软件用1.8出错,就又要装1.6,脑壳疼.网上建议先卸载1.8再装低版本,结果卸载1.8,又出现错误2 ...
- 2017年度好视频,吴恩达、李飞飞、Hinton、OpenAI、NIPS、CVPR、CS231n全都在
我们经常被问:机器翻译迭代了好几轮,专业翻译的饭碗都端不稳了,字幕组到底还能做什么? 对于这个问题,我们自己感受最深,却又来不及解释,就已经边感受边做地冲出去了很远,摸爬滚打了一整年. 其实,现在看来 ...
- .net和ASP.net,c#的区别
.NET.C#和ASP.NET三者之间的区别如下: 一.什么是.NET?.NET是微软公司下的一个开发平台,.NET核心就是.NET Framwork(.NET框架)是.NET程序开发和运行的环境,在 ...
- Docker部署Gitlab11.10.4
1.下载镜像 官方镜像地址:https://hub.docker.com/r/gitlab/gitlab-ce ,根据自己需要下载指定版本 [root@vanje-dev01 ~]# docker p ...
- wordpress 图片上传时发生了错误,请稍后再试 的解决办法
前一天网站还是好好的,仅一天过后就显示图片无法上传,百思不得其解 上下百度,大多数帖子提供的解决办法对我都不适用,继续搜,最后发现一篇帖子中提到是wp-config的编码格式问题 想到昨天刚好修改了下 ...
- MySQL之mysqldump数据备份还原
一 mysqldump指令实现数据备份.mysql指令实现数据还原 经常有朋友问我,DBA到底是做什么的,百科上说:数据库管理员(Database Administrator,简称DBA),是从事管理 ...
- 20191030-Python实现闭包
打算在过年前每天总结一个知识点,所以把自己总结的知识点分享出来,中间参考了网络上很多大神的总结,但是发布时候因为时间太久可能没有找到原文链接,如果侵权请联系我删除 20191030:闭包 首先一个函数 ...
- Go学习路径--相关基础
现在开始接触Go一段时间了,基本路径就是看基础学习材料,开始写项目,有问题找解决问题的方法.这里记录一下学习过程. go相关文章 Golang适合高并发场景的原因分析 go build 不同系统下的可 ...