如何使用rdtsc和C/C++来测量运行时间(如何使用内联汇编和获取CPU的TSC时钟频率)

本文主要是一个实验和思维扩展,除非你有特殊用途,不然不要使用汇编指令来实现这个功能。扩展阅读就列出了一些不需要内联汇编实现的

写本文是因为为了《Windows上的类似clock_gettime(CLOCK_MONOTONIC)的高精度测量时间函数》这篇文章找资料的时候,发现了一些关于测量时间如何实现的内容,所以出于好奇心进行了一些研究和实践。

本文是一个初步的尝试,旨在研究一些时间测量实现和在 C/C++ 中内联汇编和汇编函数的使用方法。

本文只尝试了 X86 的 macOS。因为没有使用系统内核,而是使用的内联汇编,所以理论上 Linux 也可以使用 macOS 的方法,只不过我没进行尝试(手上没有 X86 的 Linux 设备)。

如果你想在 Windows 中实现,那么请导入intrin.h,使用__rdtsc()__cpuid()(这两个 intrinsic 函数作用和本文写的大部分内联汇编一样或者可以等价替换),而不是使用内联汇编。因为 Visual Studio 内联汇编比 GCC 和 Clang 的复杂许多,考虑到多个兼容性,还是这样的好。本文虽然没有列出 Windows 的方法,但是在扩展阅读中尽量列出所需的资料,便于读者自己尝试。

本打算写一下 Windows 部分的,但是出于篇幅,还是不写了,博客太长对于读者和作者都不太好(个人喜好罢了)。

思路

这种测量时间的思路是通过计时器晶体的时钟计数做差,然后除以计时器的频率来获取时间,这种方法的计时误差主要取决于晶体质量。类似clock(),不过要复杂的多,因为这里的频率并不是clock()中简单的 1 MHz。

X86 上最常用的计时器是 CPU 上的 TSC,不过由于 BIOS 设置、CPU 是否支持 TSC 等诸多因素,常用的计时函数往往是使用了多个计时器来消除误差。本文并不会做到这种程度,只使用 TSC 计数和频率来实现计时。

我看了国内外很多文章,可以明显感觉到 x86 的 TSC 这个技术在 14 年前后(个人感觉)有了巨大变化,因为早期的博客、文章、代码等提及的一系列方法和问题与今天的情况几乎可以说是完全不同。这个时间段也与 Intel 官方文档中提及的一些处理器的更新和内容相关。

内联汇编实现

由于日常开发并不会出现纯汇编,所以使用内联汇编来降低复杂度和增加可读性。而且内联汇编也比较能展现汇编的结构,如果后续需要实现纯汇编,也比较方便。

确定设备有TSC,并且是不变的

第一步是要确定设备有 TSC,并且是不变的。如果是可变的,也就是会随着睿频、过热、核心等情况变化频率,那么这个方法是无法可靠地测量时间的。如果没有 TSC,那也就没必要下面的步骤了。

查看是否有 TSC 的方法是CPUID.1:EDX.TSC[bit 4] = 1,也就是使用0x1初始化,检查 CPUID 返回的EDX寄存器中第 4 位的值,如果为1则有 TSC。

查看是否有 TSC 的方法是使用0x80000007初始化,检查 CPUID 返回的EDX寄存器中第 8 位的值,如果为1则可以使用不变 TSC。

内联汇编如下:

