当前位置: 首页 > news >正文

【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.线程终止

如果需要只终止某个线程而不终止整个进程,可以有三种方法:

  1. 从线程函数return。(return nullptr;)这种方法对主线程不适用,从main函数return相当于调用exit。
  2. 线程可以调用pthread_ exit终止自己。
  3. 一个线程可以调用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线程概念&#x1f33b;1.什么是线程&#xff08;重点&#xff09;⚡&#xff08;1&#xff09;线程的概念⚡&#xff08;2&#xff09;线程库初识 &#x1f33b;2.线程的优点&#x1f33b;3.线程的缺点&#x1f33b;4.线程异常&…...

Flink--9、双流联结(窗口联结、间隔联结)

星光下的赶路人star的个人主页 我还有改变的可能性&#xff0c;一想起这点&#xff0c;我就心潮澎湃 文章目录 1、基于时间的合流——双流联结&#xff08;Join&#xff09;1.1 窗口联结&#xff08;Window Join&#xff09;1.2 间隔联结&#xff08;Interval Join&#xff09;…...

家政服务行业做开发微信小程序可以实现什么功能

家政服务行业开发微信小程序可以实现多种功能&#xff0c;从而提升服务品质和效率&#xff0c;下面我们来详细介绍一些可能实现的功能。 一、展示服务信息 家政服务微信小程序可以展示各种服务信息&#xff0c;包括各类家政服务项目、价格、服务流程、服务人员信息等。用户可以…...

20哈希表-三数之和

目录 LeetCode之路——15. 三数之和 分析&#xff1a; 官方题解&#xff1a; LeetCode之路——15. 三数之和 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nu…...

JVM 运行时数据区和垃圾收集算法

在 《深入理解 Java 虚拟机》一书中&#xff0c;作者将运行时数据区和垃圾收集算法放在开头章节&#xff0c;说明了这两个知识点是进一步学习 JVM 的基础知识点&#xff0c;相比后续的 垃圾收集器和 JMM&#xff0c;它也更加的简单。 运行时数据区 运行时数据区是《Java 虚拟…...

Java基于SpringBoot的高校招生系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 简介系统设计思路1 数据库设计2 系统整体设计 系统详细设计1系统功能模块2. 管理员功能模块3学生…...

6. Python使用Asyncio开发TCP服务器简单案例

1. 说明 在Python中开发TCP/IP服务器有两种方式&#xff0c;一种使用Socket&#xff0c;需要在py文件中引入对应的socket包&#xff0c;这种方式只能执行单项任务&#xff1b;另一种方式使用Asyncio异步编程&#xff0c;可以一次创建多个服务器执行不同的任务。 2. 接口说明 …...

景联文科技:AI大模型强势赋能,助力自动驾驶迭代升级

我国一直以来都将自动驾驶作为新兴产业发展的重点领域之一&#xff0c;工信部等相关部委出台了一系列自动驾驶发展战略、规划和标准&#xff0c;一些地方政府也在积极开展关于自动驾驶的地方立法&#xff0c;为自动驾驶技术的研发和应用提供更加具体的法律保障。例如&#xff0…...

多周期CPU设计

多周期CPU设计 指令类型clock skew 指令类型 在计算机体系结构中&#xff0c;指令可以分为不同的类型&#xff0c;通常有R-type、I-type和J-type指令。 R-type指令&#xff08;Register-type指令&#xff09;&#xff1a; R-type指令通常用于执行寄存器之间的操作&#xff0c;…...

Go 复合类型之字典类型介绍

Go 复合类型之字典类型介绍 文章目录 Go 复合类型之字典类型介绍一、map类型介绍1.1 什么是 map 类型&#xff1f;1.2 map 类型特性 二.map 变量的声明和初始化2.1 方法一&#xff1a;使用 make 函数声明和初始化&#xff08;推荐&#xff09;2.2 方法二&#xff1a;使用复合字…...

对于无法直接获取URL的数据爬虫

在爬学校安全教育题库的时候发现题库分页实际上执行了一段js代码&#xff0c;如下图所示 点击下一页时是执行了函数doPostBack&#xff0c;查看页面源码如下 点击下一页后这段js提交了一个表单&#xff0c;随后后端返回对应数据&#xff0c;一开始尝试分析获取对应两个参数&a…...

35.树与二叉树练习(1)(王道第5章综合练习)

【所用的树&#xff0c;队列&#xff0c;栈的基本操作详见上一节代码】 试题1&#xff08;王道5.3.3节第3题&#xff09;&#xff1a; 编写后序遍历二叉树的非递归算法。 参考&#xff1a;34.二叉链树的C语言实现_北京地铁1号线的博客-CSDN博客https://blog.csdn.net/qq_547…...

JSON数据处理工具-在线工具箱网站tool.qqmu.com的使用指南

导语&#xff1a;无论是处理JSON数据、进行文本数字处理、解码加密还是使用站长工具&#xff0c;我们都希望能够找到一个功能强大、简便易用的在线平台。tool.qqmu.com作为一款瑞士军刀般的在线工具箱网站&#xff0c;满足了众多用户的需求。本文将介绍tool.qqmu.com的多项功能…...

leetcode:190. 颠倒二进制位

一、题目&#xff1a; 函数原型&#xff1a; uint32_t reverseBits(uint32_t n) 解释&#xff1a;uint32是无符号int或short的别称&#xff0c;传入的参数是一个32位二进制串&#xff0c;返回值是该32位二进制串逆序后的十进制值 二、思路&#xff1a; 实际上并不需要真的去逆…...

Spring Cloud--@RefreshScope动态刷新的注意事项

原文网址&#xff1a;Spring Cloud--RefreshScope动态刷新的注意事项_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Spring Cloud的RefreshScope动态刷新的注意事项。 不用RefreshScope也能动态刷新 Spring Cloud的默认实现了动态刷新&#xff0c;不加RefreshScope就能实现动态…...

visual-studio-code通过跳板机连接远程服务器的配置操作

step1:在本机上生成私钥和公钥 sh-keygen -t rsa -C “your_emailxxx.com”生成的两个默认文件中&#xff0c;id_rsa.pub是公钥&#xff0c;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服务器是一个事件驱动程序&#xff0c;它主要处理两类事件&#xff1a;文件事件和时间事件。这些事件的处理和Redis命令的执行密切相关。下面我将以Redis服务端命令为切入点&#xff0c;深入解析其工作原理和重要性。 首先&#xff0c;我们先了解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某账本软件 非托管泄露分析

一&#xff1a;背景 1. 讲故事 中秋国庆长假结束&#xff0c;哈哈&#xff0c;在老家拍了很多的短视频&#xff0c;有兴趣的可以上B站观看&#xff1a;https://space.bilibili.com/409524162 &#xff0c;今天继续给大家分享各种奇奇怪怪的.NET生产事故&#xff0c;希望能帮助…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...