闲谈闲谈闲谈

闲谈

首先呈上c语言常用数据类型对应字节数
32位编译器:

  char :1个字节
  char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
  short int : 2个字节
  int:  4个字节
  unsigned int : 4个字节
  float:  4个字节
  double:   8个字节
  long:   4个字节
  long long:  8个字节
  unsigned long:  4个字节

64位编译器:

  char :1个字节
  char*(即指针变量): 8个字节
  short int : 2个字节
  int:  4个字节
  unsigned int : 4个字节
  float:  4个字节
  double:   8个字节
  long:   8个字节
  long long:  8个字节
  unsigned long:  8个字节

有必要说一下指针在32为是4个字节(32bit),64位是8个字节,这里就要懂一点,32位处理器内存是32位的寻址空间,即2^32,那么我们的指针是存放的地址,他就必须要有足够的位来表示每一个空间,比如32位的内存空间,那么他就他有32来表示所有的可能性,如地址是0x12345678(0001 0010 0011 0100 0101 0110 0111 1000),这32为我为了表示他指针必须也是有这么多位的。其实不管32或者64位其实最主要的就是因为内存跟CPU之间的数据线,也就是决定他们一次处理多少位。寻址范围取决于地址线的数量,32位系统下巧合地址线也是32位,所以最大内存是4G,如果是64位系统,地址线数量一般是40,最大支持1T的地址空间,但是几乎能达到16G就很不错了。
在这里插入图片描述

32位系统里内存地址长度是32位的. 所以32位的地址范围就是从 0000 0000 0000 0000 0000 0000 0000 0000 到 1111 1111 1111 1111 1111 1111 1111 1111 啦(Ox00000000 ~ OxFFFFFFFF), 这里有几个地址呢? 明显是有 2^32 个。

那么2^32到底是多少个? 2^32 = 4 * 1024 * 1024 * 1024= 4294967296 , 就是4G 啊, 而每1个地址对应1个1个字节(所以不管多少位,每个地址的最小单元就是字节), 容量就是1byte, 所以2^32个地址就总共能对应应4GB 的内存容量啊, 这里的B指的是byte 字节啊。


linux内核框架

直接放上三个图,大家就清楚了在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上面的三个图都是一样的其实内核主要有五大子系统:

进程管理子系统
内存管理子系统
文件系统子系统
网络协议子系统
设备管理子系统

const

在这里插入图片描述


static

  • 全局静态变量
    在全局变量前加上关键字static,全局变量就定义成一个全局静态变量。
    静态存储区,在整个程序运行期间一直存在。
    初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化)。
    作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。

  • 局部静态变量
    在局部变量之前加上关键字static,局部变量就成为一个局部静态变量。
    内存中的位置:静态存储区。
    初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化)。
    作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变。

  • 静态函数
    在函数返回类型前加static,函数就定义为静态函数。函数的定义和声明在默认情况下都是extern的,但静态函数只能在声明他的文件当中可见,不能被其他文件所用。
    函数的实现使用static修饰,那么这个函数只可在本cpp内使用,不会同其他cpp中的同名函数引起冲突。

  • 类的静态成员
    static 成员变量的内存既不是在声明类时分配,也不是在创建对象时分配,而是在(类外)初始化时分配。反过来说,没有在类外初始化的 static 成员变量不能使用。
    static 成员变量不占用对象的内存,而是在所有对象之外开辟内存,即使不创建对象也可以访问。具体来说,static 成员变量和普通的 static 变量类似,都在内存分区中的全局数据区分配内存,由bss区分配

  1. 一个类中可以有一个或多个静态成员变量,所有的对象都共享这些静态成员变量,都可以引用它。
  2. static 成员变量和普通 static 变量一样,都在内存分区中的全局数据区分配内存,到程序结束时才释放。这就意味着,static 成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。而普通成员变量在对象创建时分配内存,在对象销毁时释放内存。
  3. 静态成员变量必须初始化,而且只能在类体外进行。
  • 类的静态函数
    1、普通成员函数可以访问所有成员(包括成员变量和成员函数),静态成员函数只能访问静态成员。普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针,只能访问静态成员,静态数据成员只存储一处,供所有对象共用。
数组指针

首先我们定义一个数组指针

int a[3][4];
 p=a;        //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
 p++;       //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]

p+1会使得指针指向二维数组的下一行,p-1会使得指针指向数组的上一行。因为指针指向的是int[4]类型的,一个类型有16个字节。

*(p+1)+1表示第 1 行第 1 个元素的地址.