#define BIT(nr) (1UL << (nr)) //方便确定哪一位的宏:获取第nr位的内容,右端为最小端static inline void isTSC(void)
{uint32_t a=0x1, b, c, d;asm volatile ("cpuid": "=a" (a), "=b" (b), "=c" (c), "=d" (d): "a" (a), "b" (b), "c" (c), "d" (d));if ((d & BIT(4))) {printf("TSC exist!\n");} else a=0x80000007;asm volatile ("cpuid": "=a" (a), "=b" (b), "=c" (c), "=d" (d): "a" (a), "b" (b), "c" (c), "d" (d));if ((d & BIT(8))) {printf("Invariant TSC available!\n");}
}
  • static inline这个是静态内联是为了提高程序性能,不过鉴于编译转换流程,有时候写了反而降低性能,所以写不写看情况。
  • asm volatile ("cpuid\n\t" : "=a" (a), "=b" (b), "=c" (c), "=d" (d) : "a" (a), "b" (b), "c" (c), "d" (d));这行就是内联汇编了。这部分的内容使用冒号:分隔,最多有三个部分,第一个部分是汇编指令,第二个部分是输出,第三个部分是输入(需要注意先是输出,再是输入)。比如这里的"a" (a), "b" (b), "c" (c), "d" (d)就是汇编的输出,输出eaxebxecxedx4 个寄存器(由于不同位的机器上寄存器名称不一样,但是是兼容的,所以直接使用不同处abcd表示寄存器)的值给了abcd4个变量。
  • 虽然这里只使用了eaxedx,但是最好还是都声明和输入一下,不然程序可能会闪退,因为有的指令需要使用其他的寄存器。比如说如果在 macOS 上确定是否不变 TSC 的时候,没有输入和输出ebxecx寄存器,那么会出现访问内存超出的错误。

输出为:

TSC exist!
Invariant TSC available!

需要注意的是,这里是使用打印信息来说明是否有二者,但是实际程序中,需要返回值来判断是否可以使用 TSC 来计时,最后的完整代码中会加入返回值和判断部分。

这时候就可以开始准备使用 TSC 来计时了。

使用内联汇编通过rdtsc获取 TSC 计数

处理器时间戳计数器的值是使用rdtsc指令从 64 位的 MSR(Model-specific register,特定模块寄存器)中读取到EDX:EAX两个寄存器中的。

如果你好奇 MSR,可以看看我的这篇博客:MSR是个什么寄存器 - ZhongUncle CSDN

包装内联汇编的 C/C++ 函数如下:

static inline unsigned long long rdtsc(void)
{unsigned long long low, high;asm volatile ("rdtsc" : "=a" (low), "=d" (high));return low | (high << 32);
}

low | (high << 32)是因为edx寄存器加载了 MSR 的高阶32位,eax寄存器加载了低阶32位。所以为了得到 64 位的 TSC 计数,要将高位的部分左移 32 位,然后再将二者进行或|运算得到 64 位的无符号数。

输出一个值看看效果:

printf("%llu\n", rdtsc());
printf("%llu\n", rdtsc());

输出结果为:

13207359930699
13207359964314

使用内联汇编获取cpuid中 TSC 频率

知道了 TSC 计数的值,如何获取计算时间所需的频率呢?

这是整篇文章中最困难的一段:因为出于理解、安全等诸多原因,直接暴露 TSC 值是不好的。这就导致需要一定的操作才能获得 TSC 频率,并不像计数一样可以轻松获得。

简单例子

先列出代码,然后再进行说明,因为这里情况实在是有点复杂。代码实现如下(下面的24000000需要根据不同的处理器设置不同的值,看完下面的内容你就知道是怎么得到的了,并且最后会弄出一个比较通用的办法):

static inline uint32_t tsc_freq(void)
{uint32_t a=0x15, b, c=0, d;asm volatile ("cpuid": "=a" (a), "=b" (b), "=c" (c), "=d" (d): "0" (a), "1" (b), "2" (c), "3" (d));return b/a*24000000;
}

代码解释比较长,没法写成一个列表,所以请仔细看下面的内容。

TSC 频率是如何获取的呢?

为什么说情况有些复杂呢?因为早期处理器和现在的处理器的 TSC 频率获取不是一回事。

很多文章、博客、书籍获取 TSC 频率由于获取 TSC 的复杂性,都是通过内核的/sys/devices/system/cpu/cpu0/tsc_freq_khz实现的,本文不使用这个。如果你对此感兴趣,在“参考资料/扩展阅读”部分我会列出一些使用这种方法等博客和帖子。

(这里我删了一大段,因为我需要确定一件事去问了几位业内人士,如果得到确定会在这里进行补充)

