【Linux初阶】多线程1 | 页表的索引作用,线程基础(优缺点、异常、用途),线程VS进程,线程控制,C++多线程引入

文章目录
- ☀️一、深入理解页表
- ☀️二、Linux线程概念
- 🌻1.什么是线程(重点)
- ⚡(1)线程的概念
- ⚡(2)线程库初识
- 🌻2.线程的优点
- 🌻3.线程的缺点
- 🌻4.线程异常(重点)
- 🌻5.线程用途
- ☀️三、Linux线程VS进程
- 🌻1.线程和进程
- 🌻2.线程共享
- ☀️四、Linux线程控制
- 🌻1.POSIX线程库
- 🌻2.创建线程
- 🌻3.线程终止
- 🌻4.线程等待
- 🌻5.线程控制代码示例
- ⚡(1)简单版(重点)
- ⚡(2)复杂版
- ☀️五、C++多线程引入
- ☀️结语
☀️一、深入理解页表

知识点1
- 地址空间(task_struct)是进程能看到的资源窗口。
- 页表决定进程真正拥有的资源情况。
- 合理的对地址空间+页表进行资源划分,我们就可以对一个进程的所有资源进行分类。
知识点2
- 磁盘的数据储存单元为
页帧(4KB),物理内存被划分为一个一个数据页/页框(4KB)进行管理,磁盘可以将数据加载到内存。 虚拟地址需要用32位比特位来表示,设计者们有意的将其拆分为10、10、12的位数进行应用,方便对内存空间的映射。- 页表分为
页目录、页表项。 页目录使用虚拟地址的前10位进行索引,指向不同的页表项。页表项使用虚拟地址的中10位进行索引,指向不同物理内存中的页框的起始地址。操作系统依据某一页框的起始地址,以虚拟地址后12位作为偏移量,找到该页框内某一具体物理地址。- 在读取或写入某个数据时,可直接根据数据大小,在某一具体物理地址直接向下读取即可。
- 页表根据需要进行创建,不发生映射关系时,对应的数据页表不创建,可大大减少资源占用。
☀️二、Linux线程概念
🌻1.什么是线程(重点)
⚡(1)线程的概念
- 在一个程序里的一个执行路线就叫做
线程(thread)。更准确的定义是:线程是“一个进程内部的控制序列”。 - 一个进程至少有一个执行线程,可以有多个执行线程。
- 线程在进程内部运行,本质是在进程地址空间内运行,它拥有进程的一部分资源。
- 在Linux系统中,在CPU眼中,看到的PCB都要比传统的进程更加轻量化。
- 透过进程虚拟地址空间,可以看到进程的大部分资源,将进程资源合理分配给每个执行流,就形成了线程执行流。
- 在Linux下,每个线程共享一个地址空间,它们使用 task_strcut类型的数据结构对虚拟内存进行管理,进而对整个进程资源分配。
- 在window下,有为线程专门设计的数据结构(TCB)。

- Linux内核中有没有真正意义的线程呢?没有,Linux是用进程PCB来模拟线程的,是一套完全属于自己的线程方案。
- 站在CPU视角,每一个PCB,都可以将其称之为轻量级进程。
- Linux线程是CPU调度的基本单位,而进程是承担系统资源分配的基本单位。
- 进程用来申请整体资源,线程用来伸手向进程要资源。
- Linux没有真正意义上的线程 - Linux无法直接向外部提供线程的系统调用接口,而只能给我们提供创建轻量级进程的接口(
pthread库接口)。 - 好处:简单,维护成本低 - 可靠高效(避免数据结构复杂化)。
⚡(2)线程库初识
- 我们可以通过调用用户级线程库(pthread库),让库帮我们访问对应的系统调用接口,创建
轻量级进程,也就是我们Linux下的线程。 - 任何一款Linux操作系统都会默认携带这个
pthread库,这种我们将其称之为原生线程库。
mythread:mythread.ccg++ -o $@ $^ -lpthread -std=c++11 //-lpthread - 使用pthread库
.PHONY:clean
clean:rm -f mythread

- 多线程程序运行起来之后