因为*(p+1)单独使用时表示的是第 1 行数据,放在表达式中会被转换为第 1 行数据的首地址,所以地址+1就是第一行第一个元素地址。

flash

FLASH存储器又称闪存,它结合了ROM和RAM的长处,不仅具备电子可擦出可编程(EEPROM)的性能,还不会断电丢失数据同时可以快速读取数据 (NVRAM的优势),U盘和MP3里用的就是这种存储器。在过去的20年里,嵌入式系统一直使用ROM(EPROM)作为它们的存储设备,然而近年来 Flash全面代替了ROM(EPROM)在嵌入式系统中的地位,用作存储Bootloader以及操作系统或者程序代码或者直接当硬盘使用(U盘)。

目前Flash主要有两种NOR Flash和NADN Flash。NOR Flash的读取和我们常见的SDRAM的读取是一样,用户可以直接运行装载在NOR FLASH里面的代码,这样可以减少SRAM的容量从而节约了成本。NAND Flash没有采取内存的随机读取技术,它的读取是以一次读取一快的形式来进行的,通常是一次读取512个字节,采用这种技术的Flash比较廉价。用户 不能直接运行NAND Flash上的代码,因此好多使用NAND Flash的开发板除了使用NAND Flah以外,还作上了一块小的NOR Flash来运行启动代码。

一般小容量的用NOR Flash,因为其读取速度快,多用来存储操作系统等重要信息,而大容量的用NAND FLASH,最常见的NAND FLASH应用是嵌入式系统采用的DOC(Disk On Chip)和我们通常用的“闪盘”,可以在线擦除。

寄存器

寄存器的种类和个数:(以32位而言)

  • 4个数据寄存器
  • 2个变址寄存器和2个指针寄存器
  • 6个段寄存器
  • 1个指令寄存器和1个标志寄存器

1、数据寄存器:(数据、变址、指针寄存器合称为通用寄存器)

  • CPU中:数据寄存器(累加器)主要用于保存操作数和运算结果,从而减少CPU访问总线或者储存器的时间。而且16寄存器再存储数据时候,低8位的数据不会影响高8的数据,所以我们通常可以将一个16位的寄存器分成两个8位寄存器。它具有“可分可和”的特性,程序员可以自己灵活处理。
  • 在运算器中:累加器是专门存放算术或逻辑运算的一个操作数和运算结果的寄存器。能进行加、减、读出、移位、循环移位和求补等操作。是运算器的主要部分。

2、指令寄存器
指令指针EIP、IP(Instruction Pointer)是存放下次将要执行的指令在代码段的偏移量

用户态到内核态

在上面有图,linux内核的图,有三种方式。

  • 系统调用:这是用户进程主动要求切换到内核态的一种方式,用户进程通过系统调用申请操作系统提供的服务程序完成工作。
  • 异常:软件中断,当CPU正在执行运行在用户态的程序时,突然发生某些预先不可知的异常事件,这个时候就会触发从当前用户态执行的进程转向内核态执行相关的异常事。
  • 外围设备的中断:当外围设备完成了用户请求,给内核发送中断消息,CPU就会暂停执行下一条即将要执行的指令,转而去执行中断信号对应的处理程序,如果是程序是用户态的,就从内核态转到了用户态。
中断

1、什么是中断

  • 中断是指计算机在执行过程中,系统发生异常事件,使CPU立马停止当前程序去处理中断请求的事件,服务执行完毕后再返回断点处继续执行的情形。引起中断发生的事件被称为中断源。中断源向CPU发出中断请求信号叫中断请求,CPU处理相应的中断请求事件叫中断响应

2、什么是禁止中断

  • 禁止中断是指中断源发出中断请求,CPU标志寄存器中的中断允许位控制,IF位为1,可以得到CPU的响应,否则,得不到响应。那么CPU就不允许处理响应了。禁止中断也成为关中断,中断允许为被允许也成为开中断

3、什么是中断屏蔽

  • 中断屏蔽是指在中断请求产生之后,系统有选择地封锁一部分中断而允许另一部分中断仍能得到响应。不过,有些中断请求是不能屏蔽甚至不能禁止的,也就是说,这些中断具有最高优先级,只要这些中断请求一旦提出,CPU必须立即响应。

4、中断的优缺点

  • 优点速度更快,
  • 缺点:打断CPU正常运行秩序,会进行上下文切换。如果通过中断来传输数据,必定会大大降低系统执行效率,对于高速I/O设备,以及批量传输的情况,才用DMA才是最佳选择。

5、CPU怎么知道中断位置

  • 中断发生之后CPU会再中断向量表中寻找,根据中断偏移量找到对应的中断向量,然后再跳到相应的程序位置。