首先在官方文档 Intel® 64 and IA-32 Architectures Software Developer Manuals(2021-04)中可以看到CPUID.15H的返回信息部分有以下内容:

不同版本的 Intel® 64 and IA-32 Architectures Software Developer Manuals 表格数可能会不一样。

请添加图片描述

CPUID 在 EAX、EBX、ECX 和 EDX 寄存器中返回处理器识别和功能信息。指令的输出内容在执行时取决于 EAX 寄存器的值(在某些情况下还会附带上 ECX 的值)。

我们代码中使用的EAX值为0x25,而ECX0x0。你也可以使用mov指令设置寄存器的值,也可以像上面一样通过输入来设置几个寄存器的值。

从上图中可以看到EAXTSC/core crystal clock的分母,而EBXTSC/core crystal clock的分子,二者组成一个比例,这个比例有什么用呢?

这个值乘以core crystal clock就是 TSC 频率了,但是该如何获得core crystal clock呢?

如何获取核心晶体时钟频率(core crystal clock)

在 18.7.3 中有以下内容:
请添加图片描述

这个图包含了一些信息:

  • 名义核心晶体时钟频率在ecx中列出。核心晶体时钟运行在固定频率上,与整个系统的总时钟相协调。现在的绝大部分处理器(大约是从 Skylake 开始)或者使用缩放频率的处理器都不会在ecx列出核心晶体时钟频率了。如果ecx中没有说明核心晶体时钟频率,那么需要按照表 18-85 中的值计算 TSC 频率。
  • CPUID.15H 的返回值是核心晶体时钟比例的编码。
  • 中间的这个公式计算的是名义上 TSC 频率。

简而言之就是,对于CPUID.15H.EBX[31:0] ÷ CPUID.0x15.EAX[31:0]值存在,但是CPUID.15H.ECX没有值的情况,CPUID.15H.ECX的值应该如下:请添加图片描述

如何使用晶体时钟来计算TSC频率

那么该如何使用上面获取的那些值呢?

我用 Intel i5 8500B 测的CPUID.15H返回各寄存器的值如下:

eax=2
ebx=250
ecx=0
edx=0

按照这个计算来说,ebx/eax=125ecx=0,也就是需要从表格中获取值,这里使用24 Mhz(我怀疑是文档没更新,应该包含 8 代等的处理器的,因为其他两行都有说明 CPUID 签名,中间没说明应该就是其余所有这种处理器)。

这时候 TSC 频率 = 24 Mhz * ebx/eax=24 Mhz * 125 = 3000 Mhz,刚好就是 i5 8500B 的基础频率,这时候计算得到的时间精度挺高的。

如下是两次测试测到的结果,time是使用这种方法计算出的时间,下面的duration是使用clock_gettime()计算出的时间:

请添加图片描述

请添加图片描述

多次测试发现误差最多不到 500 ns(也不保证一定就是 500 ns 以内,因机器和环境而异)

要判断 TSC 频率是多少,从上面的信息知道需要考虑 2 点:

  1. ECX寄存器是否已经有频率信息了,如果有的话,直接使用;
  2. ECX寄存器没有频率信息的情况下,需要判断 CPUID 签名,从而得知频率是哪个。

第一个很好解决,一个判断语句。第二个 CPUID 签名是什么呢?

什么是 CPUID 签名

CPUID 签名就是当EAX=1时,cpuidEAX中的返回值。内容如下:

请添加图片描述

实际上并不会用到整个EAX的内容,只要取得处理器的Model部分即可。

为什么只要取得处理器的Model部分

只要取得处理器的Model部分即可有两个原因。

第一,因为前面那个表格中,两个列出的签名都是06H开头的,这是 Family ID,这部分包含以下系列的处理器:

请添加图片描述

可以看到现在使用的整个 Intel 酷睿(Core)系列和绝大部分志强(Xeon)系列都是6H开头的。这时候你会说,上面不是06H嘛?还有一位十六进制呢?这就是Extended Model ID的作用,和在一起就表示06H。可以说在过去三十年里,绝大部分 Intel 处理器的 Family ID都是6H(或者06H),只有早期处理器、Itanium 系列和极个别例外,具体情况自己搜一下“Processor Family IDs”即可。