LWP:light weight process - 轻量级进程id。PID = LWP,该线程为主线程。PID != LWP,该线程为新线程。- CPU调度的时候,以LWP为标识符表示特定执行流。
- 创建一个线程 -> 创建PCB -> 把对应的代码分配给它(分配一个函数给它)
🌻2.线程的优点
- 创建一个新线程的代价要比创建一个新进程小得多。
- 与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多。
- 线程占用的资源要比进程少很多。
- 能充分利用多处理器的可并行数量。
- 在等待慢速I/O操作结束的同时,程序可执行其他的计算任务。
- 计算密集型应用(加密、解密、算法),为了能在多处理器系统上运行,将计算分解到多个线程中实现。
- I/O密集型应用(外设、网络拉取),为了提高性能,将I/O操作重叠。线程可以同时等待不同的I/O操作。
重点:与进程之间的切换相比,线程之间的切换需要操作系统做的工作要少很多。1.进程:切换页表 & 虚拟空间地址 & 切换PCB & 切换上下文。2.线程:切换PCB & 切换上下文。
知识补充:CPU内部会集成一个硬件cache,它负责缓存常用的热点数据,当线程切换时,cache不用被切换,CPU/寄存器 可以直接从该集成硬件中直接读取数据。当进程切换时,cache需要被切换。
🌻3.线程的缺点
- 性能损失
一个很少被外部事件阻塞的计算密集型线程往往无法与共它线程共享同一个处理器。如果计算密集型线程的数量比可用的处理器多,那么可能会有较大的性能损失,这里的性能损失指的是增加了额外的同步和调度开销,而可用的资源不变。
- 健壮性/鲁棒性 降低(重点)
1.编写多线程需要更全面更深入的考虑,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的。2.一个线程出异常,会影响其他线程,因为信号是发给进程整体的,同一个进程内的不同线程共享同一个PID。
- 缺乏访问控制
进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响。
- 编程难度提高。
编写与调试一个多线程程序比单线程程序困难得多。
🌻4.线程异常(重点)
- 单个线程如果出现除零,野指针问题导致线程崩溃,进程也会随着崩溃。
- 线程是进程的执行分支,线程出异常,就类似进程出异常,进而触发信号机制,终止进程,进程终止,该进程内的所有线程也就随即退出。
多线程 不等于 多进程(父子进程),子进程崩溃不一定会影响父进程运行。
🌻5.线程用途
-
合理的使用多线程,能提高CPU密集型程序的执行效率。
-
合理的使用多线程,能提高IO密集型程序的用户体验(如生活中我们一边写代码一边下载开发工具,就是
多线程运行的一种表现)。
☀️三、Linux线程VS进程
🌻1.线程和进程
- 进程是资源分配的基本单位
- 线程是CPU调度的基本单位
- 线程共享进程数据,但也拥有自己的一部分数据:
线程ID,一组寄存器(上下文),栈,errno,信号屏蔽字,调度优先级
总结:线程一旦被创建,几乎所有资源都是被线程共享的。线程也有自己的私有资源,1.PCB属性私有;2.一定私有的上下文结构;3.每个线程都有自己独立的栈结构。
🌻2.线程共享
进程的多个线程共享同一地址空间,因此Text Segment(代码段)、Data Segment(数据段)都是共享的,如果定义一个函数,在各线程中都可以调用,如果定义一个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:
- 文件描述符表
- 每种信号的处理方式(SIG_ IGN、SIG_ DFL或者自定义的信号处理函数)
- 当前工作目录
- 用户id和组id
进程和线程的关系如下图:

