C筑基——深入理解内存对齐

news2025/3/1 10:28:18

目录

  • 1 前言
  • 2 正文
    • 2.1 为什么要有内存对齐?
    • 2.2 内存对齐原则
      • 2.2.1 基本数据类型是自然对齐的
      • 2.2.2 包含基本数据类型成员的结构体
        • 套用结构体内存对齐原则来分析
        • 使用 gdb 查看这两个结构体的成员内存位置
        • 结构体类型变量是自然对齐的吗?
      • 2.2.3 数组类型
    • 2.3 修改编译器的默认对齐方式(系数)
      • 编译器的默认对齐方式(系数)
      • 为什么要修改编译器的默认对齐系数?
      • 如何修改编译器的默认对齐系数?
      • 为什么修改了对齐系数会影响结构体的长度?
      • 发送方和接收方对同一个结构体使用不同的对齐系数例子
  • 3 最后
  • 参考

1 前言

11月份的时候,同事维护的项目出现了内存对齐问题:传递数据的一方使用了 #pragma pack(4) 修改了结构体的对齐方式,而接收方数据的一方并没有对这个结构体使用同样的对齐方式,造成了获取数据时失败的问题。

本文主要说明:

  • 为什么要有内存对齐?
  • 内存对齐原则
  • 如何修改默认对齐方式?

2 正文

2.1 为什么要有内存对齐?

单字或者双字操作数的存储跨越了 4 字节边界,或者一个四字节操作数的存储跨越了 8 字节边界,被认为是未对齐的。为了访问未对齐的内存,处理器需要作两次内存;而如果内存是对齐的,处理器仅需要一次内存访问。

处理器在访问内存时,每次读取一定的长度(这个长度就是处理器的默认对齐系数,或者是默认对齐系数的整数倍)。这个长度就是指上段中描述的 4 字节边界或者 8 字节边界。

需要说明的是,开发者并不需要直接负责如何进行内存对齐,编译器默认会对标准数据类型,结构中的成员数据进行内存对齐。开发者需要学习编译器进行内存对齐的原则,以便解决实际开发中的问题。

2.2 内存对齐原则

2.2.1 基本数据类型是自然对齐的

如果一个变量的内存地址正好是这个变量的长度的整数倍,那么这个变量就是自然对齐的。

这里通过代码来说明 charshortintdouble 类型变量是自然对齐的:

#include <stdio.h>
#include <stdlib.h>

#define mark(num) (num) == 0 ? "√" : "×"

int main(void)
{
    printf("sizeof(char): %zd, sizeof(short): %zd, sizeof(int): %zd, sizeof(double): %zd\n", sizeof(char), sizeof(short), sizeof(int), sizeof(double));
    char a = 'a';
    short b = 10086;
    int c = 1008611;
    double d = 3.1415926;
    long long aAddr = &a;
    long long bAddr = &b;
    long long cAddr = &c;
    long long dAddr = &d;
    printf("类型\t\t被 sizeof(char) 整除\t被 sizeof(short) 整除\t被 sizeof(int) 整除\t被 sizeof(double) 整除\n");
    printf("char 地址\t\t%s\t\t\t%s\t\t\t%s\t\t\t%s\n",
    mark(aAddr % sizeof(char)), mark(aAddr % sizeof(short)), mark(aAddr % sizeof(int)), mark(aAddr % sizeof(double)));
    printf("short 地址\t\t%s\t\t\t%s\t\t\t%s\t\t\t%s\n",
    mark(bAddr % sizeof(char)), mark(bAddr % sizeof(short)), mark(bAddr % sizeof(int)), mark(bAddr % sizeof(double)));
    printf("int 地址\t\t%s\t\t\t%s\t\t\t%s\t\t\t%s\n",
    mark(cAddr % sizeof(char)), mark(cAddr % sizeof(short)), mark(cAddr % sizeof(int)), mark(cAddr % sizeof(double)));
    printf("double 地址\t\t%s\t\t\t%s\t\t\t%s\t\t\t%s\n\n",
    mark(dAddr % sizeof(char)), mark(dAddr % sizeof(short)), mark(dAddr % sizeof(int)), mark(dAddr % sizeof(double)));
    return 0;
}