下表是用到Extended Family ID部分的处理器:

请添加图片描述

比如 Zen 3 和 Zen 4 的 Family ID就是19H,那么就是Extended Family ID [1] + Family ID [9]

第二,其他处理器都在ECX中有核心晶体频率,只有这部分列出的没有,而这些都是06H为 Family ID 的处理器,所以只要考虑 Model 部分即可。但是处理器的 Model 依旧有扩展部分,所以需要获取两个部分再组合。

获取处理器 Model 部分的方法

获取处理器 Model 部分的方法为:

static inline unsigned int cpu_model(void)
{uint32_t a=0x1, b, c, d;asm volatile ("cpuid": "=a" (a), "=b" (b), "=c" (c), "=d" (d): "a" (a), "b" (b), "c" (c), "d" (d));uint32_t model = (a>>4)&0b1111;uint32_t extend_model = (a>>16)&0b1111;return (extend_model<<4) | model;
}

这里提示一下,不要使用a&0b11110000,因为这样获取的会带上最后四位,而我们并不需要这 4 位。

我的处理器输出结果为:

9e

接下来我们只需要判断是否可以是表格中的那两个就行。这里就不列出判断的代码了,我将其结合到完整代码中。

注意事项

在某些文章中提到 TSC 无法测量多核并行任务的时间,我猜测这是某些系统内核实现的问题或者早期一些处理器的特性。所以要根据自己的处理器来判断是否可以使用 TSC 实现计时。

因为我依旧使用 ISPC 编写多核并行任务,得到的时间和clock_gettime()非常相近。如下:

请添加图片描述

所以在现如今的处理器上,应该是可以测量并行程序的。

至于有些人提到执行时,可能会将rdtsc挤滞后的,在现在的文档中,提到这么一段话:

请添加图片描述

不过也这段话提到:RDTSC 并非串行指令,不一定要等到所有前置指令执行完才能读取计数器。RDTSC 的后续指令也可能会在 RDTSC 读取完就开始执行。关于执行顺序分了三种情况,所以具体情况还是要看软件程序的要求。

完整代码

使用 C++ 的标准库iostream,这样可以使用uint32_tbool等类型的数据,稍微方便一些。如果你需要使用纯 C,那么自己替换成合适的格式即可,以及引入<timer.h>

我将 C 和 C++ 两个版本的整理到TSC_Timer - Github ZhongUncle了,你可以克隆下来直接试试看。

下面是完整的代码:

