大端和小端
在大端系统中,最高有效位存储在低地址内存中,最低有效位存储在高地址中。
例如,0x123456在内存中的存储方式
大端模式:
低地址 -----> 高地址
0x12 | 0x34 | 0x56
小端模式:
低地址 -----> 高地址(低存低更符合直觉)
0x56 | 0x34 | 0x12
Why?
-
一开始是由于不同架构的CPU处理多个字节数据的顺序不一样,比如x86的是小段模式,KEIL C51是大端模式。但是后来互联网流行,TCP/IP协议规定为大端模式,为了跨平台通信,还专门出了网络字节序和主机字节序之间的转换接口(ntohs、htons、ntohl、htonl)
-
大小端模式各有优势:小端模式强制转换类型时不需要调整字节内容,直接截取低字节即可(也更符合直觉);大端模式由于符号位为第一个字节,很方便判断正负。
如何判断大小端?
BOOL IsBigEndian()
{
short a = 0x1234;
char b = *(char*)&a; //强制类型转化
if(0x12 == b)
{
return TRUE;
}
return FALSE;
}
32和64位系统
- 处理数据的能力:32位计算机的CPU一次最多能处理32位数据。64位计算机一次能处理64位数据。这和寄存器位数有关
- 支持的内存不同(寻址能力不同):32位的系统许多支持4G的内存,而64位则可以支持上百G的内存。
- 架构不同:不同位处理意味着指令集等要做重新设计,一般64位向32位兼容
栈和堆
栈
栈内存首先是一片内存区域,存储的都是局部变量,凡是定义在方法中的都是局部变量(方法外的是全局变量),for循环内部定义的也是局部变量,是先加载函数才能进行局部变量的定义,所以方法先进栈,然后再定义变量,变量有自己的作用域,一旦离开作用域,变量就会被释放。栈内存的更新速度很快,因为局部变量的生命周期都很短。
堆
存储的是数组和对象(其实数组就是对象),凡是new建立的都是在堆中,堆中存放的都是实体(对象),实体用于封装数据,而且是封装多个(实体的多个属性),如果一个数据消失,这个实体也没有消失,还可以用,所以堆是不会随时释放的。
进程和线程
进程: 一个正在运行的程序,为系统中分配和管理内存资源的最小单元
线程: 程序执行的最小单元(函数)
进程
- 孤儿进程: 一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程所收养,并由init进程对它们完成状态收集工作。
- 僵尸进程: 一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
- 守护进程: 守护进程就是在后台运行,不与任何终端关联的进程,通常情况下守护进程在系统启动时就在运行,它们以root用户或者其他特殊用户(apache和postfix)运行,并能处理一些系统级的任务。
如何处理僵尸进程:
- 子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。
- fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收 还要自己做。两次fork的原理是将子进程成为孤儿进程,从而其的父进程变为init进程,通过init进程可以处理僵尸进程。
多进程
优点:
- 资源隔离,不同进程变量之间不会相互冲突
- 每个进程相互独立,子进程崩溃不影响主进程(安全)
- 每个进程能使用的空间很大(4G)
- 可以尽量减少线程加锁/解锁的影响,极大提高性能(IPC很少涉及加锁)
缺点:
- 多进程调度开销较大(需要上下文切换)
- 进程间通讯效率低,比较适合小数据量传送(涉及IPC)
多线程
优点:
- 共享内存和变量,数据交互效率高
- 程序逻辑和控制方式简单
缺点:
- 共享内存和变量处理不好,可能会发生冲突
- 可用空间相互受限,且线程数量过多影响性能提升
- 一个线程崩了可能影响整个程序
- 线程之间同步比较麻烦
使用场景
- 频繁创建和销毁资源优先线程(创建和销毁一个进程代价是很大)
- 频繁切换,需要大量计算(耗时操作)尽量使用线程
- 多机分布用进程(跨平台通讯),多核分布用线程
- 大量数据交互选线程
- 需要稳定安全时用进程
进程/线程状态和转换
异常和中断
异常一般指软中断,由程序产生,目的是要产生系统调用,需要切换进程上下文
中断是指硬中断,由硬件产生,目的是响应外设,需要切换中断上下文
中断处理流程:
- 中断响应前准备:准备中断向量表
- CPU检查是否有中断产生,得到中断号
- 查表得到中断程序
- cpu根据优先级决定要运行的中断程序
- 保存上下文
- 跳转到中断程序执行
- 执行完,恢复先前状态
上下文
- 上文: 已执行过的进程指令和数据在相关寄存器与堆栈中的内容(前面执行状态和数据)
- 正文: 正在执行的指令和数据在寄存器和堆栈中的内容
- 下文: 待执行的指令和数据在寄存器与堆栈中的内容(即将要执行的程序)
进程上下文
当触发系统调用或异常,进程由用户态切换到内核态时,需要保存当前进程的所有状态(用户态时cpu寄存器中的值,进程状态以及堆栈上的内容),即进程上下文
- 进程上文: 切换时cpu寄存器中的值,进程状态以及堆栈上的内容
- 进程下文: 主要指切换到内核态后执行的程序
中断上下文
由硬件产生中断,导致需要切到中断服务程序过程中,需要保存硬件信息和当前状态
- 中断上文: 看作是硬件传递过来的这些参数和内核需要保存的一些其他环境(主要是当前被中断的进程环境)
- 中断下文: 主要指马上要执行的中断服务程序
同步和互斥
同步:一个程序/线程的执行依赖于另一个的消息
互斥:共享资源只允许一个进程/线程使用
用户空间与内核通讯
- 系统调用,提供特定的用户空间与内核空间的信息传递。
- 信号,内核空间出现一些异常时候会发送信号给进程,如SIGSEGV、SIGILL、SIGPIPE等。
- /proc,proc可以读取内核空间的信息并且设置部分属性的值,需要循环检测。
- 文件,可以通过指定文件的读写操作来实现通信,但是流程不够实时,需要循环检测来实现。
- netlink,类似socket通信方式,可以读写大量的数据,实现稍微复杂。
- ioctl,可以实现数据量比较少时候的通信。
系统调用
系统调用是操作系统提供给应用程序(程序员)使用的接口,可以理解为一种可供应用程序调用的特殊函数,应用程序发出系统调用请求来获得操作系统的服务。
作用
- 操作系统作为用户和硬件的接口,向上提供一些简单易用的服务,向下执行具体硬件操作
- 安全,防止非法操作
分类
设备管理、文件管理、进程控制、进程通信、内存管理
具体如何实现
程序在用户空间中通过0x80中断,将控制权交给内核空间,内核根据调用好调用对应的内核函数,来执行实际处理
如:用户空间read()–> 0x80中断 -->内核空间sys_read()–>scull_fops.read–>scull_read()
linux同步和互斥
原子操作
原子操作内不可被软中断(如系统调用机制)打断,汇编层面实现,操作对象只能是整数
自旋锁
它锁住的是一块临界区,进入临界区时需要先获取自旋锁,在离开临界区时需要释放自旋锁。如果有进程已经获取了自旋锁,但是还没有释放,那么其它想要获取自旋锁的进程就得等(而且不会让出cpu,死等)
读写锁
这种锁允许多个进程同时对同一数据结构进行读操作,但不允许多个进程同时对同一数据结构进行写操作。
信号量
信号量和自旋锁类似,也是为了控制进程进入临界区。本身就是为多核处理器服务的。
但是信号量和自旋锁的最大区别是:
- 自旋锁禁止cpu抢占,而信号量不禁止cpu抢占。 所以自旋锁在锁住后进程不能进入睡眠,否则将不会有进程抢占cpu而运行。
- 信号量只能用于进程上下文,而不能用于中断上下文。 由于信号量发生争用的时候,没获取到信号量的进程会睡眠,而中断上下文不允许睡眠(执行过程也不允许抢占),所以中断上下文中只能使用自旋锁;
- 获取自旋锁的过程,不会主动调用schedule()进行进程切换,而是占着CPU,拼命的自旋,类似于while耗时操作;
- 信号量中存在一个进程等待队列,为获取锁的进程将挂接到该队列中,然后主动调用schedule()切换进程,让出CPU。适用于长时间持有情况。
内存屏蔽
在指令之间插入一道屏障,两句屏障指令之间的指令不会因为优化而重排。
互斥锁
死锁
ARM、DSP和FPGA
ARM具有比较强的事务管理功能,可以用来跑界面以及应用程序等,其优势主要体现在控制方面,它的速度和数据处理能力一般,但是外围接口比较丰富,标准化和通用性做的很好,而且在功耗等方面做得也比较好,所以适合用在一些消费电子品方面;
DSP主要是用来计算的,比如进行加密解密、调制解调等,优势是强大的数据处理能力和较高的运行速度。由于其在控制算法等方面很擅长,所以适合用在对控制要求比较高的场合,比如军用导航、电机伺服驱动等方面。
FPGA是可编程逻辑阵列,用硬件描述语言定义各硬件之间连接关系,非常灵活定义不用电路功能,且擅长进行并行处理
IO口工作方式
-
上拉电阻:将一个不确定的信号,通过一个电阻与电源VCC相连,固定在高电平。在IO口为输入模式且为上拉电阻时,IO口的常态为高电平。
-
下拉电阻:将一个不确定的信号,通过一个电阻与地GND相连,固定在低电平。在IO口为输入模式且为下拉电阻时,IO口的常态为低电平。
-
推挽输出:可以输出高、低电平,连接数字器件。推挽结果一般是指两个三极管分别受两互补信号的控制,总是在一个三极管导通时令一个三极管截止。(推挽输出的最大特点是可以真正的输出高电平和低电平,且两种电平下都有驱动能力)。IO输出0-接GND, IO输出1 -接VCC
-
开漏输出:输出端相当于三极管的集电极,要得到高电平状态需要加上拉电阻才行。适合做电流型的驱动,其吸收电流的能力比较强(20mA左右)(开漏输出最主要的特性就是高电平没有驱动能力,需要借助外部上拉电阻才能真正输出高电平)。开漏只能输出低电平,高电平的时候实际上是个高阻态,需要外接电阻来拉高的。