首先使用 sizeof 运算符获取charshortintdouble 类型的长度,并打印出来;然后声明了charshortintdouble 类型的变量,并打印各自地址被类型长度整除的情况。

打印结果:

sizeof(char): 1, sizeof(short): 2, sizeof(int): 4, sizeof(double): 8
类型            被 sizeof(char) 整除    被 sizeof(short) 整除   被 sizeof(int) 整除     被 sizeof(double) 整除
char 地址               √                       ×                       ×                       ×
short 地址              √                       √                       ×                       ×
int 地址                √                       √                       √                       ×
double 地址             √                       √                       √                       √

从打印结果可以看到,
char 类型变量的地址可以被 sizeof(char),即 1 所整除;
short 类型变量的地址可以被 sizeof(short),即 2 所整除;
int 类型变量的地址可以被 sizeof(int),即 4 所整除;
double 类型变量的地址可以被 sizeof(char),即 8 所整除。

对于这些自然对齐的变量,一次读取 8 个字节长度的处理器,只需要一次内存访问就可以取出变量的值。

2.2.2 包含基本数据类型成员的结构体

对于包含基本数据类型成员的结构体,内存对齐原则是:

  • 成员只能存储在这个成员的长度的整数倍的地址上;
  • 结构体的长度是它的最大成员长度的整数倍。

为了更好地理解,我们通过两个结构体来进一步说明:

typedef struct  
{
    char a;
    short b;
    int c;
} TestStruct1;

typedef struct  
{
    char a;
    int c;
    short b;
} TestStruct2;

问:TestStruct1TestStruct2 的长度是否一样?分别是多少?为什么?

我们先来看一下这两个结构体,区别只是 short b;int c; 这两行语句的次序不一样而已。按照直观的理解,TestStruct1TestStruct2 的长度应该是一样的。

我们打印一下二者的长度:

int main(void)
{
    printf("sizeof(TestStruct1) = %zd, sizeof(TestStruct2) = %zd\n", sizeof(TestStruct1), sizeof(TestStruct2));
    return 0;
}

打印结果:

sizeof(TestStruct1) = 8, sizeof(TestStruct2) = 12

套用结构体内存对齐原则来分析

对于结构体 TestStruct1

typedef struct  
{
    char a; // 成员长度为 1
    short b; // 成员长度为 2
    int c; // 成员长度为 4
} TestStruct1;

结构体的最大成员长度是 4 个字节,所以结构体的长度一定是 4 的整数倍。
char a 占据 1 个字节,需要存储在可以被 1 整除的地址上,所以它相对于结构体起始地址的偏移量为 0;
short b 占据 2 个字节,需要存储在可以被 2 整除的地址上,所以它相对于结构体起始地址的偏移量为 2;
int c 占据 4 个字节,需要存储在可以被 4 整除的地址上,所以它相对于结构体起始地址的偏移量为 4。
使用表格表示如下:
在这里插入图片描述
所以,TestStruct1 的长度是 8 个字节。

对于结构体 TestStruct2

typedef struct  
{
    char a;
    int c;
    short b;
} TestStruct2;

结构体的最大成员长度是 4 个字节,所以结构体的长度一定是 4 的整数倍。
char a 占据 1 个字节,需要存储在可以被 1 整除的地址上,所以它相对于结构体起始地址的偏移量为 0;
int c 占据 4 个字节,需要存储在可以被 4 整除的地址上,所以它相对于结构体起始地址的偏移量为 4;
short b 占据 2 个字节,需要存储在可以被 2 整除的地址上,所以它相对于结构体起始地址的偏移量为 8。
使用表格表示如下:
在这里插入图片描述
所以,TestStruct2 的长度是 12 个字节。