☀️四、Linux线程控制
🌻1.POSIX线程库
- 与线程有关的函数构成了一个完整的系列,绝大多数函数的名字都是以
“pthread_”打头的。 - 要使用这些函数库,要通过引入
头文件<pthread.h>。 - 链接这些线程函数库时要使用编译器命令的
“-lpthread”选项。
mythread:mythread.ccg++ -o $@ $^ -lpthread -std=c++11 //-lpthread - 使用pthread库
.PHONY:clean
clean:rm -f mythread
🌻2.创建线程
功能:创建一个新的线程
原型int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *
(*start_routine)(void*), void *arg);
参数thread:返回线程IDattr:设置线程的属性,attr为NULL表示使用默认属性start_routine:是个函数地址,线程启动后要执行的函数arg:传给线程启动函数的参数
返回值:成功返回0;失败返回错误码
错误检查
- 传统的一些函数是,成功返回0,失败返回-1,并且对全局变量errno赋值以指示错误。
- pthreads函数出错时不会设置全局变量errno(而大部分其他POSIX函数会这样做),而是将错误代码通过返回值返回。
- pthreads同样也提供了线程内的errno变量,以支持其它使用errno的代码。对于pthreads函数的错误,建议通过返回值判定,因为读取返回值要比读取线程内的errno变量的开销更小
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>void* rout(void* arg) {int i;for (; ; ) {printf("I'am thread 1\n");sleep(1);}
}
int main(void)
{pthread_t tid;int ret;if ((ret = pthread_create(&tid, NULL, rout, NULL)) != 0) {fprintf(stderr, "pthread_create : %s\n", strerror(ret)); //打印创建失败信息exit(EXIT_FAILURE);}int i;for (; ; ) {printf("I'am main thread\n");sleep(1);}
}
🌻3.线程终止
如果需要只终止某个线程而不终止整个进程,可以有三种方法:
- 从线程函数return。(return nullptr;)这种方法对主线程不适用,从main函数return相当于调用exit。
- 线程可以调用
pthread_ exit终止自己。 - 一个线程可以调用
pthread_ cancel终止同一进程中的另一个线程。
- pthread_exit函数
功能:线程终止
原型void pthread_exit(void* value_ptr);
参数value_ptr : value_ptr不要指向一个局部变量。
返回值:无返回值,跟进程一样,线程结束的时候无法返回到它的调用者(自身)
需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是用malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。
常见使用方式:pthread_exit(nullptr);
- pthread_cancel函数
功能:取消一个执行中的线程
原型int pthread_cancel(pthread_t thread);
参数thread:线程ID
返回值:成功返回0;失败返回错误码
🌻4.线程等待
- 线程也是需要被等待的!如果不等待,就会出现类似僵尸进程的问题 - 内存泄漏。
- 即已经退出的线程,其空间没有被释放,仍然在进程的地址空间内,创建新的线程不会复用刚才退出线程的地址空间。、
- 线程等待可以获取线程的退出信息,并回收对应的PCB等内核资源,防止内存泄漏。
功能:等待线程结束
原型int pthread_join(pthread_t thread, void **value_ptr);
参数thread:线程IDvalue_ptr:它指向一个指针,后者指向线程的返回值(void*是一个输出型参数,指向线程的返回值)
返回值:成功返回0;失败返回错误码
常见用法:void *ret = nullptr;int n = pthread_join(tid, (void**)&ret);assert(n == 0);
- 当我们线程
return (void*)n的时候,pthread库会为我们维护一个小的变量用于保存这个(void*)n变量,我们可以通过 (void**)&ret 的方法将库中的变量拿出来。 - 我们可以通过下图加深理解,
int **pp = &p,*pp=p,*p=a,若a为我们要取出来的值,则(void**)&p两次解引用即可得到a的值。

🌻5.线程控制代码示例
⚡(1)简单版(重点)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>void* thread1(void* arg)
{printf("thread 1 returning ... \n");int* p = (int*)malloc(sizeof(int));*p = 1;return (void*)p;
}void* thread2(void* arg)
{printf("thread 2 exiting ...\n");int* p = (int*)malloc(sizeof(int));*p = 2;pthread_exit((void*)p);
}void* thread3(void* arg)
{while (1) { //printf("thread 3 is running ...\n");sleep(1);}return NULL;
}int main(void)
{pthread_t tid;void* ret;// thread 1 returnpthread_create(&tid, NULL, thread1, NULL);pthread_join(tid, &ret);printf("thread return, thread id %X, return code:%d\n", tid, *(int*)ret);free(ret);// thread 2 exitpthread_create(&tid, NULL, thread2, NULL);pthread_join(tid, &ret);printf("thread return, thread id %X, return code:%d\n", tid, *(int*)ret);free(ret);// thread 3 cancel by otherpthread_create(&tid, NULL, thread3, NULL);sleep(3);pthread_cancel(tid);pthread_join(tid, &ret);if (ret == PTHREAD_CANCELED)printf("thread return, thread id %X, return code:PTHREAD_CANCELED\n", tid);elseprintf("thread return, thread id %X, return code:NULL\n", tid);
}运行结果:
[root@localhost linux]# . / a.out
thread 1 returning ...
thread return, thread id 5AA79700, return code:1
thread 2 exiting ...
thread return, thread id 5AA79700, return code : 2
thread 3 is running ...
thread 3 is running ...
thread 3 is running ...
thread return, thread id 5AA79700, return code : PTHREAD_CANCELED
⚡(2)复杂版
复杂版涉及线程循环创建、等待、删除
- makefile
mythread:mythread.ccg++ -o $@ $^ -lpthread -std=c++11 //-lpthread - 使用pthread库
.PHONY:clean
clean:rm -f mythread
- mythread.cc
#include <iostream>#include <cstdlib>#include <string>#include <cassert>#include <vector>#include <pthread.h>#include <unistd.h>using namespace std;// 当成结构体使用class ThreadData{public:int number;pthread_t tid;char namebuffer[64];};class ThreadReturn{public:int exit_code;int exit_result;};//1. start_routine, 现在是被几个线程执行呢?10, 这个函数现在是什么状态?重入状态//2. 该函数是可重入函数吗?是的!(可在任意时刻被打断,恢复运行时不会丢失数据)//3. 在函数内定义的变量,都叫做局部变量,具有临时性 -- 今天依旧适用// -- 在多线程情况下, 也没有问题 -- 其实每一个线程都有自己独立的栈结构!void *start_routine(void *args){ThreadData *td = static_cast<ThreadData *>(args); // 安全的进行强制类型转化int cnt = 10;while (cnt){cout << "cnt: " << cnt << " &cnt: " << &cnt << endl; // bugcnt--;sleep(1);// return nullptr; // 线程函数结束,return的时候,线程就算终止了// pthread_exit(nullptr);// exit(0); // 能不能用来终止线程,不能,因为exit是终止进程的!,任何一个执行流调用exit都会让整个进程退出}// 线程如何终止的问题// return (void*)td->number; // warning, void *ret = (void*)td->number;// return (void *)106;// pthread_exit((void*)111); ThreadReturn * tr = new ThreadReturn();tr->exit_code = 1;tr->exit_result = 106;// ThreadReturn tr; // warning,在栈上开辟的空间 return &tr,退出后数据就拿不到了;//return (void*)tr; //右值return (void*)100;}int main(){// 1. 我们想创建一批线程vector<ThreadData*> threads;#define NUM 10for(int i = 0; i < NUM; i++){ThreadData *td = new ThreadData();td->number = i+1;snprintf(td->namebuffer, sizeof(td->namebuffer), "%s:%d", "thread", i+1);//将 "thread", i+1 放入namebufferpthread_create(&td->tid, nullptr, start_routine, td);threads.push_back(td);}for(auto &iter : threads){cout << "create thread: " << iter->namebuffer << " : " << iter->tid << " suceesss" << endl;}// 线程是可以被cancel取消的!注意:线程要被取消,前提是这个线程已经跑起来了// 线程如果是被取消的,退出码:-1 (PTHREAD_CANCELED)sleep(5);for(int i = 0; i < threads.size()/2; i++){pthread_cancel(threads[i]->tid); //主线程取消新线程cout << "pthread_cancel : " << threads[i]->namebuffer << " success" << endl;}for(auto &iter : threads) //进程等待{void *ret = nullptr; // 注意: 是void *哦// ? : 为什么没有见到,线程退出的时候,对应的退出信号??? 线程出异常,收到信号,整个进程都会退出!// pthread_join:默认就认为函数会调用成功!不考虑异常问题,异常问题是你进程该考虑的问题!int n = pthread_join(iter->tid, (void**)&ret); // void ** retp; *retp = return (void*)td->numberassert(n == 0);cout << "join : " << iter->namebuffer << " success, exit_code: " << (long long)ret << endl;delete iter;}cout << "main thread quit " << endl;return 0;}
- 运行结果

下面是一张另一张结果图哦!

☀️五、C++多线程引入
上面章节讲述的都是以C语言为基础的原生线程库应用,语言设计者们将其加以封装就形成了C++11的多线程。
- 代码示例
#include <iostream>
#include <unistd.h>
#include <thread>void thread_run()
{while (true){std::cout << "我是新线程..." << std::endl;sleep(1);}
}int main()
{// 任何语言,在linux中如果要实现多线程,必定要是用pthread库// 如何看待C++11中的多线程呢??C++11 的多线程,在Linux环境中,本质是对pthread库的封装!std::thread t1(thread_run);while (true){std::cout << "我是主线程..." << std::endl;sleep(1);}t1.join();return 0;
}
- 运算结果

☀️结语
🌹🌹 多线程1 的知识大概就讲到这里啦,博主后续会继续更新更多C++ 和 Linux的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪
相关文章:
【Linux初阶】多线程1 | 页表的索引作用,线程基础(优缺点、异常、用途),线程VS进程,线程控制,C++多线程引入
文章目录 ☀️一、深入理解页表☀️二、Linux线程概念🌻1.什么是线程(重点)⚡(1)线程的概念⚡(2)线程库初识 🌻2.线程的优点🌻3.线程的缺点🌻4.线程异常&…...
Flink--9、双流联结(窗口联结、间隔联结)
星光下的赶路人star的个人主页 我还有改变的可能性,一想起这点,我就心潮澎湃 文章目录 1、基于时间的合流——双流联结(Join)1.1 窗口联结(Window Join)1.2 间隔联结(Interval Join)…...
家政服务行业做开发微信小程序可以实现什么功能
家政服务行业开发微信小程序可以实现多种功能,从而提升服务品质和效率,下面我们来详细介绍一些可能实现的功能。 一、展示服务信息 家政服务微信小程序可以展示各种服务信息,包括各类家政服务项目、价格、服务流程、服务人员信息等。用户可以…...
20哈希表-三数之和
目录 LeetCode之路——15. 三数之和 分析: 官方题解: LeetCode之路——15. 三数之和 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nu…...
JVM 运行时数据区和垃圾收集算法
在 《深入理解 Java 虚拟机》一书中,作者将运行时数据区和垃圾收集算法放在开头章节,说明了这两个知识点是进一步学习 JVM 的基础知识点,相比后续的 垃圾收集器和 JMM,它也更加的简单。 运行时数据区 运行时数据区是《Java 虚拟…...
Java基于SpringBoot的高校招生系统
博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 简介系统设计思路1 数据库设计2 系统整体设计 系统详细设计1系统功能模块2. 管理员功能模块3学生…...
6. Python使用Asyncio开发TCP服务器简单案例
1. 说明 在Python中开发TCP/IP服务器有两种方式,一种使用Socket,需要在py文件中引入对应的socket包,这种方式只能执行单项任务;另一种方式使用Asyncio异步编程,可以一次创建多个服务器执行不同的任务。 2. 接口说明 …...
景联文科技:AI大模型强势赋能,助力自动驾驶迭代升级
我国一直以来都将自动驾驶作为新兴产业发展的重点领域之一,工信部等相关部委出台了一系列自动驾驶发展战略、规划和标准,一些地方政府也在积极开展关于自动驾驶的地方立法,为自动驾驶技术的研发和应用提供更加具体的法律保障。例如࿰…...
多周期CPU设计
多周期CPU设计 指令类型clock skew 指令类型 在计算机体系结构中,指令可以分为不同的类型,通常有R-type、I-type和J-type指令。 R-type指令(Register-type指令): R-type指令通常用于执行寄存器之间的操作,…...
Go 复合类型之字典类型介绍
Go 复合类型之字典类型介绍 文章目录 Go 复合类型之字典类型介绍一、map类型介绍1.1 什么是 map 类型?1.2 map 类型特性 二.map 变量的声明和初始化2.1 方法一:使用 make 函数声明和初始化(推荐)2.2 方法二:使用复合字…...
对于无法直接获取URL的数据爬虫
在爬学校安全教育题库的时候发现题库分页实际上执行了一段js代码,如下图所示 点击下一页时是执行了函数doPostBack,查看页面源码如下 点击下一页后这段js提交了一个表单,随后后端返回对应数据,一开始尝试分析获取对应两个参数&a…...
35.树与二叉树练习(1)(王道第5章综合练习)
【所用的树,队列,栈的基本操作详见上一节代码】 试题1(王道5.3.3节第3题): 编写后序遍历二叉树的非递归算法。 参考:34.二叉链树的C语言实现_北京地铁1号线的博客-CSDN博客https://blog.csdn.net/qq_547…...
JSON数据处理工具-在线工具箱网站tool.qqmu.com的使用指南
导语:无论是处理JSON数据、进行文本数字处理、解码加密还是使用站长工具,我们都希望能够找到一个功能强大、简便易用的在线平台。tool.qqmu.com作为一款瑞士军刀般的在线工具箱网站,满足了众多用户的需求。本文将介绍tool.qqmu.com的多项功能…...
leetcode:190. 颠倒二进制位
一、题目: 函数原型: uint32_t reverseBits(uint32_t n) 解释:uint32是无符号int或short的别称,传入的参数是一个32位二进制串,返回值是该32位二进制串逆序后的十进制值 二、思路: 实际上并不需要真的去逆…...
Spring Cloud--@RefreshScope动态刷新的注意事项
原文网址:Spring Cloud--RefreshScope动态刷新的注意事项_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Spring Cloud的RefreshScope动态刷新的注意事项。 不用RefreshScope也能动态刷新 Spring Cloud的默认实现了动态刷新,不加RefreshScope就能实现动态…...
visual-studio-code通过跳板机连接远程服务器的配置操作
step1:在本机上生成私钥和公钥 sh-keygen -t rsa -C “your_emailxxx.com”生成的两个默认文件中,id_rsa.pub是公钥,id_rsa是私钥 step2:在vscode安装Remote-SSH插件 step3:将本机生成的私钥和公钥上传服务器上 把本机生成的rsa_id.pub公钥上传至服务…...
LuatOS-SOC接口文档(air780E)-- gpio - GPIO操作
常量 常量 类型 解释 gpio.LOW number 低电平 gpio.HIGH number 高电平 gpio.PULLUP number 上拉 gpio.PULLDOWN number 下拉 gpio.RISING number 上升沿触发 gpio.FALLING number 下降沿触发 gpio.BOTH number 双向触发,部分设备支持 gpio.HIGH_IRQ …...
一个命令让redis服务端所有信息无所遁形~(收藏吃灰系列)
Redis服务器是一个事件驱动程序,它主要处理两类事件:文件事件和时间事件。这些事件的处理和Redis命令的执行密切相关。下面我将以Redis服务端命令为切入点,深入解析其工作原理和重要性。 首先,我们先了解Redis服务端有哪些命令。…...
通过Node.js获取高德的省市区数据并插入数据库
通过Node.js获取高德的省市区数据并插入数据库 1 创建秘钥1.1 登录高德地图开放平台1.2 创建应用1.3 绑定服务创建秘钥 2 获取数据并插入2.1 创建数据库连接工具2.2 请求数据2.3 数据处理2.4 全部代码 3 还可以打印文件到本地 1 创建秘钥 1.1 登录高德地图开放平台 打开开放平…...
记一次 .NET某账本软件 非托管泄露分析
一:背景 1. 讲故事 中秋国庆长假结束,哈哈,在老家拍了很多的短视频,有兴趣的可以上B站观看:https://space.bilibili.com/409524162 ,今天继续给大家分享各种奇奇怪怪的.NET生产事故,希望能帮助…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
力扣-35.搜索插入位置
题目描述 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...
SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
Ubuntu系统复制(U盘-电脑硬盘)
所需环境 电脑自带硬盘:1块 (1T) U盘1:Ubuntu系统引导盘(用于“U盘2”复制到“电脑自带硬盘”) U盘2:Ubuntu系统盘(1T,用于被复制) !!!建议“电脑…...
stm32wle5 lpuart DMA数据不接收
配置波特率9600时,需要使用外部低速晶振...
Python竞赛环境搭建全攻略
Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型(算法、数据分析、机器学习等)不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...