#include <iostream>#define BIT(nr) (1UL << (nr)) //获取第nr位的内容,右端为最小端static inline unsigned long long rdtsc(void)
{unsigned long long low, high;asm volatile ("rdtsc" : "=a" (low), "=d" (high));return low | (high << 32);
}static inline bool isTSC(void)
{uint32_t a=0x1, b, c, d;asm volatile ("cpuid": "=a" (a), "=b" (b), "=c" (c), "=d" (d): "a" (a), "b" (b), "c" (c), "d" (d));if ((d & BIT(4))) {// TSC exist!a=0x80000007;asm volatile ("cpuid": "=a" (a), "=b" (b), "=c" (c), "=d" (d): "a" (a), "b" (b), "c" (c), "d" (d));if ((d & BIT(8))) {// Invariant TSC available!return true;}} else {// TSC not existreturn false;}return false;
}static inline unsigned int cpu_model(void)
{uint32_t a=0x1, b, c, d;asm volatile ("cpuid": "=a" (a), "=b" (b), "=c" (c), "=d" (d): "a" (a), "b" (b), "c" (c), "d" (d));uint32_t model = (a>>4)&0b1111;uint32_t extend_model = (a>>16)&0b1111;return (extend_model<<4)|model;
}static inline unsigned long tsc_freq(void)
{uint32_t model = cpu_model();uint32_t a=0x15, b, c, d;asm volatile ("cpuid": "=a" (a), "=b" (b), "=c" (c), "=d" (d): "0" (a), "1" (b), "2" (c), "3" (d));if (c != 0) {return b / a * c;}if (model == 0x55) {return b / a * 25000000;}if (model == 0x5c) {return b / a * 19200000;}return b/a*24000000;
}int main(int argc, const char * argv[]) {//判断是否有可靠的TSCif (!isTSC()) {printf("TSC is not exist or variant!");}//获取TSC频率unsigned long freq = tsc_freq();// 使用clock_gettime辅助验证struct timespec start;clock_gettime(CLOCK_MONOTONIC, &start);//获取TSC计数作为开始uint64_t rdtsc1 = rdtsc();//用于计时的测试代码for (int i=0; i<100; i++) {std::cout << "Hello, World!\n";}//获取TSC计数作为结束uint64_t rdtsc2 = rdtsc();struct timespec end;clock_gettime(CLOCK_MONOTONIC, &end);//获取TSC的时间double time = (double)(rdtsc2-rdtsc1)/(double)freq*1e9;//打印时钟、频率和TSC时间printf("clock\t = %llu cycles\n", rdtsc2-rdtsc1);printf("freq\t = %lu Hz\n", freq);printf("TSC time = %.0f ns\n", time);//计算clock_gettime辅助验证的时间double duration = (double)(end.tv_nsec-start.tv_nsec) + (double)(end.tv_sec-start.tv_sec)*((double) 1e9);//打印clock_gettime辅助验证的时间printf("duration = %.0f ns\n", duration);return 0;
}

希望能帮到有需要的人~

扩展阅读

How to get the CPU cycle count in x86_64 from C++? - Stack Overflow:这篇帖子的最高赞回答解释了 Linux 和 Windows 使用 C++ 内建的__rdtsc获取 x86_64 计数的方法。

__rdtsc - Microsoft Learn:__rdtsc的微软官方文档。

TSC Frequency For All: Better Profiling and Benchmarking - Trail of Bits:这篇博客就是使用/sys/devices/system/cpu/cpu0/tsc_freq_khz来获取 TSC 频率的。

Getting TSC rate from x86 kernel - Stack Overflow:这个帖子的这个回答,介绍了很多“高层级”获取 TSC 频率的方法。

Using and Preserving Registers in Inline Assembly - Microsoft Learn:微软官方介绍 Windows 使用内联汇编的方法。

How to do cpuid and rdtsc on 32 bit and 64 bit Windows - By Stephen Kellett:这篇博客介绍了如何在 Windows 的内联汇编中使用cpuidrdtsc的方法。

参考资料

研究的时候被很多资料误导了,所以列出使用到的和一些我觉得还不错的资料。

Intel® 64 and IA-32 Architectures Software Developer Manuals:Intel 处理器的官方文档,介绍了很多指令和架构信息。唯一缺陷就是太太太长了,所以如果你想自己看看,那么建议下一本看看大概,看的差不多了之后,后续只用看更新部分,这样大大降低时间。如果更新一本换一本,那么看不完的。

6.48.2 Extended Asm - Assembler Instructions with C Expression Operands - GNU GCC: GNU GCC 内联汇编的官方文档。

Inline Assembly/Examples - OS Dev:这篇 wiki 提供了许多内联汇编的例子。

建议下面 4 篇结合着看!

CPUID — CPU Identification - felixcloutier:介绍和解释了 CPUID 的指令使用方法和返回值含义等内容。这个可以当做 Intel SDM 的在线版,他把指令和内容解释做成了网页的。

CPUID - OS Dev:这篇 wiki 非常详细的介绍了如何通过汇编和内联汇编来使用 CPUID 的案例,但是对内容没有进行什么解释。

CPUID - Wikipedia:这里比较方便查看 CPUID 家族等一些寄存器的信息,但不是全部。

Query CPUID with Inline Assembly - Jack Henschel’s Blog:这篇博客是作者写学士论文时,研究内联汇编请求 CPUID 内容的总结。个人感觉例子比上面的 OS Dev 的例子好一些(个人喜好)。

RDTSC — Read Time-Stamp Counter - felixcloutier:介绍和解释了 CPUID 的指令使用方法和返回值含义等内容。

Function asm volatile(“rdtsc”); - Stack Overflow:这篇帖子讨论了如何使用内联汇编使用rdtsc命令获取 TSC 计数。

TSC frequency computation - Intel Community:这个帖子讨论了早期 CPU 计算 TSC 频率出现的精度问题。

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

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

相关文章

不同版本vue安装vue-router

vue-router 是vue官网发布的一个插件库&#xff0c;单页面路由。vue 和 vue-router 之间版本也需要对应。 vue2.x版本使用vue-router3.x版本&#xff0c;vue3.x使用vue-router4.x版本&#xff0c;根据自己的需要选择合适的版本 1、可以在安装前查看vue-router版本&#xff0c;…

陈奂仁联手 The Sandbox 推出“Hamsterz Doodles”人物化身系列

全新人物化身系列结合艺术与实用性 开创元宇宙新篇章 著名亚洲唱作歌手兼香港电影金像奖得主陈奂仁携手 The Sandbox&#xff0c;兴奋地宣布推出新的元宇宙人物化身系列 —— Hamsterz Doodles 仓鼠涂鸦。 陈奂仁在 The Sandbox 推出 Hamsterz Doodles 系列&#xff0c;将艺术与…

波士顿动力抛弃液压机器人Atlas,推出全新电动化机器人,动作超灵活

本周&#xff0c;机器人科技巨头波士顿动力宣布液压Atlas退役&#xff0c;并推出了下一代产品——专为实际应用而设计的全电动Atlas机器人&#xff0c;这也意味着人形机器人迈出了商业化的第一步。 Atlas——人形机器人鼻祖 Atlas&#xff08;阿特拉斯&#xff09;这个名字最…

STM32F407,429参考手册(中文)

发布一个适用STM32F405XX、STM32F407XX、STM32F415XX、STM32F417XX、STM32F427XX、STM32F437XX的中文数据手册&#xff0c;具体内容见下图&#xff1a; 点击下载&#xff08;提取码&#xff1a;spnn&#xff09; 链接: https://pan.baidu.com/s/1zqjKFdSV8PnHAHWLYPGyUA 提取码…

计算请假时间,只包含工作时间,不包含中午午休和非工作时间及星期六星期天,结束时间不能小于开始时间

1.计算相差小时&#xff0c;没有休息时间 computed: {// 计算相差小时time() {let time 0;if (this.ruleForm.date1 &&this.ruleForm.date2 &&this.ruleForm.date3 &&this.ruleForm.date4) {// 开始时间let date1 this.ruleForm.date1;let y date…

[笔试训练](二)

004 牛牛的快递_牛客题霸_牛客网 (nowcoder.com) 题目&#xff1a; 题解&#xff1a; 使用向上取整函数ceil()&#xff0c;&#xff08;记得添加头文件#include<cmath>&#xff09; #include <iostream> #include <cmath> using namespace std;int main(…

ArrayList与顺序表(1)

前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&#x…

【苍穹外卖】HttpClient-快速理解入门

目录 HttpClient-快速理解&入门1. 需求2. 如何使用3. 具体示例4. 大致优点5. 大致缺点 HttpClient-快速理解&入门 1. 需求 在平常访问服务器里面的资源的时候&#xff0c;我们通常是通过浏览器输入网址&#xff08;或者在浏览器点击某个连接&#xff09;这种方式&…

测试的分类(2)

目录 按照执行方式分类 静态测试 动态测试 按照测试方法 灰盒测试 按照测试阶段分类 单元测试 集成测试 系统测试 冒烟测试 回归测试 按照执行方式分类 静态测试 所谓静态测试就是不实际运行被测软件,只是静态地检查程序代码, 界面或文档中可能存在错误的过程. 不以…

ffmpeg安装使用(详细)

目录结构 前言ffmpeg下载ffmpeg环境变量配置ffmpeg环境变量配置验证ffmpeg使用举例说明.mp4 转 .wav.mp3 转 .wav.ogg 转 .wav 参考链接 前言 本文主要记录ffmpeg在Windows系统中的安装使用方法。 ffmpeg下载 FFmpeg官网下载 ffmpeg环境变量配置 解压后将“.\ffmpeg\bin”…

vscode 如何断点调试ros1工程

在vscode中断点调试ros1工程主要分为以下几步&#xff1a; 1. 第一步就是修改cmakelist.txt&#xff0c;到调试模式。 将CMAKE_BUILD_TYPE原来对应的代码注释掉&#xff0c;原来的一般都不是调试模式。加上下面一行代码&#xff0c;意思是设置调试模式。 # 断点调试 SET(CMAK…

【Linux】文件基本属性

Linux 系统是一种典型的多用户系统&#xff0c;不同的用户处于不同的地位&#xff0c;拥有不同的权限。 为了保护系统的安全性&#xff0c;Linux 系统对不同的用户访问同一文件&#xff08;包括目录文件&#xff09;的权限做了不同的规定。 在 Linux 中我们可以使用 ll 或者 …

《你想活出怎样的人生》上映,AOC带你打开宫崎骏的动画世界大门!

摘要&#xff1a;宫崎骏式美学&#xff0c;每一帧都是治愈&#xff01; 近日&#xff0c;宫崎骏新作《你想活出怎样的人生》正式公映。苍鹭与少年的冒险、奇幻瑰丽的场景、爱与成长的主题&#xff0c;让观众们收获到满满的爱与感动。宫崎骏总能以细腻的画面、温柔的音乐&#…

设计模式——模板方法

1)模板方法模式(Template Method Pattem)&#xff0c;又叫模板模式(Template Patern)&#xff0c;在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现&#xff0c;但调用将以抽象类中定义的方式进行。 2)简单说&#xff0c;模板方法模式 定义一个操作中…