分析结果与打印结果是一致的。

使用 gdb 查看这两个结构体的成员内存位置

为了更深入地理解这两个结构体的成员内存位置,我们再使用 来分析一下:

先对 memory_alignment.c 程序进行修改如下:

int main(void)
{
    TestStruct1 ts1;
    ts1.a = 0x11;
    ts1.b = 0x2222;
    ts1.c = 0x33333333;
    TestStruct2 ts2;
    ts2.a = 0x11;
    ts2.c = 0x33333333;
    ts2.b = 0x2222;
    return 0;
}

分别对各自的成员,使用相应的 16 进制整数进行赋值。这是为了更方便地在内存中查看成员占据的字节数。

使用 gcc 以调试模式编译程序:

$ gcc -g memory_alignment.c

生成的可执行程序是 a.out。

使用 gdb 调试 a.out,在 return 0; 这行打断点,并 run

$ gdb a.out
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
---Type <return> to continue, or q <return> to quit---
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.
(gdb) break 29
Breakpoint 1 at 0x68f: file memory_alignment.c, line 29.
(gdb) run
Starting program: /home/wangzhichao/c-examples/_11_memory_management/a.out 
sizeof(TestStruct1) = 8, sizeof(TestStruct2) = 12

Breakpoint 1, main () at memory_alignment.c:29
29          return 0;
(gdb) 

显示 ts1ts2 的地址,并用 x 命令查看对应地址的内存(关于 x 命令的使用,可以查看GDB查看内存命令(x命令) 用gdb查看指定地址的内存内容):

(gdb) display &ts1
1: &ts1 = (TestStruct1 *) 0x7fffffffde9c
(gdb) x/8xb 0x7fffffffde9c
0x7fffffffde9c: 0x11    0x55    0x22    0x22    0x33    0x33    0x33    0x33
(gdb) display &ts2
2: &ts2 = (TestStruct2 *) 0x7fffffffdea4
(gdb) x/12xb 0x7fffffffdea4
0x7fffffffdea4: 0x11    0x7f    0x00    0x00    0x33    0x33    0x33    0x33
0x7fffffffdeac: 0x22    0x22    0x00    0x00
(gdb) 

x/8xb 0x7fffffffdea0 表示以 16 进制格式输出从 0x7fffffffdea0 向后的 8 个字节上的内容;
x/12xb 0x7fffffffdea8 表示以 16 进制格式输出从 0x7fffffffdea8 向后的 12 个字节上的内容。

这些与表格表示出的内存位置是一致的:
对于 ts1:
在这里插入图片描述
对于 ts2:
在这里插入图片描述

结构体类型变量是自然对齐的吗?

在 2.2.1 小节中说过,如果一个变量的内存地址正好是这个变量的长度的整数倍,那么这个变量就是自然对齐的。

那么,结构体类型变量的内存地址是不是这个结构体类型长度的整数倍呢?

我们直接通过程序来验证吧:

int main(void)
{
    TestStruct1 ts1;
    TestStruct2 ts2;
    printf("TestStruct1 ts1 的地址是否可以被其长度整数:%lld\n", (long long)&ts1 % sizeof(TestStruct1));
    printf("TestStruct1 ts2 的地址是否可以被其长度整数:%lld\n", (long long)&ts2 % sizeof(TestStruct2));
    return 0;
}

多次运行程序,打印如下:

$ ./a.out 
TestStruct1 ts1 的地址是否可以被其长度整数:4
TestStruct1 ts2 的地址是否可以被其长度整数:8
$ ./a.out 
TestStruct1 ts1 的地址是否可以被其长度整数:4
TestStruct1 ts2 的地址是否可以被其长度整数:0
$ ./a.out 
TestStruct1 ts1 的地址是否可以被其长度整数:4
TestStruct1 ts2 的地址是否可以被其长度整数:4