小、大端字节序的定义和原理

字节序就是数据类型大于1个字节的数据,在内存中存放的顺序(例如一个16bit的short型x,x的值为0x1122,那么0x11为高字节,0x22为低字节)

(1)小端:低字节放在低地址
(2)大端:低字节放在高地址

例子:在内存中0x01020304的存储方式
内存地址 :
  4000  4001  4002   4003
LE 04    03    02    01
BE 01    02    03    04
为什么有小、大端字节序:
因为出了char类型是1个字节,int、short、double都是多个字节的,多个字节的储存就是一个问题,所以出现了小、大端的字节序。
为什么网络字节序采用大端模式:
在数据传输过程中,/TCP/IP协议规定:把接收到的第一个字节当作高位字节看待,这就要求发送端发送的第一个字节是高位字节;那么我们从低地址发送,低地址就要存放高字节,也就是大端字节序。

字节对齐
  • 什么叫字节对齐
    计算机对基本数据类型在内存中的存储做了一些限制,要求存储的地址为2、4、8的倍数。
  • 为什么要字节对齐
    1、需要字节对齐的根本原因在于CPU访问数据的效率问题,
    2、 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

比如,你的电脑规定4字节对齐,那么数据存放在内存以4字节为一个单位,这样当CPU访问的数据的时候就值需要访问一次,如果不字节对齐的话,如:
typedef struct __test
{
char c_x; //1 byte
float f_y; //4 bytes
} test_type1_t
不字节对齐这样一个结构体,在内存中存放的地址就为 c_x的地址为0x01,f_y的地址为0x02-0x05,那么当CPU访问的时候,4个字节读一次,先读0x01-0x04,读出了c_x ,而读f_y的时候就还需要读一次 0x05-0x08,再将两部分和起来,这样就比较影响效率。

#include <stdio.h>

typedef struct __test
{
  char c_x; //1 byte
  float f_y; //4 bytes
} test_type1_t; //此时字节对齐为8

typedef struct __test2
{
 char c_x; //1 byte
  double d_y; //8 bytes
} test_type2_t; //此时字节对齐,为16


typedef struct __test3
{
   double d_y; //8 bytes
   char c_x;
} test_type3_t; //此时字节对齐,仍然是16

typedef struct __test4
{
    char c_x;
    double d_y;
} __attribute__((packed)) test_type4_t;//取消了字节对齐 

int main()
{
	test_type1_t test1_temp;
	test_type2_t test2_temp;
	test_type3_t test3_temp;
	test_type4_t test4_temp;
	printf("the bytes count of 'char + float' is :%d\n",sizeof(test1_temp));
	printf("the bytes count of 'char + double' is :%d\n",sizeof(test2_temp));
	printf("the bytes count of 'double + char' is :%d\n",sizeof(test3_temp));
	printf("the bytes count of 'char + double +attribute(packed)' is :%d\n",sizeof(test4_temp));
	return 0;
}

如何在设计结构体的时候,既要满足对齐,又要节省空间:让占用空间小的成员尽量集中在一起

  • 对齐规则
    • 一般设置的对齐方式为1,2,4字节对齐方式;
    • 结构的首地址必须是结构内最宽类型的整数倍地址;
    • 结构体的每一个成员起始地址必须是自身类型大小的整数倍
    • 比如:
      struct A
      {
      int a;
      char b;
      short c;
      };
      sizeof(A) 为 8;a的地址为0x01-0x04,b为0x05是自己的整数倍 (5的1的5倍)
DNS域名系统

流程:比如访问www.baidu.com,首先DNS解析会检查自己的浏览器中是否有对应的信息,没有的话就去看本机的hosts有没有,还没有就去看本区域的DNS服务器看,找到根域名服务器,他告诉你顶级域名也就是.com服务器的地址,你再去找.com顶级域名的地址,顶级域名的地址又告诉你,权威域名baidu.com服务器的地址,你又去找,然后找到ip,返回。