ETLCloud中多并行分支运行的设计技巧

在大数据处理领域&#xff0c;ETL&#xff08;Extract, Transform, Load&#xff09;流程是至关重要的一环&#xff0c;它涉及数据的提取、转换和加载&#xff0c;以确保数据的质量和可用性。而在ETL流程中&#xff0c;多并行分支的运行设计是一项关键技巧&#xff0c;可以有效…

华为ensp中MSTP多网段传输协议(原理及配置命令)

作者主页&#xff1a;点击&#xff01; ENSP专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月22日15点29分 在华为ENSP中&#xff0c;MSTP&#xff08;多段传输协议&#xff09;是重要的生成树协议&#xff0c;它扩展了STP&#xff08;生成树协议&#xff09…

猴子摘桃问题(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int sum 1;int i 0;//运算&#xff1b;for (i 1; i < 10; i){//运算&#xff1b;sum …

typecho博客的相对地址实现

typecho其中的博客地址,必须写上绝对地址,否则在迁移网址的时候会出现问题,例如页面记载异常 修改其中的 typecho\var\Widget\Options\General.php 中的165行左右, /** 站点地址 */if (!defined(__TYPECHO_SITE_URL__)) {$siteUrl new Form\Element\Text(siteUrl,null,$this-…

怎么把3d模型旋转加复制---模大狮模型网

在3D设计中&#xff0c;旋转和复制模型是常见且重要的操作&#xff0c;它们可以帮助设计师创建复杂的场景并节省时间。本文将介绍如何在3D建模软件中旋转并复制模型&#xff0c;以及一些技巧和注意事项&#xff0c;帮助您轻松实现这些操作。 旋转3D模型&#xff1a; 旋转3D模型…

心理学|变态心理学健康心理学——躯体疾病患者的一般心理特点

一、对客观世界和自身价值的态度发生改变 患者除了内部器官有器质或功能障碍外&#xff0c;他们的自我感觉和整个精神状态也会发生变化。使人改变对周围事物的感受和态度&#xff0c;也可以改变患者对自身存在价值的态度。这种主观态度的改变&#xff0c;可以使患者把自己置于人…