所以,可以知道:结构体变量不是自然对齐的。

2.2.3 数组类型

数组类型是依据其元素类型对齐:如果第一个元素可以对齐,那么后面的元素自然也可以对齐;如果第一个元素不可以对齐,那么后面的元素无法保证对齐。

如果数组元素是基本类型,那么这个数组后面的元素是可以对齐的;
如果数组元素是构造类型,那么这个数组的元素就不保证对齐了。

2.3 修改编译器的默认对齐方式(系数)

编译器的默认对齐方式(系数)

使用 #pragma pack(show) 指令,在 warning 信息里面会打印默认对齐方式(系数)。
遗憾的是,在 Linux gcc 环境下,这条指令并不生效:

$ gcc -g memory_alignment.c 
memory_alignment.c:4:9: warning: unknown action ‘show’ for#pragma pack’ - ignored [-Wpragmas]
 #pragma pack(show)
         ^~~~

在 Android Studio 或者 CLion中,把鼠标放在 pack 上,可以显示出来默认对齐方式(系数):
在这里插入图片描述
另外,使用 clang 环境的在线编辑器,也可以显示出来默认对齐方式(系数)。

需要说明的是,不同的编译器的默认对齐系数可能是不一样的。

为什么要修改编译器的默认对齐系数?

一般情况下,不建议修改编译器的默认对齐系数;但是,当我们开发好的结构作为 API 的一部分提供给第三方使用的时候,第三方开发者可能将编译器的默认对齐系数改变或者第三方开发者的编译器默认对齐系数与我们的不同,这就会造成重大的数据问题。解决办法是:和第三方开发者约定使用一样的对齐系数。

如何修改编译器的默认对齐系数?

使用 #pragma pack(n) 指令,其中 n 就是设置的对齐系数。

需要注意的是,对齐系数的取值可以是 0,1,2,4,8,16 中的一个。当取 0 时,表示恢复默认对齐系数。

示例代码如下:

#include <stdio.h>
#include <stdlib.h>

#pragma pack(4) // 设置对齐系数为 4

typedef struct  
{
    char a;
    short b;
    int c;
} TestStruct1;

typedef struct  
{
    char a;
    int c;
    short b;
} TestStruct2;

int main(void)
{
    printf("sizeof(TestStruct1) = %zd, sizeof(TestStruct2) = %zd\n", sizeof(TestStruct1), sizeof(TestStruct2));
    TestStruct1 ts1;
    ts1.a = 0x11;
    ts1.b = 0x2222;
    ts1.c = 0x33333333;
    TestStruct2 ts2;
    ts2.a = 0x11;
    ts2.c = 0x33333333;
    ts2.b = 0x2222;
    return 0;
}

设置不同的对齐系数,打印信息如下:

对齐系数打印信息
1sizeof(TestStruct1) = 7, sizeof(TestStruct2) = 7
2sizeof(TestStruct1) = 8, sizeof(TestStruct2) = 8
4sizeof(TestStruct1) = 8, sizeof(TestStruct2) = 12
8sizeof(TestStruct1) = 8, sizeof(TestStruct2) = 12
16sizeof(TestStruct1) = 8, sizeof(TestStruct2) = 12

从表格中,可以看到:当对齐系数小于 4 时,结构体的长度与默认对齐系数下的结构体长度不同。这是为什么呢?

为什么修改了对齐系数会影响结构体的长度?

这是因为对齐系数的修改,影响内存对齐原则。

具体来说,考虑到对齐系数的内存对齐原则是:

  • 成员只能存储在 min(这个成员的长度, 对齐系数) 的整数倍的地址上;
  • 结构体的长度是 min(它的最大成员长度, 对齐系数) 的整数倍。

在 2.2.2 中,我们没有考虑对齐系数的影响,是因为默认的对齐系数不小于基本数据类型的长度,也就是说,默认情况下,对齐系数不会对包含基本数据类型成员的结构体的内存对齐原则产生作用。