TCP粘包和分包
  • 粘包:有时候我们发送数据过小,发送缓冲区为了更高效的组织数据会采用优化算法(Nagle算法),将小数据一起放在缓冲区,后一包的头部紧接着前一包的尾部,然后封包发送。
    • 粘包原因
      1、Nagle算法(发送方)
      Nagle算法算法有两个作用:

      • 上一组数据得到确认,发送下一组
      • 将小数据合并成大数据发送

      2、发送接受速度不匹配
      TCP接收到网络传来的数据包不会立马给应用层,而是放入读取缓存中,然后应用程序主动读取缓存中的数据。这样的话,如果TCP接受过快,而读取过慢,就会造成包都粘再一起了。

    • 粘包处理方法:
      1、发送方:取消Nagle算法
      2、发送、接收方:应用层来处理,将数据格式化,设置开始、结束符,或者规定数据长度,发送多少接受多少。

    • 例子:
      当我发送data1和data2的时候出现如下问题就是粘包
      1、先接收到data1的部分数据,然后接收到data1余下的部分以及data2的全部.
      2、先接收到了data1的全部数据和data2的部分数据,然后接收到了data2的余下的数据.
      3、一次性接收到了data1和data2的全部数据.

  • 分包:数据太大了,如果数据超过最大消息长度(MSS),那么在传输层进行拆分,这个时候接收端接收到的就是两个数据包。这里我们了解个概念,路由器有一个最大传输单元(Maximum Transmission Unit,MTU),这个是由路由器、网卡决定的,最大位1500个字节,减去IP头部(最小20字节,最大60字节,40字节是因为ip首部有一个信息是首部长度4位,有16个取值,每行四个字节,4*15=60),减去TCP首部(最小20),那么MSS基本上最大位1460,那么如果我发送1500个字节的数据就需要拆分,一个是1460一个是40,所以需要接收两次。
volatile

volatile 是一个类型修饰符。volatile 的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,
volatile修饰的变量一旦被修改会在内存中将这个值更新,不会只储存在储存器中,特别在多线程的时候,修改线程A修改了变量为了让线程B立马看见。最好使用volatile变量。

TCP流量控制
1、TCP滑动窗口

我们发送方可以连续发出若干个分组然后等待确认,而不是发送一个分组就停止并等待该分组的确认。
事先规定谁好滑动窗口大小,然后每次发送端发送几个字节过去,接收端收到了会发一个确认,并告诉发送端我要接收几个字节,和第几个位置的数字,如果接收方若没有缓存足够使用,就会发送零窗口大小的报文,此时发送放将发送窗口设置为0,停止发送数据。

Linux板级描述信息——DTB

1、为什么出现设备树
之前我们编译linux比如在使用arm7系列的处理器的时候,我们在做内核的时候需要修改内核中关于开发板信息的,以前在arch/arm/plat-xxx和arch/arm/mach-xxx中就是我们的开发板的信息,但是,后来随着发展我们把板级信息跟Linux分离出来做成了DTB,这样的话就让Linux内核减少了很多无用的代码,而是指定特定的DTB(device tree block)
为什么我们需要把开发板的信息给Linux内核呢?因为我们每一个操作系统运行都需要依赖我们的处理器,要获取寄存器、内存、中断信息、GPIO、外围设备、FLASH等信息,使得内核根据这些资源进行后续的初始化操作。

2、 DTS
DTS是设备树文件,DTB是通过DTS编译生成的二进制文件
可以这样抽象:

1、dtb = dts + dtsi
2、dtsi相当于include,我们在dts中会引入dtsi

3、DTS基本语法

  • 跟节点:\
  • 设备节点:nodex
  • 节点名称:图中node
  • 节点地址:@后面
  • 子节点:图中child-node
  • 属性:属性名称(Property name)和属性值(Property value)
  • 属性值:三种表示
    • " "
    • < >
    • [ ]
grub

当bios完成自检之后,bios会找到我们的MBR(主引导记录),MBR分为三个部分,第一个部分446字节,是负责检查硬盘分区表、寻找可引导分区并负责将可引导分区的引导扇区(DBR)装入内存,系统由此开始启动; 第二个部分是64个字节,里面记载了每个分区的类型、大小和分区开始、结束的位置等重要内容;第三个部分是结束符55AA。然后找到我们的grub程序,此时mbr将权限给grub,gurb分为三个步骤。

  • start1:它被安装在MBR扇区(0面0磁道的第1扇区),大小512个字节,start1是引导其他模块的,当start1_5被配置时,start1将加载start1_5,否则如果没有配置start1_5,start1就加载start2.
  • start1_5:由于start1只有不到512个字节,因此无法识别文件系统,start1_5的任务就是从文件系统中加载start2,为了支持多种文件系统,start1_5被设计为针对多种不同的文件模块,e2fs_start_5,fat_start_5等等,start1首先将start1_5(0面0磁道的第2扇区)的内容读取内存中,依靠start.s的扇区列表把start1_5的全部内容读进内存,
  • stage2:start2是grub的主体,所有功能都在start2中实现,如果没有配置start1_5,start1会将start.s读入内存,然后依靠扇区列表将start2全部读入内存。
  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值