套用完整的内存对齐原则,以及配合使用 gdb 查看内存位置来分析,得到表格如下:

TestStruct1:
在这里插入图片描述
TestStruct2:
在这里插入图片描述
可以看到:

  • 表格中得到的结构体长度与代码打印的结构体长度是一致的;
  • 较小的对齐系数可以产生更加紧凑的结构体,更加小的结构体长度;
  • 较小的对齐系数使得基本数据类型成员不再是自然对齐的了,这会增加一定的内存访问,降低性能。

发送方和接收方对同一个结构体使用不同的对齐系数例子

发送方,send_data.c

#include <stdio.h>
#include <stdlib.h>

#pragma pack(1) // 对齐系数是 1

typedef struct  
{
    char a;
    int c;
    short b;
} TestStruct2;

int main(void)
{
    FILE * fp = NULL;

    fp = fopen("data", "wb");
    TestStruct2 ts2;
    ts2.a = 0x11;
    ts2.b = 0x2222;
    ts2.c = 0x33333333;
    fwrite(&ts2, sizeof(TestStruct2), 1, fp);
    printf("send success\n");
    fclose(fp);
    return 0;
}

接收方 receive_data.c

#include <stdio.h>
#include <stdlib.h>

#pragma pack(1) // 对齐系数也是1

typedef struct  
{
    char a;
    int c;
    short b;
} TestStruct2;

int main(void)
{
    FILE * fp = NULL;

    fp = fopen("data", "r");
    TestStruct2 ts2;
    fread(&ts2, sizeof(TestStruct2), 1, fp);
    printf("receive: a = %#x, b = %#x, c = %#x\n", ts2.a, ts2.b, ts2.c);
    fclose(fp);
    return 0;
}

发送方和接收方的对齐系数都是 1。
先运行发送方,将结构体数据写入到文件中;再运行接收方,从文件中读取结构体数据到结构体中。
运行打印如下:

$ gcc send_data.c 
$ ./a.out 
send success
$ gcc receive_data.c 
$ ./a.out 
receive: a = 0x11, b = 0x2222, c = 0x33333333
$ 

可以看到,当发送方和接收方的对齐系数相同时,接收方可以正常获取数据。

现在把接收方程序的对齐系数改为 4,再次运行接收方程序,打印如下:

$ gcc receive_data.c 
$ ./a.out 
receive: a = 0x11, b = 0x7fff, c = 0xff222233

可以看到,当发送方和接收方的对齐系数不相同时,接收方获取了错误的数据。

为了找到数据出错的原因,我们使用 gdb 查看发送方和接收方的内存:
发送方内存:

(gdb) display &ts2
1: &ts2 = (TestStruct2 *) 0x7fffffffdea9
(gdb) x/7xb 0x7fffffffdea9
0x7fffffffdea9: 0x11    0x33    0x33    0x33    0x33    0x22    0x22

接收方内存:

(gdb) display &ts2
1: &ts2 = (TestStruct2 *) 0x7fffffffde9c
(gdb) x/12xb 0x7fffffffde9c
0x7fffffffde9c: 0x11    0x33    0x33    0x33    0x33    0x22       0x22    0xff
0x7fffffffdea4: 0xff    0x7f    0x00    0x00
(gdb)

把这些信息放在表格里面:
在这里插入图片描述
可以看到,接收方可以完成获取发送方发送的 7 个字节的数据,但是,接收方获取成员数据的偏移量与发送方写入成员数据的偏移量不同,造成了 intshort 这两个类型的数据读取了一部分垃圾值。

3 最后

本文到这里就草草结束了,虽然有些啰嗦,但是还是希望可以帮助到大家。

参考

  1. 字节对齐专题
  2. GDB查看内存命令(x命令) 用gdb查看指定地址的内存内容
  3. C语言深度解剖 3.6.8 #pragma pack
  4. 深度刨析为何要内存对齐
  5. 深入理解C语言内存对齐

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

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

相关文章

今天 4 点,龙蜥自动化运维平台SysOM 2.0的诊断中心功能介绍 | 第 66-68 期

本周 3 期「龙蜥大讲堂」预告来啦&#xff01;我们邀请了系统运维 SIG Contributor 阙建明分享《SysOM 2.0 诊断中心功能介绍》&#xff0c;龙蜥社区云原生机密计算 SIG Maintainer、Intel 高级云计算软件工程师黄晓军分享《Intel HE Toolkit 介绍》主题演讲&#xff0c;龙蜥社…

【Linux】操作系统与Linux — Linux概述、组成及目录结构

目录 一、什么是操作系统&#xff1f;都有那些&#xff1f; 二、Linux概述 三、Linux组成 三、Linux目录结构 四、Linux目录结构 &#x1f49f; 创作不易&#xff0c;不妨点赞&#x1f49a;评论❤️收藏&#x1f499;一下 一、什么是操作系统&#xff1f;都有那些&#x…

频率信号转电压或电流信号隔离变送器0-1KHz /0-5KHz /0-10KHz转0-2.5V/0-5V/0-20mA

主要特性:>> 精度等级&#xff1a;0.2级>> 全量程内极高的线性度&#xff08;非线性度<0.1%&#xff09; >> 辅助电源/信号输入/信号输出&#xff1a; 2500VDC 三隔离>> 辅助电源&#xff1a;5VDC&#xff0c;12VDC&#xff0c;24VDC等单电源供电&g…

2020蓝桥杯真题约数个数(填空题) C语言/C++

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 1200000 有多少个约数&#xff08;只计算正约数&#xff09;。 运行限制 最大运行时间&#xff1a;1s 最大运行内存: 128M 所需变量 int a 1200000;//初始最大数 i…

模式识别 —— 第一章 贝叶斯决策理论

模式识别 —— 第一章 贝叶斯决策理论 前言 新的学期开始了&#xff0c;当然是要给不爱吃香菜的月亮记录学习笔记呀~ 没多久了&#xff0c;待夏花绚烂之时~人山人海&#xff0c;我们如约而至&#xff01; 以后清河海风 溶溶月色 共赏之人 就在身侧 mua~ 文章目录模式识别 —…

【服务器数据恢复】HP EVA存储lun丢失的数据恢复案例

服务器故障&检测&分析&#xff1a; 某品牌EVA存储设备中的RAID5磁盘有两块硬盘掉线&#xff0c;lun丢失。硬件工程师对故障服务器进行物理故障检测&#xff0c;发现掉线硬盘能够正常读取&#xff0c;无物理故障&#xff0c;也没有发现坏道。 故障服务器掉线硬盘没有物理…

深度理解Redux原理并实现一个redux

Redux的作用是什么 Redux的作用在于实现状态传递、状态管理。在这里你可能会说了&#xff0c;如果是状态传递&#xff0c;那我props的传递不也是可以达到这样的效果吗&#xff1f;context上下文方案不也是可以达到这样的效果吗&#xff1f;没错&#xff0c;是这样的&#xff0…

汇川SV660N与基恩士 KV7500 控制器调试说明

1. 伺服相关部分配置 1.1 伺服相关版本 SV660N 试机建议使用“SV660N-Ecat_v0.09.xml”及以上设备描述文件。 SV660N 单板软件版本建议为“H0100901.4”及更高版本号。 1.2 相关参数说明 SV660N 对象字典中 60FD 的含义较 IS620N 有所更改&#xff1a;bit0、1、2 分别为负限位…

移动字母--降维与DFS

一、题目描述 2x3=6 个方格中放入 ABCDE 五个字母,右下角的那个格空着。如下图所示。 和空格子相邻的格子中的字母可以移动到空格中,比如,图中的 C 和 E 就可以移动,移动后的局面分别是: A B D E C A B C D E 为了表示方便,我们把 6 个格子中字母配置用一个串表示出…

如何创建出实用的员工手册?

员工手册主要是企业内部的人事制度管理规范&#xff0c;包含企业规章制度和企业文化&#xff0c;同时还起到了展示企业形象、传播企业文化的作用。它既覆盖了企业人力资源管理的各个方面规章制度的主要内容&#xff0c;又因适应企业独特个性的经营发展需要而弥补了规章制度制定…

【HTML】列表结构

列表结构HTML效果HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdev…

HIVE --- zeppelin安装

目录 把zeppelin压缩包拷贝到虚拟机里面 解压 改名 修改配置文件 编辑zeppelin-site.xml—将配置文件的ip地址和端口号进行修改 编辑 zeppelin-env.sh—添加JDK和Hadoop环境 配置环境变量 刷新环境变量 拷贝Hive文件 拷贝外部文件 启动zeppelin 启动Hadoop&Hi…

Web API接口鉴权方式

一、什么是鉴权&#xff1f;为什么要鉴权 鉴权&#xff08;authentication&#xff09;&#xff0c;也叫做认证&#xff0c;即验证用户是否拥有访问系统的权利。 HTTP本身是无状态的请求&#xff0c;每次请求都是一次性的&#xff0c;并不会知道请求前后发生了什么。但在很多…

记一次linux服务器磁盘空间占满的问题排查

问题&#xff1a;服务器安装后两天&#xff0c;发现磁盘空间使用满了【date: write error: No space left on device】问题排查&#xff1a;1、使用df -hl命令查看2、使用du -hl --max-depth1&#xff0c;从根目录开始查起&#xff0c;最后发现&#xff0c;磁盘的空间全部被/va…

自学5个月Java找到了9K的工作,我的方式值得大家借鉴 第二部分

我的学习心得&#xff0c;我认为能不能自学成功的要素有两点。 第一点就是自身的问题&#xff0c;虽然想要转行学习Java的人很多&#xff0c;但是非常强烈的想要转行学好的人是小部分。而大部分人只是抱着试试的心态来学习Java&#xff0c;这是完全不可能的。所以能不能学成Jav…

【Linux】项目的自动化构建-make/makefile

&#x1f4a3;1.背景会不会写makefile&#xff0c;从一个侧面说明了一个人是否具备完成大型工程的能力 一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;makefile定义了一系列的 规则来指定&#xff0c;哪些文件需要先编译&#xff…

Java List系列(ArrayList、LinekdList 以及遍历中删除重复元素时发生的异常和解决办法)

目录List集合系列List系列集合特点List集合特有方法List集合的遍历方式ArrayList集合的底层原理分析源码LinkedList集合的底层原理集合的并发修改异常问题&#xff08;删除重复元素时&#xff09;List集合系列 List系列集合特点 ArrayList、LinekdList &#xff1a;有序&#…

HNU工训中心:电子开关与信号隔离

工训中心的牛马实验 1.实验目的&#xff1a; 1) 认识三极管和MOS管构成三端电子开关电路&#xff1b; 认识信号隔离的继电器和光电隔离方式。 2) 认识施密特触发器&#xff0c;掌握一种波形变换方法。 3) 实现一种脉冲波形发生器。 2.实验资源 HBE硬件基础电路实验箱、示波…

2.FFmpeg5.1下载和使用

1.FFmpeg库下载 进入http://ffmpeg.org/download.html 官网,如下图所示: 由于我们初期只在windows上

北京筑龙吴英礼:ChatGPT对采购与招标数字化的影响

2月25日下午&#xff0c;平台经济学沙龙&#xff08;第八期&#xff09;在清华大学互联网产业研究院成功举办。本期沙龙以“ChatGPT对招标采购的影响”为主题&#xff0c;由清华大学互联网产业研究院平台经济课题组组长、中国招标投标公共服务平台原总经理、首席经济学家平庆忠…