嵌入式系统学习杂记

win10 系统常用命令 --------------------------------------

chkdsk D:/f /r //检查硬盘, 有坏道了可以抢救下

rom 不易失 ---------------------------------------------------

1, PROM  一次性, 熔丝

2, EPROM  可编程可擦除, 电写入和紫外线擦除

3, E2PROM 电可擦可编程, 电写入和电擦除

ram 易失 随机存取存储器 --------------------------------

1, SRAM 静态的, 无需刷新 主要用作cpu的cache, 和需要高速的应用

2, DRAM 动态的, 需不断刷新以补充释放的电荷

3, SDRAM 同步动态的, 需不断刷新以补充释放的电荷

flash 不易失 ---------------------------------------------------

1, NOR  可直接寻址,    成本高, 容量小, 随机快, 写入慢, 擦除慢, 省电, 无需坏块处理, 适合存代码

2, NAND 不可直接寻址   成本低, 容量大, 随机慢, 写入快, 擦书快, 耗电, 需坏块处理,   适合存数据

3, eMMC = NAND + 控制器 + 标准封装接口 也就是接口是统一的里面是个 NAND

Linux ENV -----------------------------------------------------

/etc/profile 每个用户设置用, 还有/etc/profile.d 目录下

~/.bashrc 当前用户用

environment path 配置目录, 启动加载一次, 修改需要重启, 改不好可能奔溃的

kernal 内核------------------------------------------------------------

make V=1 显示所有信息

time make -j4 编译且统计时间且使用4核心

Kconfig 文件 用来配置 menucofnig 的配置选项

make xxx_defconfig 加载xxx主板的配置

make uImage -j4    编译Uimage, uImage是专门给uboot用的, uboot会加一个启动头

make pbt           我自定义的发布规则, 编译加发布

.config 文件 保存所有的配置, arch/arm/configs里有所有的板级配置

CONFIG_HelloWorld=y 表示包含在内核 obj-$(CONFIG_HelloWorld) := xx.o

CONFIG_HelloWorld=m 表示编译为模块 obj-$(CONFIG_HelloWorld) := xx.o

rootfs 文件系统-------------------------------------------------------------------

bin 用户命令 busybox

sbin 超级用户命令 busybox

etc 保存系统配置

lib 保存各种库

usr 其他用户命令也有bin和sbin busybox

dev 设备文件,驱动相关, 不关心内容

proc 驱动相关, 不用关心内容

sys  驱动相关, 不用关心内容

home 用户主目录, 可选

mnt 外挂设备, 可选

var 零时, 可选

opt 自己造的可执行文件, 可选

busybox 可以制作rootfs, Simplified modutils 选项要去掉, 否则编译出来的是简易版

http头 ----------------------------------------------------------------------

HOST:PORT POST /form/entry HTTP/1.1 回车

Accept: 接受的介质MIME, 见MIME.c 回车

Accept-Charset: 浏览器可接受的字符集 回车

Accept-Encoding: gzip, deflate, br

accept-language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6

http 响应样例-----------------------------------------------------------------

Accept-Ranges: bytes

Connection: keep-alive

Content-Length: 1

Content-Type: text/html

Date: Sun, 04 Jun 2023 06:49:05 GMT

Etag: "58b576df-1"

Last-Modified: Tue, 28 Feb 2017 13:10:55 GMT

Server: bfe/5

bochs 调试 ------------------------------------------------------------------

s 单步

n 跳出循环 对rep 和 loop 有用 必须在执行loop的那句使用才能跳出

u/2 0xfc5f 从0xfc5f开始反汇编出两条, 一般用于跳出非loop循环

info eflags 查看32位的eflags, 这里有所有的标记

c run

b 07c00 断点

r 显示寄存器

sreg 显示段寄存器

xp/2 0xb8000 显示内存 双字

print-stack 打印栈

q 退出

qemu 虚拟机------------------------------------------------------------------------

qemu-img convert -O qcow LEECHUNG.vhd root.img

qemu-img convert -O raw root.img diskimage.raw

qemu-system-i386.exe -smp cores=1 -m 32 -name asm

qemu-img create -f qcow2 root.qcow2 3G

qemu-img create -f rawroot.raw 10M

linux program ----------------------------------------------------------------

1, #include <bits/wordsize.h>

   #if __WORDSIZE == 64  // 可判断当前机器的位数

linux shell-------------------------------------------------------------------

	sudo chown tarena:tarena ./project -R  //这是目录的拥有者且递归
		
	file 命令 查看文件, 是否下位机
	
	top linux 版本的任务管理器
	
	nice 优先级设置

	ls /sys/module/helloworld/parameters 查看模块参数文件, 一般设权限0取消参数文件, 因为一个要4k浪费内存
	
	ls -l /sys 该目录下罗列了所有的设备驱动和硬件信息 /sys/bus.. /sys/devices/.. 可以在驱动中创建
	
	ls -n 显示id
	
	cat /proc/slabinfo | grep kmalloc 查看slab内存分配
	
	insmod 加载模块
	
	rmmod 卸载模块
	
	lsmod 列出模块
	
	dmesg 模块加载后消息
	
	depmod 更新模块依赖
	
	modprobe xxx 加载模块, 会首先加载依赖, 这两个命令, 需要结合 modules_install
	
	cat /proc/interrupts 查看所有中断
	
	cat /proc/drivers 查看所有设备和设备号
	
	mknod /dev/myled c 250 0  如果dev下没有设备节点则open前需要建立节点 250是主设备号, 0是从设备号, 参考/proc/drivers, 也可自动完成
	
	echo 8 > /proc/sys/kernel/printk 设置内核打印级别, 8以下可以打印. 越小级别越高. 或者内核启动参数追加 quit:4(0-3) debug:10 loglevel=x:x. level1-3基本没消息, 4有些告警信息
	
	dmesg | tail -n 20 查看最后20个printk日志
	
	grep -nR xxx ./xx/
	
	cat ~/.vimrc | xsel -b -i  管道
	
	cp   复制
	
	rm -fr  删除目录
	
	head / tail -5 xxxx 显示头尾5行
	
	sort , uniq(sort -u)  排序和去重
	
	whereis 比 which 详细
	
	apropos 查询命令 忘记命令可以用, 但这个名字也很难记
	
	find ./ -name star.c 查找同名文件
	
	who / w / who am i 查看账号
	
	mkdir -p  创建目录自动补全
	
	ls -R 递归显示
	
	r(4) w(2) x(1)
	
	wx(3) rx(5) rw(6) rwx(7)
	
	chmod 664 xxx.xxx linux 默认权限
	
	chmod o-rx xxx 删除权限rx o(other), a(all), u(user), g(group)
	
	chmod o+rx xxx 加权限
	
	chmod u+s xx 修改有效用户id为root, 需要root执行
	
	chown user:group file1.txt group组下的user
	
	chown :group file1.txt     group组
	
	chown user file1.txt user
	
	chown -R user:group /usr/meng  子目录一起改
	
	chown -h user:group /usr/meng  只修改符号连接
	
	chown -f user:group /usr/meng  不显示错误信息
	
	PATH=$PATH:.  追加当前目录到path, 当前目录找链接库用
	
	env 查看全部环境变量
	
	echo $PATH 查看 path
	
	cat xx > xxx.txt ;标准输出重定向到xxx.txt 可打字 ctrl-d结束
	
	cat xx >> xxx.txt ;追加模式
	
	> 重定向用于文件 , | 管道用于程序
	
	ls > temp
	
	lpr temp        
	
	rm temp      
	
	以上等价于 打印 ls
	
	ls | lpr    
	
	ipcs -m 查看共享内存
	
	ipcs -q 查看消息队列
	
	ipcs -s 查看信号量
	
	ipcs -a 所有
	
	ipcrm -m shmid
	
	ipcrm -q
	
	ipcrm -s
	
	cat /proc/interrupts 查看中断
	
	cat /proc/drivers    查看设备
	
	nm lib.a 查看符号表
	
	ldd a ldd是一个脚本, 查看程序的依赖
	
	readelf -d xx 查看执行elf格式文件的依赖
	
	env 显示环境变量命令
	
	echo $name 输出指定环境变量
	
	pstree 显示进程树
	
	ps     显示进程
	
	ps -a  显示所有用户拥有的控制终端进程信息
	
	ps -x  包括没有控制终端的
	
	ps -u  详尽显示
	
	ps -w  更大列宽显示
	
	ps -aux 显示进程详情
	
	-----
	
	user 进程的用户id
	
	PID
	
	%cpu
	
	%mem
	
	VSZ 占用虚拟内存大小
	
	RSS 占用物理内存大小
	
	TTY 终端次设备号
	
	STAT 进程状态********
	
	    R 运行中
	
	    S 可唤醒睡眠
	
	    D 不可唤醒睡眠, 只能用wake_up唤醒
	
	    T 收到SIGSTOP(19), SIGCONT(18)继续
	
	    Z 僵尸
	
	    < 高优先级
	
	    N 低优先级
	
	    L 存在被锁定的分页
	
	    s 会话首进程
	
	    l 多线程化进程
	
	    + 在前台进程组
	
	*********************
	
	START 进程开始时间
	
	TIME 进程运行时间
	
	COMMAMD 进程启动命令
	
	-----
	
	有效用户id 有实际的权限 如果文件有用户id位 则启动进程的权限为本文件的拥有者.
	
	粘滞位的可执行文件, 是连续存储的.已经没啥用
	
	有粘滞位的文件夹, 里面只能删和改自己的文件
	
	umask 设置权限掩码
	
	tar -jcvf hello.tar.bz2 hello 压缩bz2
	
	tar -zcvf hello.tar.gz hello 压缩gz
	
	tar -xvf hello.tar.bz2/gz 解压
	
	bzcat/zcat 查看压缩包
	
	jobs 查看 当前未结束进程
	
	kill -KILL %1 强制结束%1
	
	fg %1 将挂起的%1号进程恢复, 1可以不写默认是1
	
	命令结尾加 & 后台启动
	
	ctrl-z 加 bg %id 命令后台继续
	
	fg %id 返回前台 如可以回复意外ctrl-z关闭的vim
	
	ls --help | less 使用分页 w上d下 e单步y单步退回 q退出
	
	ls -l 显示权限
	
	ls -ld 显示目录权限
	
	ls -i 显示inode
	
	ls -R 递归
	
	文件状态
	
	-: 普通文件
	
	d: 目录
	
	c: 字符
	
	s: 套接字
	
	b: 块设备
	
	l: 符号链接
	
	p: 有名管道
	
	ln -s xx.f 符号链接
	
	hd file 16 进制显示 -c 显示对照
	
	od file 8 进制显示 -c 显示对照
	
	less 和 more 只有细微差别 如 more 看完就退出
	
	man who 查看who命令文档
	
	man 自动使用less
	
	man 2 write 查看第二个write文档
	
	info 2 write 是错误的
	
	info 和 man不同 h 打开交互文档, ? 列出命令 space 下一页, m 子菜单, q 退出
	
	ctrl alt f7 可回到GUI, 实用前面(f1-f6)6个没界面,可登录其他账户, 用户不能重复登录
	
	passwd 修改密码
	
	ctrl+h 退格, ctrl+u 清理行, ctrl+m 回车,
	
	file 测试文件

vim --------------------------------------------------------------------------------------

:%!xxd  开启二进制模式

vim 编译配置参考

./configure --with-features=huge \

--enable-multibyte \

--with-python-config-dir=/usr/lib64/python2.7/config-2.7m-x86_64-linux-gnu \

--enable-python3interp=yes \

--with-python3-config-dir=/usr/local/lib/python3.8/config-3.8-x86_64-linux-gnu \

--enable-gui=gtk2 \

--enable-cscope \

--enable-quickfix??\

--prefix=/usr/local/vim

shift v 行选, y 复 ,d 切, p 粘,

x 删, dd 行删, cc 行字母, 5dd 切5行,  yy 复制行, 5yy 复制5行 ,

u 撤销, ctrl-r 反悔

shift zz 快读保存退出

ctrl-s 卡住了可用 ctrl-q 恢复

ggvG= 格式化

%s/main/hello/g  全局替换main->hello

33,22s/main/hello/g 局部替换

:vs 文件名 开启视图

ctrl+ww 切换视图

:wqall 关闭所有

:e ++enc=utf-8 filename 编码模式

/main 查找main高亮

:nohl 取消高亮

gcc --------------------------------------------------------------------

gcc -E  x.c -o x.i 预处理

gcc -S  x.i -o x.s 编译->转换汇编

gcc -c  x.s -o x.o 汇编->转换机器码

gcc     x.o -o x.out  链接->程序

gcc -DSIZE 编译时替换宏 SIZE

gcc -fpic -shared -o libled.so led.c 编译动态库 小端模式

make ----------------------------------------------------------------------

.c.o: #老写法

%.o: %.c #新写法

$(GCC) -c $< -o $@

$(GCC) -c $*.c -o $*.o

$< 源 $@ 目标 带后缀

obj-m := xx.o

make -C /opt/kernel SUBDIRS=${PWD} modules  //外部目录编译内核模块

c 预处理 ---------------------------------------------------

#define ID(X) id##X  // 拼接

#define ID(X) #X     // 字符串加双引号

#define SUM ((X,Y) (X) + (Y))  // 计算的宏一定加()

#define SCANF(x, s) scanf("%"#x"s", (s))  // 字符串拼接

printf 参数 ----------------------------------------------------------

%d      // int

%u      // unsigned int

%hd     // short int

%hu     // short unsigned int

%hhd    // char

%hhu    // unsigned char

%ld     // long int

%lu     // unsigned long int

%lld    // long long int

%llu    // unsigned long long int

__FILE__ 文件名

__func__ 程序名

__LINE__ 行号

__DATE__ 日期

__TIME__ 时间s

// 在scanf里 %*s 表示第一个虚读, 而 %*s 在printf里表示*表示宽度参数

scanf(con, "%*s%s", hreq->connection);

number --------------------------------

100          

100u/U

100l/L

100ll/LL

100ul/UL

100f/F

100.0 // 默认是double

0nn 八进制

0xnn 十六进制

0bnn 二进制

100H/h 16

100B/b  2

long 少用  难移植 win 和 linux 不一样

linux 32 long:4 64 long:8

win   32 long:4 64 long:4

ascii -------------------------------

13 回车 CR '\r' 0x0D

10 换行 LF '\n' 0x0A

空格 32

'0' 48 '9' 57

'A' 65 'Z' 90

'a' 97 'z' 122

16进制 规则 ----------------------------------------------

1 0001

2 0010

3 0011

4 0100

5 0101

6 0110

7 0111

8 1000

9 1001

A 1010   10

B 1011   11

C 1100   12

D 1101   13

E 1110   14

F 1111   15


bit 位

0x00000001  0 0 * 4 + 0

0x00000002  1 0 * 4 + 1

0x00000004  2 0 * 4 + 2

0x00000008  3 0 * 4 + 3

0x00000010  4 1 * 4 + 0

0x00000020  5 1 * 4 + 1 (2^1)

0x00000040  6 1 * 4 + 2 (2^2)

0x00000080  7 1 * 4 + 3 (2^3)

0x00000100  8

0x00000200  9

0x00000400  10

0x00000800  11

0x00001000  12

0x00002000  13

0x00004000  14 3 * 4 + 2

0x00008000  15

0x00010000  16

0x00020000  17

0x00040000  18

0x00080000  19

0x00100000  20 5 * 4 + 0

0x00200000  21 5 * 4 + 1

0x00400000  22 5 * 4 + 2

0x00800000  23 5 * 4 + 3

0x01000000  24

0x02000000  25

0x04000000  26 6 * 4 + 2

0x08000000  27

0x10000000  28

0x20000000  29

0x40000000  30

0x80000000  31 7 * 4 + 3

1010101 = 1*2^6 + 1*2^4 + 1*2^2 + 1*2^0 = 85  2转10

85 除 2 取余数 结果逆序  10转2

gcc环境安装 --------------------------------

sudo apt update // 更新库

sudo apt install build-essential  //安装编译器

ld --verbose 查看链接脚本

x86 汇编 ------------------------------------

命令

DB 直接写入字节

DW 2 字节

DD 4 字节

SHR 右移

ALIGNB 16 一直加1 到可以被16整除

RESB 8 追加8个0

寄存器

EAX AX 累加器 AH + AL

ECX CX 计数器 CH + CL

EDX DX 数据寄存器 DH + DL

EBX BX 基址寄存器 BH + BL 可以指定内存地址

PUSHAD 压入部分32位寄存器

ESP SP 栈指针

EIP IP 程序指针

EBP BP 基址指针 可以指定内存地址

ESI SI 源变址 可以指定内存地址

EDI DI 目的变址 可以指定内存地址

ES 附加段 主要是ds不能动的时候代替用

CS 代码段 IP结合用

SS 栈段   SP结合用

DS 数据段 是默认的地址段寄存器, 地址小时用不到设0即可, 设了会乘以16即左移4位, 数据寻址默认段寄存器

FS 无名段

GS 无名段

RET  普通返回

RETF 长跳返回 可用作跳转

IRETD 中断返回

eflags 终端允许状态寄存器, 禁止中断前

EIP IP 指令指针寄存器

objdump -D -b binary -m i386 ipl10.bin

objdump -D -b binary -m i8086 ipl.bin --start-address=0X50

ORG 0x7c00 程序加载到 0x7c00 启动

RESB 10 后面10个字节都是0

地址 X0200 512字节处最后是55 AA 就是启动扇区

shell ------------------------------------------------------------

copy /B asmhead.bin+bootpack.hrb haribote.sys windows 合并两个文件

一月(Jan)、二月(Feb)、三月(Mar)、四月(Apr)、五月(May)、六月(Jun)、七月(Jul)、八月(Aug)、九月(Sep)、十月(Oct)、十一月(Nov)、十二月(Dec)

ssh-keygen -t rsa -C "xxxxx@xxxxx.com" // 生成ssh

c/c++ -------------------------------------------------------------------

const int *p 和 int const *p 指向常量的指针

int const *p 常指针

volatile 标记为易变的, 会禁止优化, 用于防止全局变量的过度优化

sizeof(a=b); sizeof 内赋值无效, 自增无效

#program pack(n) 设定对齐数, 奇数一般不行

c++ ----------------------------------------

explicit 声明不可用于隐式转换

tool --------------------------------------

aptitude search ctags 查看装了多少ctags包

Linux errorno -------------------------------------------------------------------------

#define EPERM        1  /* Operation not permitted */

#define ENOENT       2  /* No such file or directory */

#define ESRCH        3  /* No such process */

#define EINTR        4  /* Interrupted system call */

#define EIO          5  /* I/O error */

#define ENXIO        6  /* No such device or address */

#define E2BIG        7  /* Argument list too long */

#define ENOEXEC      8  /* Exec format error */

#define EBADF        9  /* Bad file number */

#define ECHILD      10  /* No child processes */

#define EAGAIN      11  /* Try again */

#define ENOMEM      12  /* Out of memory */

#define EACCES      13  /* Permission denied */

#define EFAULT      14  /* Bad address */

#define ENOTBLK     15  /* Block device required */

#define EBUSY       16  /* Device or resource busy */

#define EEXIST      17  /* File exists */

#define EXDEV       18  /* Cross-device link */

#define ENODEV      19  /* No such device */

#define ENOTDIR     20  /* Not a directory */

#define EISDIR      21  /* Is a directory */

#define EINVAL      22  /* Invalid argument */

#define ENFILE      23  /* File table overflow */

#define EMFILE      24  /* Too many open files */

#define ENOTTY      25  /* Not a typewriter */

#define ETXTBSY     26  /* Text file busy */

#define EFBIG       27  /* File too large */

#define ENOSPC      28  /* No space left on device */

#define ESPIPE      29  /* Illegal seek */

#define EROFS       30  /* Read-only file system */

#define EMLINK      31  /* Too many links */

#define EPIPE       32  /* Broken pipe */

#define EDOM        33  /* Math argument out of domain of func */

#define ERANGE      34  /* Math result not representable */

linux driver 互斥和同步 ---------------------------------------------------------------------

中断屏蔽 总结:
1, 简单高效的解决中断引起的并发

2, 屏蔽中断应尽量使用local_irq_save和local_irq_restore

3, 中断屏蔽的时间不宜过长, 因为中断必须执行完才会继续调度

4, 只能屏蔽本地cpu中断, 对于对称多处理器 SMP, CPU之间的竞态继续存在
原子变量 总结:
1, 利用了汇编的不可分割性, 和cpu架构密切相关, 汇编不直接支持就用中断屏蔽等手段

2, 只能用于整形变量 int, 能使用的时候尽量使用, 比任何锁和屏蔽消耗少

3, 还有位原子操作

atomic_t available;// 追	
atomic_set(&available, 100); //设置上限

if (atomic_dec_and_test(&available) >= 0) // 测试减法
    return 0;
else {
    atomic_inc(&available);  // 减到 < 0 就返回错误
    return -EBUSY	
}
自旋锁 总结:
1, 获得后的临界代码时间不宜太长, 因为是忙等锁, 自然上锁解锁都是同一个上下文, 持有期间不可退出进程, 否则调度器不再运行

2, 获得锁期间不能调用可能引起进程切换的函数, 忙等是禁止抢占的. 如 copy_xx_user kmalloc 等就很危险

3, 自旋锁不可递归的, 自己等自己会死锁的

4, 可以用于中断上下文, 因为是忙等的, 所以不会引起进程切换

5, 如果中断中要访问共享资源, 非中断那边要获取共享资源时要先禁止中断再获取锁. 此时要使用1,spin_lock_bh 只禁止底半部分关了软中断包括tasklet 和 timer, 不包括工作队列

6, spin_lock_irq 禁止中断, spin_lock_irqsave 恢复中断(推荐备份)

7, 在单处理器可抢占内核中, 自旋锁只是禁止抢占, 且默认可以中断, 多处理器中才是真禁止cpu进程的运行, 因为其他cpu没事做也要等着.

8, 自旋等待是靠汇编的原子操作完成的

// 内核案例 自旋锁核心逻辑

static DEFINE_SPINLOCK(gpio_lock);
spin_lock_irqsave(&gpio_lock, flags);
spin_unlock_irqrestore(&gpio_lock, flags);
static inline void arch_spin_lock(arch_spinlock_t *lock) // lock 全局的锁
{
    unsigned long tmp;
    u32 newval;
    arch_spinlock_t lockval; // 局部的备份 lock
    prefetchw(&lock->slock);

    __asm__ __volatile__(
    "1: ldrex   %0, [%3]\n"          // 读取全局锁lock.slock到 局部的 lockval , 并且标记独占, 说是独占其实也就是如果被抢先改了, 可以知道
    "   add %1, %0, %4\n"            // 将slock的高16位加1, 也就是tickets.next+1
    "   strex   %2, %1, [%3]\n"      // 将结果写回全局的lock.slock, 有独占标记所以失败了会设置%2也就是tmp为1, 成功了tmp=0
    "   teq %2, #0\n"                // 比较tmp和0
    "   bne 1b"                      // tmp不是0则,重新回到1:处, 以上操作就是为了在全局的next里加1, 且保证一定会成功, 最多就是执行多次. 注意局部的next是没有变化的
    : "=&r" (lockval), "=&r" (newval), "=&r" (tmp) // 参数 %0 - 2
    : "r" (&lock->slock), "I" (1 << TICKET_SHIFT) // 参数 %3 - 4
    : "cc");  // 指明改变条件寄存器

    while (lockval.tickets.next != lockval.tickets.owner) { //检查局部备份那个锁里的 next 是否等于同样备份的 owner, 如果当前没有锁自然是相等的直接得到锁, 如果不相等说明当前有锁自旋等待

        wfe(); // 让当前 cpu 进入低功耗模式

        lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner); //更新 owner 到本地备份, 得到锁的进程结束后会把 owner 设为下一个, 没有下一个则保持和next相等, 这样下一个进程可以离开自旋

    }
    // --------------------------------------------------	
    smp_mb()	
}
读写锁 总结:
1, 允许读锁之间的并发

2, 写锁间不可以并发, 和读锁和写锁也不可以.

3, 锁期间应该和自旋锁差不多, 和中断抢资源用 xxirq_lock 等禁止中断,

rwlock_t lock;

rwlock_init(&lock);

write_lock_irqsave(&lock, flags);

i++

wirte_unlock_irqrestore(&lock, flags);

read_lock_irqsave(&lock, flags);

v = i;

read_unlock_irqrestore(&lock, flags);

#### 顺序锁 总结:

1, 允许读和写的并发, 适合读多写少的场合. 但是读要写在循环里, 基本思想是读到脏数据要重新读, 要检查顺序值, 类似 do{ getseq getval }while(check seq)

2, 加写锁期间应该和自旋锁差不多, 和中断抢占用 xxirq_lock,

3, 写期间不可使指针失效,  否则读取时会奔溃

int i = 5;	
unsigned long flags;	
seqlock_t lock;
seqlock_init(&lock); //先初始化
int v;
unsigned start;
do {
    start = read_seqbegin(&lock); //读前先获得顺序值
    v = i;
} while (read_seqretry(&lock, start)); //读后检查锁是否变化了
write_seqlock_irqsave(&lock, flags);  //写前获得写锁
i++;
write_sequnlock_irqrestore(&lock, flags); //写后解锁

以上都有不可触发调度的特点
信号量 总结:
1, 可以被多个进程同时持有, 可以用来互斥. 此时信号量初值为1, 减法判断加锁, 0就不可以继续了

2, 得不到信号量则立即休眠, 会调度其他进程执行. 不会忙等, 切换不宜频繁

3, 因为得不到时会引起进程切换, 所以一般不可以用于中断上下文. 中断中只能使用down_trylock确实的得到锁, 一样使用up解锁.

4, 持有锁时可触发调度器, 但需注意死锁, 因该可以递归.

5, 信号量频繁切换开销比较大, 因为得不到一定会休眠, 当临界区不长, 应该使用自旋锁.

6, 没说不可递归, 没说上锁解锁需要同一个上下文?
读写信号量 总结:
1, 类似读写锁, 允许读锁之间的并发, 允许调度

2, 其他类似信号量

3, 只能有一个写锁

互斥量 总结:

1, 得不到锁不会马上休眠, 上锁和解锁要在同一个上下文

2, 如同自旋锁不可以递归加锁, 容易死锁

3, 持有互斥量时不能退出进程, 因为必须同一个上下文, 估计锁和继承id有关

4, 绝不能用于中断上下文, trylock也不行, 说明trylock也会造成调度, 可能是为了尽量得到锁, 会尝试短暂休眠

5, 持有锁期间, 允许切换进程, 所以是允许带锁休眠的, 注意进程间死锁

6, 是一种特殊的信号量, 互斥锁会休眠但不会马上休眠, 估计先用spinlock锁一会

7, 都不违反原则的情况下, 推荐 自旋锁(开销小, 不会睡) -> 互斥量(开销中, 可能睡) -> 信号量 (开销大, 一定睡)
RCU机制 总结:
1, 适合读多写少的共享, 可尽量减少锁的使用, 读没有锁开销, 不会死锁, 写开销较大

2, 更新会比较复杂, 需使用自旋锁隔离写队列临界区, 读取加锁使用rcu_read_lock函数

3, 读锁之间不排斥, 只是用来统计在读的进程和写互斥, 读进程全部解锁后就会立即处理写队列的请求, 写队列结束后就会解锁读

4, 也就是可休眠版的顺序锁, 同样写期间也是可以读的.

5, 和顺序锁的区别是, 顺序锁是读循环检查顺序值, rcu是写在队列等读空闲更新, 顺序锁期间不可触发调度, rcu读锁期间可以触发调度, 写spinlock期间不可调度

6, 提供了许多的库函数支持, 支持链表操作,  还需要进一步研究。一般是内核使用较多

sruct foo {
    int a;
    char b;
    long c;
};

DEFINE_SPINLOCK(foo_mutex);

struct foo *gbl_foo;

void foo_update_a(int new_a)
{

    struct foo *new_fp;
    struct foo *old_fp;

    new_fp = kmalloc(sizeof(*new_fp), GFP_KERNEL);
    spin_lock(&foo_mutex); //自旋锁
    old_fp = gbl_foo;
    *new_fp = *old_fp;     //复制
    new pf->a = new_a;
    rcu_assign_pointer(gbl_foo, new_fp); //写回队列替换指向, 复制的只是指针
    spin_unlock(&foo_mutex); //解锁
    synchronize(); //同步等待读全部结束
    kfree(old_fp); //释放旧资源
}

int foo_get_a(void)
{
    int retval;
    rcu_read_lock(); //读锁
    retval = rcu_dereference(pb1_foo)->a;
    rcu_read_unlock();//解锁
    retrun retval;
}
完成量 总结:
1, 用于操作的同步, 等待某个事件发生

linux driver 中断 和 调度----------------------

1. 任务: 计算机中的任务细分三类: 进程, 硬件中断 和 软中断

进程: 从用户空间开始调度, 执行系统调用是进入内核空间, 最后回到进程空间

硬件中断: 外设给CPU发送的中断信号, 内核执行注册的硬件中断处理函数, 此函数永远运行在内核空间, 且不可抢占不可休眠

软中断: 软件单独调用swi或svc指令立刻触发软中断异常, 立刻执行其软中断处理函数, 此函zhe数同样位于内核空间, 优先级低于硬中断


2. 任务优先级: 衡量任务获取CPU资源的能力

优先级越高, 获取CPU资源的能力越强, 越能够及早运行

在linux内核中, 三类任务的优先级划分:

硬件中断 > 软中断 > 进程

进程之间存在优先级之分(nice命令可以设置优先级)

软中断之间存在优先级, 中断不支持嵌套, 因为有了中断底半部的支持. 会影响一些极端的实时性. 实时性操作系统还是支持嵌套的如uc2-3

硬件中断无优先级之分(哪怕中断控制器支持优先级, 硬件优先级给裸机使用, 且中断不支持嵌套)


3. 进程调度: linux内核会给每个进程分配时间片, 进程一旦轮到其运行, 在此时间片之内可以一直运行

时间片到期, 进程调度器(软件,有很牛叉的算法)会将当前进程的CPU资源撤下给其他进程使用

切记: 中断不参与进程调度, 中断来了, 直接会抢夺CPU资源


4. 休眠sleep: 休眠只能用于进程, 不可用于中断, 中断没有休眠规则, 中断中执行调度会重启

进程休眠: 当前获取CPU资源的进程一旦要进入休眠状态, 此进程会立马释放占用的CPU资源, 此过程由cpu原生支持属于需移植项目


结论: 由以上概念得到: 在linux内核中, 内核希望中断处理函数的执行速度要越快越好

    如果中断处理函数长时间占用CPU资源, 势必影响系统的并发能力和响应能力(其他的硬件中断再也无法得到响应, 各种卡)

    问: 不可能所有的中断处理函数都能够满足内核的希望, 对此内核实现了napi机制可以了解下

           例如: 网卡利用中断获取网络数据包, 其中断处理函数本身执行的速度就很慢

           如果其他中断长时间占用CPU资源, 就会造成丢包现象!, 有些网卡用了napi机制

           napi: 适用于大量中断产生大量数据的场景, 此时可以只响应最初的中断, 然后屏蔽中断, 最后统一轮询快速处理, 也就减少频繁切换中断的消耗.



顶半部和底半部实现机制:

1.顶半部特点

    硬件中断触发, 立刻执行

    顶半部本质就是中断处理函数, 只是现在里面做紧急, 耗时较短的内容, 注意当今的linux中断是不可抢占的

    CPU在执行顶半部中断处理函数期间不允许发生CPU资源的切换

2.底半部特点

    底半部对应的处理函数做不紧急, 并且耗时较长的内容

    在执行期间允许高优先级的任务来抢夺CPU资源, 等处理完毕再回到底半部继续运行

    底半部实现方法三种: 软中断, tasklet, 工作队列

    底半部的本质就是延后执行的一种手段, 并不是非要和顶半部配合使用, 也就是可以单独用底半部来

    将你想要延后执行的事情进行延后, 可以将宝贵的CPU资源给其他高优先级的任务使用


底半部实现机制之软中断(了解即可)

    中断特点:

    1.软中断 同样有对应的延后处理函数,此函数可以同时运行在多个CPU核上,效率极其高

    属于中断上下文, 可以运行在多核上, 可能需处理同步问题, 软中断不能编译为模块, 软中断表和回调是定义在内核的, 有一个专门的调度器且基于优先级可以互相抢占,

    软中断的延后处理函数不能insmod和rmmod动态的安装和卸载, 必须和uImage编写在一起

    2.tasklet 基于软中断实现, 不能休眠, 但是它的延后处理函数同一时刻使能运行在一个CPU核上

    也不用关心互斥访问的问题, 也就是运行在一个核上的软中断. 同时调度时后面的会被忽略, 一个已经运行则下一个不会忽略

    3.工作队列 基于进程实现, 可触发调度, 一个工作未完成, 不能调度下一个, 也就是队列中的任务不会互相抢占的

    static struct work_struct btn_work;

    schedule_work(&btn_work);

    INIT_WORK(&btn_work, btn_work_function);

    DECLARE_WORK(vswork, vser_work); // 声明和初始化一步完成

    static void btn_work_function(struct work_struct *work) {}


6.总结:

    1, tasklet基于软中断实现, 工作队列基于进程实现

    2, 如果要进行延后执行, 并且延后执行的内容中有休眠操作, 只能用工作队列

    3, ..., 无休眠操作, 但又要考虑效率问题, 建议使用tasklet

    5, ..., 无休眠操作, 需要考录效率问题, 并且想让多中断抢占处理, 尽量提高实时性, 可以用软中断, 但不可编译为模块属于高级操作,  一般用于内核

7,共享中断

    1, request_irq(sh_irq, xxx_interrupt, SA_SHIRQ, "xxx", xxx_dev);

    2, int status = read_int_status();

       if(is_myint(dev_id, status))

linux driver I/O总结 -------------------------------

非阻塞I/O: 资源不可用时返回错误, 不适合大多时候资源不可用的设备, 如中断类设备, 没有特别操作只要返回错误 -EAGAIN , 可用flag O_NONBLOCK

阻塞I/O: 资源不可用时阻塞睡眠, 有资源时唤醒. 使用 wait_queue 等待队列

I/O多路复用: 多用多路阻塞模式poll, 全部不可用时就睡眠, 有资源可用时使用wake_up唤醒, 然后检查所有的资源, 有资源则会跳出循环返回到用户空间的poll继续执行, 没有资源则schedule继续休眠.适合同时监听, 也可以用多线程,多线程. 类似还有 select, poll, linux特有的 epoll

异步I/O: 1,异步IO是POSIX通用接口, 和内核驱动实现异步IO, 可以分散聚集操作.使用aio_read和aio_write 循环查看aio_error(&my_aiocb) == EINPROGRESS, 结束后 aio_return(&my_aiocb)

        2,也可使用aio_suspend(aiocblist, MAX_LIST, NULL))休眠等待,  

        3,aio_cancel(fd, aiocb)会返回操作是否成功, 操作所有的aiocb=NULL, 是要有一个失败就是AIO_NOTCANCELED

        4,lio_listio(mode, aiocblist, nent, sig) 批量操作

        5,也可以配合使用异步通知参考 详解 p185

        6,信号以外也可以传送回调函数给驱动 详解 p186

        7,只有字符设备需要用aio, 块和网络设备本来是异步的

        8,驱动须实现 aio_read, aio_write, aio_fsync? 详解 p187

异步通知: 异步通知可以向应用层发送信号, 需要使用F_SETSIG, gcc编译追加-D_GUN_SOURCE, 需要设置FASYNC标志位

mmap内存映射: 驱动需要实现ops.mmap注入, 适合文件和显卡类的块设备

定位操作: 随机访问设备需要实现ops.llseek注入,和wirte,read注入

linux driver timer_list 定时器 -------------------------------

// 声明

static struct timer_list temp_timer;

// 初始化

init_timer(&led_timer);

// 指定定时器的超时时间

led_timer.expires = jiffies + 2*HZ;

// 指定定时器的超时处理函数

led_timer.function = led_timer_function;

// 指定给超时处理函数传递的参数

led_timer.data = (unsigned long)&g_data;  

add_timer(&led_timer);

// 回调

static void led_timer_function(unsigned long data) {
   mod_timer(&led_timer, jiffies + 2*HZ);
}

//删除

del_timer(&led_timer);

TIMER_INITALIZER(_function, _expires, _data) //赋值结构体的快捷宏

DEFINE_TIMER(_name, _function, _expires, _data) //定义的初速化一体化宏

linux 延迟 ---------------------------------------------

1, ndelay(nsecs) 忙等纳秒

2, udelay(usecs) 忙等微妙

3, mdelay(msecs) 忙等毫秒, 最好少用

4, void msleep(unsigned int millisecs)  睡眠, 不可打断 无返回

5, unsigned ong msleep_inteuptible(unsigned int millisecs), 可打断, 返回剩余

6, void sleep(unsigned int seconds)  不可打断

7, 延迟: unsigned long delay = jiffies + 100; //忙等

        while(time_before(jiffies, delay));

8, time_after 和 time_before相反, 判断参数1是否>=参数2

time_before()

linux driver 内存和DMA -------------------------------------------

内存组织: 总结

1, 内存管理方式, 一种是UMA(一致内存访问, cpu共享内存) , 另一种 NUMA(非一致内存访问, cpu分别拥有内存), linux高层不做区分, UMA就是NUMA的一种特列, 本地内存不足时可从其他cpu获取, 就是慢点

2, 每一个内存划分位多个区 ZONE

   ZONE_DMA: 适用于DMA操作的内存区

   ZONE_DMA32: 64位系统中使用32位寻址的地址区, 低4G空间, 32位系统无此区域

   ZONE_NORMAL: 常规内存区, 通过简单的偏移可以的到虚拟地址的区域, 32位系统就是内核空间的低896MB里面去除ZONE_DMA的空间,当内存不够时会尝试从ZONE_DMA分配.

   ZONE_HIGHMEM: 32位系统就是1G内核空间的高128MB, 64位系统内核空间太大, 无需这段了

   ZONE_MOVABLE: 伪内存区域, 防止物理内存碎片时使用.

3, 每个区被分为页 MMU(4KB/1MB), 目前最常见的页大小是4KB, 使用的结构 struct page

4, MMU内存管理硬件: 1, 物理内存到虚拟内需映射 TLB 快表 TLW 漫游表

                  2, 内存访问权限保护, Cache缓存控制

5, mmu_init(): 初始用, 都是汇编

6, 1G内核空间 1, 物理内存映射区 一般是896M 3G 地址低

            2, 虚拟内存映射区            .

            3, 高端页面映射区            .

            4, 专用页面映射区            .

            5, 系统保留映射区            4G 高地址


按页分配内存: 总结

1, 内存分配掩码: gfp_t *p=alloc_pages(gfp_t) -> page_address(p) 非高端, kmap/kmap_atomic(p) 高端->非高端, kunmap(p) 解除

   GFP_ATOMIC: 以原子方式分配内存, 分配分配期间不可切换进程. 自旋锁和中断上下文需要这个掩码, 万一切换了就奔溃

   GFP_KERNEL: 最常用的分配掩码, 允许使用紧急情况的保留内存, 允许IO操作, 允许文件内存映射

   GFP_USER: 用户用户空间分配的掩码, 驱动应该不会用

   GFP_DMA: 只允许使用 ZONE_DMA 区分配内存

   __GPF_HIGHMEM: 只允许在高端内存区域分配, 没有则退回到 ZONE_NORMAL->ZONE_DMA



2, kmem_cache_create slab分配器: 从实现分配好的页内存中切分更小的单元, 类似内存池. 可以使用cat /proc/slabinfo | grep kmalloc 查看kmalloc/kzalloc可以使用的碎片, 会尽量使用匹配的碎片. 也可使用专门的函数, 但是kmalloc/kzalloc更加智能, 最常用

3, 不连续内存分配: 内存不够时可尝试, 性能一般.使用vmalloc/vzalloc vfree

4, 内存池: mempool_create,

5, 查看系统分配好的slab: cat /proc/slabinfo | grep kmalloc

per-CPU变量: 145

1, 就是每个cpu都有的一个变量副本, 典型的用法是统计各个cpu的信息.

I/O内存: 147

0, request_region() , release_region() 检查是否一杯映射, 不用也可以的, 就是检查下

0, request_mem_region() 内存申请

1, io内存主要用于映射物理寄存器. ioremap

2, io内粗需要使用request_mem_region申请虚拟内存区域. 如果上级驱动已经获得了则可以直接使用, 一般移植的时候就分配好了.

3, ioremap用来映射内存, 需要映射的内存其实是一个偏移量, 只要知道偏移直接换算也可以

4, 使用 ioreadx iowritex writeb writew wirtel readb ioreadx_rep memcpy_from....

5, 可以如同裸板程序操作寄存器, 一般为底层驱动使用. 唯一区别在使用的内存为虚拟内存地址.

6, 可多次分配,得到的虚拟地址相同.

7, /proc/meminfo 里可以查看创建好的io资源(request_mem_region)

DMA: 155

1, DMA用来代替cpu执行数据传输操作, 分为 内存到内存, 内存到外设, 外设到内粗, 外设到外设, 不一定支持所有种类

2, DMA需要没有缓存命中的内存, 一般不会太多, 普通ARM有3MB-16MB

一致性DMA映射: 157

1, 通常适用于整个驱动存在周期的映射

2, 这里都是从3MB-16MB专用空间取得, 没有一致性问题

流式DMA映射:

1, 使用上层提供的DMA指针, 不会长期占用, 开销比较小, 但是可能不适合DMA, ARM体系的可以通过设置cache无效(读方向)和写通(写方向)来实现, 可以使用普通内存, 最好尽快释放.


分散聚集DMA映射:

1, 就是一次性执行多个流式DMA映射

DMA池: 159

1, 分配一个较大的一致性缓冲区, 然后分配为小的DMA池

回弹缓冲区:

1, 当上传传递的地址不可用于DMA时, 需要进程复制->dma->复制, 建立一个中转站, dma的意义全无

总线,设备和驱动:

1, 设备和驱动都绑定了总线, 由总线判断是否匹配, 匹配一般依靠名称name属性

2, 一般情况下总线在系统进行了封装, 我们只需实现相应的xx_device, 和xx_driver, 如platform_device 和 platform_driver, 中间需要用name绑定, id来区分设备

3, 驱动和设备加载顺序无区别, 一种驱动只需加载一次即可, 设备可以多个分别加载

4, 不用xx_device和xx_driver也可以写字符驱动, 驱动框架可以注册系统回调. 不用框架则最多写module_init和module_exit, 直接写在内核里的需要注册在启动列表里

4, .remove = __devexit_p(mma8653_remove), 配置是否是模块, 不是模块就是NULL

5, 现在流行使用设备树实现xx_device部分, 手动的device部分只是实现了init和exit, 一般使用xx_add_devices注册, 注意exit时反注册和清理, 设备树只是一个配置无需写代码

6, 追加驱动参数:  err = sysfs_create_group(&data->input->dev.kobj,

            &mma8653_attribute_group); 参考 drivers 下 mma8653.c

7, 判断是否有设备树配置: if(pdev->dev.of_node)

设备树:193

1, 平台识别, mach-xx/mach-xx-dt.c 里面的 __initdata 数组配置了匹配项, 用来匹配设备树的根节点/ 下的 compatible 属性

2, 实时配置, 可以用啦配置启动参数, 大多数配置在/closen节点

3, 设备注入, setup_machine_fdt函数进行早期的设备树遍历, of_platform_populate函数进行节点的遍历和转换

4, 在arch/arm/boot/dts/xx.dts..xx.dtsi ->xx.dtb

5, / {} 是根节点

6, 字符串: xxx = "xxx";

7, cells: xxx = <1>;

8, 二进制: xxx = [0x01 0x02];

9, 混合:   xxx = "xxx", <2>, [0x01, 0x02];

10, 字符串列表: xxx = "xxx", "yyy";

11, 节点名称: <name>[@<地址>];   // <>必须 [可选] 如果相同名称地址不同 则例: 12c_0: 12c@13860000 {}, 12c_1: 12c@13870000 {}

12, 绑定: compatible = "arm,p1330", "arm,primecell";   // <制造商><型号> 可写多个作为兼容匹配

13, 地址信息: reg = <0x12680000 0x1000> , <0x12690000 0x1000>;// 地址和长度可多个, 数量配在父节点的 #address-cells = <2>;#size-cells = <1> 64位需要两个cell,基址,偏移和长度也是两个cell

14, 地址转换1: ranges = <0 0x802000 0x1000>; <子地址 父地址 范围> 然后子配置里写 reg = <0 0x1000> 等于 <0x802000 0x1000>

15, 地址转换2: ranges; //和父配置相同

16, 中断相关: gic: interrupt_controller@10490000 { interrupt_controller; .. // 一个空的属性说名这个时中断控制器

             子节点: #interrupt_calls = <3>;    // 类似地址 配置需3个cell

                    interrupt_parent = <&gic>;  // 配置控制器, 没有则继承父节点

                    子节点: interupts = <0 35 0> // 中断指示符列表, 35一般就是中断号, 其他的包含上升下降沿等配置, 具体要参考设备树说明文档 (Documentation/devicetree/bindings)

17, 别名: aliases {spi0 = &spi_0;}    // 这样就不用全路径, 配置在根节点下

18, 启动参数: chosen { bootargs = "console=tty$0,115200 loglevel=8";

                      initrd-start = <0xc8000000>;  //initrd 文件的起始地址

                      initrd-edn = <0x82000000>;

                    }

总线设备驱动:

1, 平台设备驱动: 专门给没有特定总线的设备, 多是不支持热插拔的

2, i2c总线驱动: 用于i2c总线设备, 二线总线, 总线上可以有多个主机, 主机发起请求, 速度100K - 5M, 地址多时7位, 也有10位的.

3, spi总线驱动: 4线总线设计, 速度可达到50M, 输入输出线独立, 独立的从机选择线, 只有一个主机, 有4种采样模式.

4, usb总线驱动: 嵌入式主要使用usb2.0, 主从结构的总线,

模式有:

        1, 控制传输: 用户命令和状态的操作

        2, 等时传输: 用于音频和视频, 可以容许一些错误, 实时要求高

        3, 中断传输: 主要用于键盘鼠标这种, 虽说是中断, 更像时间固定间隔的轮询.

        4, 块传输:   主要用于块设备, 比如优盘,硬盘

5, pci总线驱动: 速度块, 复制, 典型应用是显卡, 高速硬盘

poll使用总结 ----------------------------------------

1, poll不是一个文件使用的, 且多个设备文件使用一个驱动时要用次设备号区分

3, poll的前提是已经实现了阻塞等待队列 wait_queue_head_t 对象, poll_wait 时作为参数, 使用wake_up(&q)激活

4, 需要追加poll文件回调, 其中先用poll_wait追加等待队列, 可以多个. 然会检查资源转台返回mask

5, 返回的mask不可以随意定义, 要符合poll的规范, 一般就是POLLIN,POLLOUR和POLLNORMAL

6, 用户空间返回后, 需循环检查pollfd对象确认是否有资源可用, 一般就是&PULLIN,POLLOUT, 有资源时执行对应操作

驱动相同时可通过file找到次的设备号

struct inode *inode = file->f_path.dentry->d_inode;

//2.通过inode获取次设备号

int minor = MINOR(inode->i_rdev);

自动添加设备节点 --------------------------------------------------------------

1, /etc/init.d/rcS 脚本中追加 echo /sbin/mdev > /proc/sys/kernel/hotplug

linux App ------------------------------------------------------------------

打开设备: 统一使用open函数打开文件, 可以设定读写和权限, 取得文件描述符fd

读写设备: 使用设备文件描述符读取和写入设备, 描述符是一个int

内存映射: 将设备的地址空间映射为虚拟内存, 适合硬盘和显卡等块设备, 驱动中如果也有隐射, 地址将不同, 但是物理地址相同的

僵尸进程: 僵尸进程保存进程退出状态, 等待主进程使用wait回收. 太多了则会大量占用内存, 需要及时回收

waitpid(-1, NULL, WNOHANG) 非阻塞模式, 返回-1 需判断 errno == ECHILD 表示没有可回收

检查文件状态: 使用access函数, 查看某路径的文件状态. F_OK:存在检查 R_OK W_OK X_OK

复制文件描述符: 复制的文件描述符, 里面的操作都是共享的, 注意互斥

修改文件大小: truncate函数可以修改文件大小, 超出补0, 多用于mmap前的补齐.

文件锁: 支持局部加锁和解锁, 阻塞加锁和非阻塞加锁

fork子进程: 使用fork创建, fork返回0的是子进程, >0 的返回子进程号,供主进程查看

exec进程: exec重新打开进程. execve是底层通用函数

退出处理: on_exit(func, arg), atexit(func), 注册函数先进后出. 异常时不会处理

发送信号: kill可以发送信号, kill(pid, SIGKILL). pid:0:同组,-1:所有,< -1:组

注册信号: signal(SIGINT, SIG_IGN/func)!=SIG_ERR, 设置一次一直可用

系统调用: system("./vim 1.txt"), 返回状态为int s, WIFEXITED(s):是否正常 WEXITSTATUS(s):最后返回值

有名管道: 使用前需要mkfifo("./fifo", 0664), 使用后unlink("./fifo") 写满了会休眠, 读空的会休眠

无名管道: 使用pipe(pipefd), pipefd是个int数组, 用于父子进程通信, 不用的通道要尽快主动关闭

信号屏蔽: 信号分为和靠和不可靠信号, 可靠信号屏蔽也不会丢失, 主要使用不可靠信号. 原来的信号屏蔽可以备份

睡眠: 1 用户空间使用sleep按秒睡眠, 睡的越长越好. 有usleep 没有 msleep

         2, 内核空间使用

         mdelay(usecs) 忙等(<10us),  

         usleep_range(min,max) 微秒睡(10us-20ms),

         msleep(msecs) 微妙(>20ms),           

         msleep_interruptible(msecs)  // 可中断

共享内存: 需使用ftok(".", 100)合成键, shmget -> shmat -> shmdt

消息队列: 需使用ftok, msgget -> msgrcv -> 无需销毁

信号量: 用加减来控制资源, 没有就睡的机制

tcp: 需连接, 有校验 速度慢, socket -> bind -> listen -> accept:三次握手 -> read/write, 需要4次分手

udp: 无需连接, 无校验 速度快, socket -> bind -> recvfrom/sendto

线程: 线程需要的资源比进程小, 线程是共享资源的, 但是不共享堆栈. 线程最后需要join释放. 也可以设置为detach自动释放

     如果可能立即退出的detach线程需要在启动前设置, 互斥使用pthread_mutex_lock

描述i2c时序图 100k 24cxx ------------------------------------------------------------------------------

位序: 高到低, 地址序 低到高

start: sda:1 scl:1 5u sda:0 5u scl:0 先拉高数据和时钟, 保持5微妙, 先数据线0 等待 5u 时钟线0 延迟和速度有关 100k兼容最好, 时序要求是不小于 5u

stop:  scl:0 sda:0 5u scl:1 5u sda:1 5u 时钟先置1, 5微妙后数据置1为停止, 因为普通传数据都是先数据再时钟

ack:   scl:0 sda:0 5u scl:1 5u scl:0 正常响应, 数据线0,2微妙时钟线1保持5微妙

nak:   scl:0 sda:1 5u scl:1 5u scl:0 非正常响应, 数据线1,2微妙时钟线1保持5微妙

write: scl:0 sda:x 5u scl:1 5u scl:0...8次. sda:1 5u scl:1 1u ack scl:0  时钟线保持时间较短, 完成后要释放等待ack输入, 先高再低

read:  scl:0 5u scl:1 1u sda:x 5u scl:0...8次. 主动发 ack/nak  读取时拉高scl后1微妙就可以读取

write_block: 一般的i2c闪存芯片 跨区域写注意要重新start, 最后发送stop

write_block: 最后发送nack stop

描述spi时序图

位序:低位到高位

时序跨度: 普通1微妙

二种电平模式: CPOL:0 低电平空闲 CPOL:1 高电平空闲

二种采样模式: CPHA:0 第一个跳变 CPHA:1 第二个跳变

四线总线: 1:sclk 时钟

         2:mosi: 输出数据

         3:miso: 输入数据

         4:ss1-3: 片选, 低电平有效

其他: 有的芯片还有数据/命令 切换

主要链接: 小型OLED, 小型闪存, ADC数模转换, RTC时钟

描述一线时许图 ds18b20 温度 1-Wire --------------------------------------------------------------------------------

1, 链接简单就一根线, 但是时序较为复杂.使用主从模式

2, 对时许的要求较高, 需要锁, 时序要求高的都要锁

3, 时序: 1 检测存在脉冲, 主控拉低480-960us, 主控释放拉高, 等待15-60us, 接收芯片拉低60-240us, 从芯片拉低信号起码持续60us

      2 写入0时序, 主控60us内持续拉低则为写0

      3 写入1时序, 主控拉低1-15us, 然后拉高, 15-60us 芯片检测采样到高电平则1

      4 读取时序, 主控拉低1us, 释放 芯片拉低至少15us, 主控再2-15us之间读入0为0, 读入1为1.

4, 传输位低bit到高bit

描述uart时序图 -----------------------------------------------------------------------------

1, 使用波特率同步双方的信号, 双方速率相同

2, 有9个针脚, 目前主要使用RX,TX,GND 三个引脚 -3~-15表示1 反之表示0, 通过芯片转换就成了 UART

3, 三种通信类型, 单工, 半双工, 全双工

4, 晶振为何要是那个11.0592MHz就是为了提高除出整数的概率, 硬件会采样16次, 所以要多除以16

5, 空闲, 起始, 数据 , [校验], 停止 空闲 , 一般不做校验.

描述485总线, modbus ---------------------------------------------------------------------

1, 485总线是rs232的改良

2, 使用差分信号, 是一种半双工总线, 全双工需要两路 485 使用的较少, 最大速率10Mbps以上

3, 可挂载多个设备, 同时只有一个设备可以通信

4, 有RTU,ASCII,TCP三种通信方式, TCP模式是对帧的打包和解包, 去除了附加地址(tcp不再是总线)和校验追加报文头(tcp自己有校验)

5, 普通的modbus使用单向的菊花链网络, 各中心相互连接, 各中心又可以有子节点

描述can总线 ----------------------------------------------------------------------------------------

1, can总线是一种串行总线, 使用差分双绞线

2, 可以配置双方波特率的差微调参数, 可自动适配一定量的频率差异

3, 可检测损坏设备将其隔离在总线之外

4, 较长的传输距离和较大的设备容量

5, 协议较modbus复杂, 可靠性比modbus好

6, 是一种单向总线, 双向需要两路4线

7, 最大速率1Mbps

8, CAN采用的是非破坏性总线仲裁技术, 按优先级发送, 使用id位的线性和隐形来区分总裁, 当出现显性隐形差异时, 显性的优先级高, 因为总线这时也是显性, 谁和总线最早出现差异谁就退出

9, 125kbps~1mbps的高速can通信标准ISO11898, 闭环总线(差分端点不连接, 2.2千欧), 长度<40M,使用于发送机变速箱等要求试试性的场合

10, 10~125kbps的低速can通信标准ISO11519, 开环总线(端点连接 120欧), 40kbps时, 总线长度可达1000米, 断掉任一线路时还可以继续接收数据

11, 模式: 1, 正常模式: 可以发送接收

         2, 静默模式: 可发送1(隐性位), 不可发送0(显性位)

         3, 回环模式: 发送直接到输入, 总线可检测, 用于测试总线软硬件. 也就是通过总线自己收发

         4, 回环静默模式: 发送直接到输入, 不能接收总线数据, 也就是自己发自己收, 不走总线

12, 双线里断掉一根也还可以尝试运行

网络拓扑 ------------------------------------------------------------------------------------------------

1, 总线型: 所有设备并联在一起发起信号, 主干瘫痪全部瘫痪

2, 环形: 信息发送时单向的, 需要中继器, 效率低

3, 星型: 一个中央节点和许多子节点, 所有的数据都要通向中央节点, 中心节点负担较重

4, 树形: 越高的节点可靠性越强, 计算机网络

5, 网状: 最复杂的网络形式, 成本大, 可靠性好.

描述红外NEC协议 --------------------------------------------------------------------------------------

1, 通常使用38k的载波信号

2, 调制后高电平不变表示空闲, 低电平转为时载波, 用户/数据 0:560us载波+560us空闲 1:560us载波+1.68ms空闲

3, 帧结构: 引导码(9ms载波+4.5ms空闲), 用户码(8bit), 用户反码(8bit), 键数据码(8bit), 键数据反码(8bit) 停止码忽略

4, 低bit在前高bit在后传送

描述mqtt协议 -----------------------------------------------------------------------------------------------

1, Message Queuing Telemetry Transport 一种基于TCP/IP的轻量通信协议

2, 登录阿里云物联网, 注册开通工程, 取得三元组, 三元组经过加密转换后放置再报文里一起发送

3, 报文: CONNECT     链接服务器  固定0x10开始 + 剩余长度(只需1字节) + 可变报文头(配置字节, 长度固定) + 有效载荷 + clientid长度(高字节前)+clientid信息 + username长度+username信息 + passwd长度+passwd信

    CONNACK      服务器确认 90 03固定 +  00 0A 可变  + 01 有效载荷固定

    PUBLISH  发布消息  30 + 剩余报文长度(位数不固定算法) + 0030(主题名长度) + 主题名 + 有效载荷json格式的状态数据(无需长度) 来回的格式差不多

    PUBACK   响应 40 02 + publish 报文标识符

    SUBSCRIBE 订阅消息 0x8235固定 + 000A 可变报头 + 0030(主题长度) + 主题链接名 + QOS质量(00)      

    SUBACK    订阅确认 0x9003固定 + 000A   01

    UNSUBSCRIBE 取消订阅  0xA234固定 +  000A 可变报头 + 0030(主题长度)  + 主题的名字

    UNSUBACK    响应   0x0B0A固定 + 000A

    PINGREQ   心跳    C0 00      

    PINGRESP  响应

4, 使用网络调试助手调试

描述几种滤波算法

描述pwm波

1, pwm波是一种方波, pwm波最好是偶数分频

2, pwm波连续变动时寄存器不可重启, 否则会抖动

3, linux里寄存器调教的pwm波比, 官方封装的config精确

举例子画出一个五级指令流水线: 参见PPT, P24,P25  

F - Fetch     取指  

D - Decode    解码  

E - Execute   执行  

M - Memory    访存(只有ldr指令)  

W - Writeback 写回

画出37个arm寄存器在七种工作模式下的分布图

    SVC 管理模式        spsr r13(sp) r14(lr)     系统复位或者代码调用swi/svc指令

    FIQ 快速中断模式     spsr r13(sp) r14(lr)     r8-r12 外设给CPU核发送FIQ中断电信号,  优先级更高, 拥有自己的banked寄存器会自动备份r8-r12寄存器,  地址靠后可以直接执行少跳转一次

    IRQ 中断模式        spsr r13(sp) r14(lr)     外设给CPU核发送IRQ中断电信号

    Abort 中止模式      spsr r13(sp) r14(lr)     取指F取指失败(指令没有)或者M访存失败(地址无权限)

    Undef 未定义指令模式 spsr r13(sp) r14(lr)  CPU处理一个不认识的指令, 例如: lisi

    User 用户模式                   一般应用程序正常运行时, CPU就是处于User用户模式

    System 系统模式                 中断如果有子程序会写入返回地址到pc, 如果再次来了同样的中断会覆盖掉pc(这里无法干预), 可以事先进入用户模式避免这个问题(为何呢), 但会造成权限不足问题,  因此出现了system模式,  system和user共用寄存器

ARM寄存器

    37个

    每个寄存器大小: 32位, 4字节

    6个程序状态寄存器 cpsr 1 个 user system 共用, spsr 5 个异常独有, 备份cpsr

    31个通用寄存器的名称: r0,r1,r2...r15, r15=pc,r14=lr,r13=sp 快速中断的r8-r12 5个异常模式的 r13,r14 (16+5+2+2+2+2+2=31)

    不区分大小写

    其中:

    特殊3个:

    r13又称sp: 永远只能保存栈指针

    r14又称lr: 永远只能保存返回地址(bl指令和中断会自动处理)

    r15又称pc: 永远只能存储取指器要取的那条指令的内存存储地址

    通用数据13个:r0,r1...r12:剩余寄存器随意存储数据

    6个程序状态寄存器又分:

    1个cpsr: 保存当前程序运行的状态信息, user和system公用

    5个spsr: 备份cpsr的值,保存cpsr, 分别是:spsr_svc,spsr_undef,spsr_irq,spsr_fiq,spsr_abort, 对应各种状态

    面试时: 务必画出37个寄存器在七种工作模式下的分布图!

cpsr的各位作用32位

    bit[4:0]=模式位,用于记录当前程序在运行时对应的CPU核的工作模式

        例如: 运行时,bit[4:0]=10000,此时CPU核的工作模式就是用户模式

        系统复位, bit[4:0]=10011,此时CPU核的工作模式就是SVC管理模式

        外设发送IRQ中断信号,bit[4:0]=10010,此时CPU核的工作模式就是IRQ模式

    bit[5]=状态位  

        0: 表示当前程序运行时对应的CPU核状态是ARM状态

        1: 表示当前程序运行时对应的CPU核状态是Thumb状态

    bit[6]=F位

        0: 允许CPU核响应处理FIQ中断

        1: 禁止CPU核响应处理FIQ中断

    bit[7]=I位

        0: 允许CPU核响应处理IRQ中断

        1: 禁止CPU核响应处理IRQ中断

    bit[28]=V位: 表示当前程序运算结果是否有溢出现象

        0: 没有溢出

        1: 有溢出

    bit[29]=C位: 表示当前程序运算是是否发生过进位或者借位

        加法运算产生进位

        0: 没有发生

        1: 发生过

        减法运算产生借位:

        0: 发生

        1: 没有发生

    bit[30]=Z位: 表示当前程序运算的结果是否是0

        0: 运算结果不为0, 表示不相等

        1: 运算结果为0, 表示相等

    bit[31]=N位, 表示当前程序运算的结果是否是负数或者小于

        0: 不为负数或者大于等于

        1: 运算结果是负数或者小于

中断恢复过程, 中断向量和状态,栈保存和恢复

stm32 的中断 ------------------------------------------------------------------------------------

1, 复位中断:    -3 不可屏蔽

2, NMI:       -2 外部不可屏蔽中断, NMI引脚

3, hardfault: -1 硬件错误

4, 其他:      0 - 255 可编程中断

5, 优先级 越小越大

6, AIRCR->PRIGROUP(10:8) 优先级分组 0 - 7 0: 全抢占 7: 全子级

7, 中断屏蔽: 1, PRIMASK 禁止除了NMI和HardFault以外的全部

            2, FAULTMASK NMI 复位 以外的全部

            3, BASEPRI 关闭除了 负数的以外的全部, 且可以设置屏蔽等级: 0:不屏蔽, 1: >= 1的屏蔽, freeRTOS主要用这个

stm32的gpio接口模式 ----------------------------------------------------------------

输入: 浮空输入 输入电压由外部确定, 否则不确定

     上拉输入 默认上拉, 电压状态稳定

     下拉输入 默认下拉

     模拟输入 不接上下拉, 输入到adc

输出: 开漏输出 断开或者接通底线, 高电平需要上拉电阻

     复用开漏 用作其他功能而不是cpu控制开漏输出

     推挽输出 无需上拉的电阻即可输出高电平, 高低电平电流方向不同

     复用推挽 用作其他功能而不是cpu控制推挽输出

通过file找到次的设备号, 一般时一个驱动有多个设备, 取得子设备号就可以得到每个设备的私有数据

struct inode *inode = file->f_path.dentry->d_inode;

//2.通过inode获取次设备号

int minor = MINOR(inode->i_rdev);

ARM 汇编 --------------------------------------------------------------------------

1, ram处理器是risc

2, 用户可访问寄存器 17个

3, 开机处于svc模式

4, R15 是pc , R14 是链接lr R13 是 栈指针sp , r4-13 局部变量, r0-3 参数和返回 cpsr 状态寄存器

5, 指令集32位

模式:

User : 非特权模式,大部分任务执行在这种模式

System : 使用和User模式相同寄存器集的特权模式

FIQ : 当一个高优先级(fast) 中断产生时将会进入这种模式

IRQ : 当一个低优先级(normal) 中断产生时将会进入这种模式

Supervisor :当复位或软中断指令执行时将会进入这种模式

Abort : 当存取异常时将会进入这种模式

Undef : 当执行未定义指令时会进入这种模式

异常: 正常工作之外的流程都是异常。如: 让正常播放视频的播放器快进,就是中断。

1, FIQ、IRQ、SVS本质上就是中断, 中断是异常的一种, 但属于正常且可控的异常。 Abort、Undef属于真正的异常, 导致CPU无法正常的工作。

2, 中断类似于异步: 属于响应式的工作方式,而非根据同步时钟工作的同步工作方式, 异 步就是通过中断来实现。

异常向量表的含义及作用:

1, 异常向量表: 是CPU硬件已经设定好的, 专门用来处理异常,类似于向量组成的表格, 只有汇编可以复制

ARM的异常处理机制:

CPU在发现异常后,具体是如何来跳转到异常向量表并处理异常的流程?

1, 保护现场

    1>> CPSR(程序状态寄存器)复制到SPSR,

    2>> 修改CPSR: 1>Thumb到ARM指令集—>ARM指令集(Thumb状态下无法处理异常) 2>用户到系统模式—>相应的异常模式 3>中断禁止(如果需要的话)

    3>> PC->LR

    4>> 异常向量->PC

    5>> 运行异常

    6>> SPSR——>CPSR

    7>> LR——>PC

linux下的程序分段 -----------------------------------------------------------------------

32位每个进程4G虚拟空间

高地址: 命令行和环境变量 env

高地址: 栈 stack

中地址: 堆 Heap

低地址: 未初始化的数据段 .bss

低地址: 初始化的数据段 .data

低地址: 代码段 .text

驱动错误处理: 启动后 挂载 mount -t debugfs none /sys/kernel/debug

dev_info(): 启动过程、或者模块加载过程等“通知类的”信息等, 一般只会通知一次, 例如probe函数

dev_err(&chip->spi_dev->dev, "SPI read error\n"); 一般使用在严重错误, 会直接输出, 尤其是用户无法得到errno的地方

dev_dbg(): 一般使用在普通错误, 如-EINVAL、-ENOMEM等errno发生处, 用于调试, 输出要用一定方法

make menuconfig选中CONFIG_DYNAMIC_DEBUG以及CONFIG_DEBUG_FS

echo -n 'module overlay +p' > /sys/kernel/debug/dynamic_debug/control

echo -n 'file pwm.c +p' > /sys/kernel/debug/dynamic_debug/control

echo -n 'file nxp-fb.c +p' > /sys/kernel/debug/dynamic_debug/control

驱动私有数据:

struct inode *inode = file->f_path.dentry->d_inode; //从file得到inode, 弄得到私有数据, MINOR(inode->i_rdev) 得到次设备号

int minor = MINOR(inode->i_rdev);

ch368 = container_of(inode->i_cdev, struct ch368_dev, cdev);  //将cdev放在自己定义的驱动数据里可以这样取回, 只要有inode, 适合多设备

filp->private_data = fsadc //file结构里有个privte_data可以藏东西, 一般根据设备号选择, 可以一驱动支持多个硬件设备, 不过要给每个设备一个名字才能区分

dev_set_drvdata(&spi_dev->dev, chip); //设置到私有对象, 似乎是有人封装的, 有device的地方就可以拿到, 比如初始化和卸载

platform_set_drvdata(pdev, fsadc); // 同上, 只不过用的是pdev, pdev下面有device对象, 多是卸载时用get取得

board_info_t *db = netdev_priv(dev) // 各种驱动框架里也会有, 这个得研究下

描述fb

1, drivers/video/fbmem.c 使用subsys_initcall加载, 也可以用module_init加载, 前者再内核里, 后者在模块里, 是framebuffer的操作系统接口层, 提供注册函数和通用库函数

2, deivers/video/nxp-fb.c 芯片厂商提供的驱动实现, 这里参数最后调用 fbmem register_framebuffer

3, mach-s5p6818/soc/display.c nxp-fb的通用库函数模块, 各种显示接口的通用接口

4, mach-s5p6818/soc/display_lcd.c nxp-fb的lcd实现驱动, 使用diaplay库函数注册自己. 其他还有许多接口如html

5, info->dev是设备对象register_framebuffer的时候初始化, info->device是platform_device里的dev, 作为parent

c++ 03 --------------------------------------------------------------------------

1, explicit: 禁止构造函数的隐式转换, 构造函数有时无意间会被隐式转换匹配上, 可能需要禁止

c++ 11 主要新特性 ---------------------------------------------------------- gcc 4.8+

1, auto关键字: 类似var的动态类型推断, 注意老版本里面也有个auto已经废弃, 原来的auto表示作用域自动的变量

2, decltype: auto的高级版, 可以绑定另外一个变量推断类型

3, nullptr: 用来代替NULL

4, constexpr: 常量表达式声明, 用来定义值不会变化的, 在编译阶段就可以计算出值的表达式. 也就是告诉编译器这个定义的对象将不会改变. 如 constexpr int limit = 2 + x(也是个const)和, constexpr int *q 定义常量指针, 指向不可改. 同样必须指向静态区的变量

5, range for: for(auto c: ivec) {}

6, Lambda: [capture list] (parameter list) specifiers exception -> type { function body }

            1, [capture list] 捕获列表

            2, (parameter list) 参数列表

            3, specifiers 限定符 如 mutable:可以在函数体内修改复制的变量

            4, exception 异常说明符, 如noexcept 不可抛异常

            5, type 返回类型 ,可指定返回类型, 否则编译器推断. 也就是指定强转

            6, { function body } 表达式的函数体, 没区别

            auto f = [](int x, int y){ return x + y; };

            int result = f(10, 20);    //result == 30

            auto f1 = [a, b]{ return a + b; }; // a, b是外部的变量的复制

            auto f1 = [&a, &b]{ return a + b; }; // & 表示是引用

            auto f1 = [=]{ return a + b + c; }; // = 表示全部

            auto f = [=,&c]{ c++; return a + b; }; // 混合

            int result1 = f1();    //result1 == 30

总结: 方便的创建匿名函数, 比较灵活, 会牺牲点性能.

7, initializer_list标准库对象: 基本是一个列表, 有ptr begin(), ptr end(), int size(), 里面都是常量, 拷贝也其实都是引用, 简化了复杂对象的初始化方式如标准库的 std::vector v = { 1, 2, 3, 4 }, 也可自定义自己的 如 MyNumber(const std::initializer_list<int> &v)

8, bind库函数: 类似适配器, 将函数和参数绑定在一起变成新的对象, 而不需要重复的写一样的代码. 也就是整合零散参数的新方法, 不用被零散参数建立结构体. 支持泛型, 支持动态值
9, 智能指针shared_ptr: 一种指针的管理回收机制, 可共享指针, 提供一些方法控制指针. 可拷贝, 拷贝会引用增加计数.释放到0则销毁内存. 使用make_sheared<t>(val)创建, sheared_ptr<t>p(p0)复制, 也可shared_ptr<int> p(new int(42)) 创建, sheared_ptr<t>p(u) 接管unique_ptr

9, 智能指针unique_ptr: 一种自动回收机制, 不可共享, 唯一的指向. 释放0则销毁内存, 可转为共享. 只能移动不能复制

10, 右值引用: 记为&&, 起初const 可以指向右值, 一般不可指向的, 右值一般是将要销毁的零时的变量, 右值引用是为了引用右值, 如函数返回值默认是不可引用的, Test && t = getObj(); 这样就可以引用了,可避免右值复制.

C++14 ---------------------------------------------------------------------------- gcc 6.4

1, 变量模板: template<class T>

         constexpr T pi = T(3.1415926535897932385L);

2, lambda: 支持泛型

           支持初始化捕获, 移动捕获

           std::unique_ptr<Item> item(new Item());

           auto func = [m = std::move(item)] { /* do something */ }; move 转换为右值, 改变原值

3, constexpr限制放宽, 更加灵活

           1, 修饰function时可以有多个返回了, 对if和循环的支持更好了, 但同样需要编译期间能计算出全部内容

4, 数字分隔符

            int val1 = 100000000;

            int val2 = 100'000'000;

5, 函数返回值可以用 auto 自动推导, 不支持虚函数

6, [[deprecated]]标记, 也就是标记废弃警告

7, 新增std::make_unique

8, 新增读写锁std::shared_timed_mutex与std::shared_lock 离开作用域自动解锁 17里继续完善

9, std::exchange: #include <utility> , 它的作用是把第二个值赋值给第一个值, 同时返回第一个值的旧值, 不会改变第二个值

10, std::quoted, 也就是自定义引号的标记, escape \ 和 delim "  stringstream ss << std::quoted(in, delim, escape)";

C++17 ------------------------------------------------------------------------------- gcc 7+

1, 结构化绑定: 类似java里的绑定操作, auto [u, v] = ms; 复制绑定 auto& [u, v] = ms; 引用绑定, 可绑定结构体,数组,类,tuple

2, 初始化if语句: if (status s = check(); s == status::success) 就是可以在if里初始化了, 好处是作用域在if else里面

3, 初始化的switch语句: switch(Color color = GetSelectedColor(); color) { 同上

4, 内联变量: 同通过inline 关键字可以在类的定义中初始化的非 const 静态变量 inline static std::string msg{"OK"};  如果是全局变量则可以多次include ?

5, 聚合体扩展: 1: 聚合体指数组或者C 风格的简单类结构体, C 风格的简单的类要求没有用户定义的构造函数、没有私有或保护的非静态数据成员、没有虚函数

             2: 另外, 在 C++17 之前, 还要求没有基类

             3: Data x = {"test1", 6.778}; 或者 11以后 Data x {"test1", 6.778};

             4: MoreData y{{"test1", 6.778}, false}; {"test1", 6.778} 初始化基类, 参数全可以省略括号, 也可定义构造函数去掉括号

6, lambda 表达式扩展: 1: 编译器将尽可能使用constexpr修饰, 不行会去掉, 可提高性能, 也可强制写 [](auto val) constexpr, 这样可以看到错误

                    2: [*this] {std::cout << name} 类中用的17开始可以加*复制this指向, name这里时复制的, 以前只能复制指针, [=][&]也可以

7, [[fallthrough]] 属性: case 后面用, 强制这个分支一定要写break; 最后一个分支不可以使用

8, [[nodiscard]] 属性: 函数声明前用, 警告某个默认函数的返回值没有使用

9, [[maybe_unused]] 属性: 变量定义前, 某个变量没有使用不告警, 也就是可能不使用, 比如预处理跳过

10, 嵌套命名空间: 也就是嵌套的namespace简写 namespace A::B::C {}

11, UTF-8 字符字面量: 可以这样定义了 char c = u8'6'; 11 开始就可以用于字符串了. u8:utf8, u:utf16, U:utf32, L:两个或者四个字节的宽字符集

12, 单参数 static_assert: static_assert(std::is_default_constructible_v<T>); 可以单参数了, 从 11 开始有 那时候必须双参数, 现在少写了::value,"xxx", 感觉就是xxx的描述没啥用懒得写了

C++20 --------------------------------------------------------------------------- gcc 8+

1, concept: 泛型约束用, 如: template<typename T>

                          concept number = std::is_arithmetic<T>::value; // 定义数值类型的约束 number

                          template<number T> //使用此约束

2, requires: 配合concept可以细化约束: template<typename T>                  

                                concept cust_constraint = requires(T t) // 细化配置 T 类型

                                {

                                    std::is_class<T>::value; // 限定是一个类型

                                    std::is_same<decltype(t.action()), int>::value; // 限定T类型的action()函数的返回值为int类型

                                    t();     // 限定T有个无参的构造函数

                                    t.action(); // 限定T具有一个action()成员函数

                                }

3, explicit: 03 追加的, 本次可以加参数false关闭 explicit(false)

4, constexpr: 11 追加的, 本次追加虚函数支持, 禁止constexpr函数使用try-catch

5, consteval: 用于表示函数是立即数, 只用于函数, 编译期间计算, 比constexpr严格, constexpr是混合的, consteval是纯的编译期间

   constinit: 显式地指定变量的初始化方式为静态的

6, char8_t: utf-8字面量的专门类型

7, co_await、co_yield、co_return: 携程功能, 编译选择项 -fcoroutines, 单线程无需堆栈, 线程切换自己管理, 无需切换到内核态, 开销小, 无需加锁, 需要判断数据状态. 多线程+协程

                                1: 一种轻量级的多线程处理方式, 使用 co_await 挂起

                                2: co_await expression; expression 是可等待对象 例如std::future, std::experimental::future

8, constinit: 强制初始化不可以有动态的内容, 可配合constexpr ,必须是全局的和static或者是 线程存储持续时间的?,  thread_local可不初始化

9, 位域语法扩展: 位域变量在声明时可进行初始化 int x5 : (true ? 10 : b) = 20

10, 修改const限定的成员指针:struct S { void foo() const& { } };  (S{}.*&S::foo)();

11, 允许lambda表达值按值捕获this: auto f = [=, this]() {this->value++;}; // = 表示值捕获和所有捕获, 非隐式么?

12, 指定初始化: 顺序必须与成员的内存顺序一致{.x = 1, .y = 2}, 也就是聚合初始化的值可以指定了

13, 专门的访问检查: 访问检查更加精确好用?

14, lambda在初始化捕获时进行包扩展: return [f, args...]() -> decltype(auto) { std::invoke(f, args...);} 等等 class...?

15, 放宽结构化绑定, 新增自定义查找规则: 可以自定义绑定的第几个是哪个类型, 而且可以指定解绑的个数

16, for循环, 新增自定义范围方法: 可自定义begin()和end()的值

17, 可以用类类型作为模板的参数了, 1, 本来只支持非类:Equal<a, b>::value a, b 可以是类类型了 , 前提是结合constexpr

18,                          2, 字符串类型本来要提供长度, 现在直接编译期间计算了

19, 内联namespce 改为 namespace A::inline C 原来是 namespace A {inline namespace C {}}

20, 约束声明的另一种办法:

                    template<typename T>

                    concept CanCompare = requires(T t){

                        t * t;  // T类型需要提供*运算符

                        Compare().operator()(T(), T()); // 根据Compare结果体, 需要T类型提供 < 运算符?

                    };

                   

                    // concept与auto的结合

                    CanCompare auto pow2(CanCompare auto x)

                    {

                        CanCompare auto y = x * x;

                        return y;

                    }

21, 允许在常量表达式中使用dynamic_cast多态typeid:

22, 允许用圆括弧的值进行聚合初始化: 就是相当于默认有一个有全部非静态数据成员的构造函数

23, new表达式的数组元素个数的推导: new double[]{1,2,3} new double[]{}

24, unicode字符串字面量: std::u16string str1 = u"aaaaaa" ;std::u32string str2 = U"bbbbbb";

25, 允许转换成未知边界的数组: func(T (&arr)[]) func(T (&&arr)[]) func<double>({1.0, 2, 3, 4, 8.0});

26, 聚合初始化推导类模板参数: 也就是聚合里模板参数可以不写了

27, 隐式地将返回的本地变量转换为右值引用: 希望没副作用

28, 允许default修饰运算符按值比较: friend bool operator==(C, C) = default;

29, 新增的delete运算符函数: void operator delete(type *, std::destroying_delete_t); delete时会执行这个, 而不是默认的释放 gcc9

30, likely和unlikely: linux里常见 gcc9

31, [[no_unique_address]] : 同类型的子对象或成员不占用同一个地址, 该属性对空类型(没有非静态数据成员)有效。....

32, [[nodiscard("asdfasfa")]]: 提示返回类型没有使用

33, lambda弃用使用[=]来隐式捕获this

34, 比较运算符的改进: 弃用枚举的隐式算术转换, 数组的比较 arr1 == arr2

35, 弃用下标表达式中的逗号操作符, 只保留最后一个有效, 这个特性不变.

freeRTOS 的移植 ---------------------------------------------------------------------------------------

1, 复制Demo文件里的对应FreeRTOSConfig.h 修改裁剪, 可参考FreeRTOS.h 那里有默认值, 不配就会用默认值

2, 选择heap1.c - heap5.c 管理内存分配 一般选4 或者 2 老版没有合并功能,

                                                heap_1 —— 最简单, 不允许释放内存。不太有用, 因为 FreeRTOS 添加了静态分配支持

                                                heap_2 —— 允许释放内存, 但不会合并相邻的空闲块, 稍小。heap_2 现在被视为旧版, 因为 heap_4 是首选。

                                                heap_3 —— 简单包装了标准 malloc() 和 free(), 以保证线程安全。这为标准 C 库 malloc() 和 free() 函数实现了简单的包装器,  在大多数情况下, 将与您选择的编译器一起提供。 该 包装器只是使 malloc() 和 free() 函数线程安全。

                                                heap_4 —— 合并相邻的空闲块以避免碎片化。 包含绝对地址放置选项。此方案使用第一适应算法, 并且与方案 2 不同,  它确实将相邻的空闲内存块组成单个大内存块(它确实包含合并算法) 。

                                                heap_5 —— 如同 heap_4, 能够跨越多个不相邻内存区域的堆, 比如内部ram + 外部ram。此方案使用与 heap_4 相同的第一拟合和内存合并算法, 允许堆跨越多个不相邻(非连续) 内存区域(如多个内存芯片), 最大

3, 复制源文件到项目, 复制对应的开发板 port(portable/RVDS) 到项目

4, 追加中断处理, 建议配置#define替换原来的处理函数, 需要删除原来的定义, 也可以直接修改启动文件(不推荐), 或者修改OS源码(不推荐)

              #define xPortPendSVHandler    PendSV_Handler 任务切换相关中断

              #define vPortSVCHandler   SVC_Handler 启动中断

              #define xPortSysTickHandler SysTick_Handler 时钟中断, 也可追加 if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED) xPortSysTickHandler();, 任务启动后再触发

5, 堆的位置要先初始化, 可以静态也可以动态的, 再vTaskStartScheduler之前即可

freeRtos 任务创建 ----------------------------------------------------------------

  TaskHandle_t StartTask_Handler;

1, 动态的:   xTaskCreate((TaskFunction_t )start_task,           //任务函数  同台分配堆栈, 任务结束释放内存

                        (const char*    )"start_task",          //任务名称

                        (uint16_t       )START_STK_SIZE,        //任务堆栈大小

                        (void*          )NULL,                  //传递给任务函数的参数

                        (UBaseType_t    )START_TASK_PRIO,       //任务优先级

                        (TaskHandle_t*  )&StartTask_Handler);   //任务句柄  本来就是指针  动态独有          

            vTaskStartScheduler();                  //开启任务调度

            第一个Task不用加锁, 子Task创建要加锁  taskENTER_CRITICAL(); xTaskCreate... vTaskDelete(StartTask_Handler); // 不用可以删掉起始任务 taskEXIT_CRITICAL();

2, 静态的:   StartTask_Handler=xTaskCreateStatic((TaskFunction_t)start_task, //任务函数 直接返回句柄,  任务分配堆栈是静态的速度快, 适合生命周期任务

                        (const char*    )"start_task",      //任务名称

                        (uint32_t       )START_STK_SIZE,    //任务堆栈大小

                        (void*          )NULL,              //传递给任务函数的参数

                        (UBaseType_t    )START_TASK_PRIO,   //任务优先级

                        (StackType_t*   )StartTaskStack,    //任务堆栈  静态独有

                        (StaticTask_t*  )&StartTaskTCB);    //任务控制块  静态独有      

            vTaskStartScheduler();          //开启任务调度

            第一个Task不用加锁, 子Task创建要加锁  taskENTER_CRITICAL(); xTaskCreate... vTaskDelete(StartTask_Handler); // 不用可以删掉起始任务 taskEXIT_CRITICAL();

            // 需要开启 configSUPPORT_STATIC_ALLOCATION 实现 vApplicationGetIdleTaskMemory vApplicationGetTimerTaskMemory

3, 任务要持续执行必须是死循环, 且无返回值

4, 任务要及时删除

freeRtos 的竞太处理 ----------------------------------------------------------

1, portDISABLE_INTERRUPTS()

2, portENABLE_INTERRUPTS()

1, portTICK_TYPE_ENTER_CRITICAL()=portDISABLE_INTERRUPTS()=vPortRaiseBASEPRI()  关中断 >=0 的,  portTICK_TYPE_IS_ATOMIC 时使用

2, portTICK_TYPE_EXIT_CRITICAL()=portENABLE_INTERRUPTS()=vPortSetBASEPRI(0) // 开中断 >= 0 的   portTICK_TYPE_IS_ATOMIC 时使用

3, portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR()=portSET_INTERRUPT_MASK_FROM_ISR();              portTICK_TYPE_IS_ATOMIC 时使用

4, portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( x )=portCLEAR_INTERRUPT_MASK_FROM_ISR( ( x ) ) portTICK_TYPE_IS_ATOMIC 时使用

5, 按优先级屏蔽: vPortSetBASEPRI(U32 ulBASEPRI); // 设置中断屏蔽位 >= ulBASEPRI 屏蔽

3, 可编程中断的全部打开: vPortSetBASEPRI(0); //开全部中断  

4  可编程中断全部关闭: vPortRaiseBASEPRI(); //关闭中断 >= configMAX_SYSCALL_INTERRUPT_PRIORITY

3, 调度器和中断屏蔽: taskENTER_CRITICAL()=portENTER_CRITICAL()=vPortEnterCritical();  // 最后调用vPortRaiseBASEPRI(), // 可嵌套 默认屏蔽了 >=5 的中断, 5:PendSV 6:SysTick 调度器也停了

4, 调度器和中断全开启: taskEXIT_CRITICAL()=portEXIT_CRITICAL()=vPortExitCritical();   // 最后调用vPortSetBASEPRI(0)   // 可嵌套

4, 中断里调用关调度: taskENTER_CRITICAL_FROM_ISR()=portSET_INTERRUPT_MASK_FROM_ISR() // 最后使用 ulPortRaiseBASEPRI();  //可返回当前的中断屏蔽起始, 再已经调用了部分中断屏蔽后, 高优先级的中断一定要抢占要用这个, 用户恢复上一级的操作

6, 中断里调用开调度: taskEXIT_CRITICAL_FROM_ISR(x)=portCLEAR_INTERRUPT_MASK_FROM_ISR(x) // 最后使用 vPortSetBASEPRI(x); //打开时可设置中断屏蔽起始, 中断里用这两个比较安全

7, 关闭中断和开启中断之间的延时不能调用 vTaskDelay(), 看来时休眠来延迟的

freeRTOS 任务管理 -------------------------------------------------------------------------

1, 任务挂起: void vTaskSuspend(handler);

2, 任务恢复: void vTaskResume(handler);

3, 任务通知(状态位): xTaskNotify(handler, eSetValueWithOverwrite)

               BaseType_t  xReturn == pdPASS 表示成功

4, 任务等待通知: xTaskNotifyWait(0x0,         //进入函数的时候不清除任务bit

                               ULONG_MAX,     //退出函数的时候清除所有的bit

                               &r_num,        //保存任务通知值

                               portMAX_DELAY) //一直等待,0:不等待

                pdTRUE == xReturn

5, 任务信号(二值): ulTaskNotifyTake(pdTRUE,portMAX_DELAY); //true清除计数, 一直等待, 0:立即返回

6, 任务发信号:    xTaskNotifyGive(Receive1Task_Handler);   //发送任务信号

                xReturn == pdPASS

freeRTOS 消息队列 -------------------------------------------------------------------------

1, 创建: QueueHandle_t Test_Queue = xQueueCreate((UBaseType_t ) QUEUE_LEN,/* 消息队列的长度 */

                     (UBaseType_t ) QUEUE_SIZE);/* 消息的大小 */

2, 发送: xQueueSendToFront(queue) 前向入队

        xQueueOverwrite() 写满覆盖, 一般用xQueueCreate( 1 , sizeof(FifoMessage) );

        BaseType_t xReturn = xQueueSend( Test_Queue, /* 消息队列的句柄 */

                                        &send_data2, /* 发送的消息内容 */

                                        0 );         /* 等待时间 0 ,portMAX_DELAY 一直等待*/

        xReturn == pdPASS 表示成功

3, 全部都有 FormISR() 中断用专用, 用于备份和恢复中断mask*/

freeRtos 信号量2值, 自动释放 ----------------------------------------------------------------------------

1, 创建: SemaphoreHandle_t BinarySem_Handle = xSemaphoreCreateBinary();

2, 等待: xReturn = xSemaphoreTake(BinarySem_Handle,/* 二值信号量句柄 */

                                portMAX_DELAY); /* 等待时间*/

        xReturn == xReturn;

3, 发送: xReturn = xSemaphoreGive( BinarySem_Handle ); // take后需要give

        xReturn == pdPASS 表示成功

freeRtos 计数信号量 -------------------------------------------------------------------------------------

1, 创建: SemaphoreHandle_t CountSem_Handle = xSemaphoreCreateCounting(5,5);  //最大5个,初始5个

2, 等待: xReturn = xSemaphoreTake( CountSem_Handle, 0 ) // 等待或者立即返回

        xReturn == pdTRUE

3, 释放: xReturn = xSemaphoreGive( CountSem_Handle )

        xReturn == pdTRUE

freeRtos 互斥量 ----------------------------------------------------------------------

1, 创建: SemaphoreHandle_t MuxSem_Handle = xSemaphoreCreateMutex();  

2, 等待: xReturn = xSemaphoreTake(MuxSem_Handle,/* 互斥量句柄 和信号量一样的函数, 句柄不同*/

      portMAX_DELAY); /* 等待时间 */

3, 释放: xReturn = xSemaphoreGive( MuxSem_Handle );//给出互斥量

freeRTOS 事件 ---------------------------------------------------------------------

1, 创建: EventGroupHandle_t Event_Handle = xEventGroupCreate();

2, 等待: r_event = xEventGroupWaitBits(Event_Handle,  /* 事件对象句柄 */

	    KEY1_EVENT|KEY2_EVENT,/* 接收线程感兴趣的事件 */
	
	    pdTRUE,   /* 退出时清除事件位 */
	
	    pdTRUE,   /* 满足感兴趣的所有事件 */
	
	    portMAX_DELAY);/* 指定超时事件,一直等 */

3, 释放: xEventGroupSetBits(Event_Handle,KEY1_EVENT);

freeRTOS 定时器 ------------------------------------------------------------------

1, 创建: TimerHandle_t Swtmr1_Handle=xTimerCreate((const char*        )"AutoReloadTimer",

	   (TickType_t       )1000,      /* 定时器周期 1000(tick) */
	
	   (UBaseType_t      )pdTRUE,    /* 周期模式 */
	
	   (void*            )1,         /* 为每个计时器分配一个索引的唯一ID */
	
	   (TimerCallbackFunction_t)Swtmr1_Callback);

freeRTOS 内存管理 ---------------------------------------------------------------

1, 申请: Test_Ptr = pvPortMalloc(1024);

2, 释放: void vPortFree(Test_Ptr);

3, 获取大小: g_memsize = xPortGetFreeHeapSize();

freeRTOS 利用率 ------------------------------------------------------------------

1, 取得利用率: vTaskGetRunTimeStats((char *)&CPU_RunInfo);// size 400

        printf("任务名       运行计数         利用率\r\n");

        printf("%s", CPU_RunInfo);

        printf("---------------------------------------------\r\n\n");

2, 任务列表:  vTaskList((char *)&CPU_RunInfo);  //获取任务运行时间信息

         printf("---------------------------------------------\r\n");

         printf("任务名      任务状态 优先级   剩余栈 任务序号\r\n");

         printf("%s", CPU_RunInfo);

         printf("---------------------------------------------\r\n");

QT SLOT -----------------------------------------------------------------------------

1, 信号和槽的参数要一致: QObject:connect(A,SIGNAL(sigfun(int)), B, SLOT(slotfun(int)));        // ok    A 的 信号 sigfun 和 B 的 槽 slotfun 连接

       QObject:connect(A,SIGNAL(sigfun(int)), B, SLOT(slotfun(int, int)));   // error 有一个参数没法提供

       QObject:connect(A,SIGNAL(sigfun(int)), B, SLOT(slotfun(int, int=0))); // ok 有默认值默认的就是可以的

       QObject:connect(A,SIGNAL(sigfun(int,int)), B, SLOT(slotfun(int)));    // ok 提供多了不要紧, 多余的会被忽略

2, 信号和槽之间是可以1对n也可以n对1: QObject:connect(A,SIGNAL(sigfun(int)), B1, SLOT(slotfun(int))); A->B1

       QObject:connect(A,SIGNAL(sigfun(int)), B2, SLOT(slotfun(int))); A->B2

       QObject:connect(A1,SIGNAL(sigfun(int)), B, SLOT(slotfun(int))); A1->B

       QObject:connect(A1,SIGNAL(sigfun(int)), B, SLOT(slotfun(int))); A1->B

3, 信号可以级联:                QObject:connect(A1,SIGNAL(sigfun(int)), A2,SIGNAL(slotfun(int))); A1->A2

4, moc前缀的文件是QT标准转换后的C++的文件

QT Designer ------------------------------------------------------------------------

1, 编译后所有的xx.ui文件会被uic自动转换位xx.h Ui_xx.h

2, xx.h 里是类声明, slot函数声明

3, Ui_xx.h 里面是控件对象申明, 控件布局的代码

4, moc_xx.cpp 是 xx.cpp 里事件的绑定和描述, 简化了xx.cpp的的connect写法

5, 继承ui的设计方案: 
    1, 使用designer命令创建一个xx.ui文件, 使用 uic xx.ui -o ui_xx.h //转换
    2,建立和xx.h里的同名类 Ui::xx, 构造方法里调用 setupUi(this); //初始化控件, 新建类里追加处理函数

6, 组合方案: 

	1, 就是qt建立工程的默认方案, 可以参考默认方法也可以建立一个private的Ui::xx对象 叫ui
	2, 使用ui->setupUi(this); //要放构造的第一句话
	3, 使用ui->xx 使用界面的对象
	4, 析构函数里 delete ui

QT Create ----------------------------------------------------------------------------

1, Qt Widgets Application: qt窗口工程, 常用
2, Qt Quick Application: 使用QML语言和C++结合开发
3, Qt Consol Application: 控制台

QT --------------------------------------------------------------------------------------

1, 所有的事件都来自QEvent

QT sqllite 数据库-------------------------------------------------------------------

1, sudo apt-get install sqlite3

   sqlite3 student.db 打开目录下的 student.db

   .help

   .databse 查看数据库

   .open testDB.db 打开

   .table .tables 查看表

   .schema table 查看创建信息

   .mode 显示模式设置 有list..

   .nullvalue 空白显示的字符

   .header on 显示数据表的表头

   .exit .quit 退出

2, 创建: create table company (

    id INT PRIMARY KEY NOT NULL,

    name TEXT NOT NULL,

    age INT NOT NULL,

    address TEXT,

    salary REAL NOT NULL

);

    删除: DROP TABLE xxx

5, ctrl+l  清屏

6, 配置相关是.xx, 数据库相关是xx;

7, 配置文件: vi ~/.sqliterc

8, 类型: BLOB             none               空

        NONE             none               空

        BIGINT           int                整型

        INTEGER          integer            整型

        INT              int                整型

        DOUBLE           double             浮点型

        REAL             real               浮点型

        NUMERIC          numeric            精确数值型, numeric 与 decimal相同

        DECIMAL(p,s)     decimal            精确数值型, p是精确值, s是小数位数

        BOOLEAN          bool               布尔类型

        STRING           string             字符串类型

        TEXT             text               字符串类型

        VARCHAR          varchar            字符串类型

        CHAR             char               字符串类型

        DATA             data               时间类型, 以 YYYY-MM-DD 格式返回日期

        TIME             time               时间类型, 以 HH:MM:SS 格式返回时间

        DATETIME         datetime           时间类型, 以 YYYY-MM-DD HH:MM:SS 格式返回

9, qt/../gcc/glugins/sqldrivers/ //qt驱动库位置

GUN C ------------------------------------------------------------------------------

1, 零长度数组: 定义在最后一个如 char data[0] 可以紧接后面的内存操作

2, 范围case: case 0 ... 9:

3, 语句表达式:  #define min_t(type,x,y) \

                ({type __x = (x); type __y == (y); __x < __y ? __x : __y;}) //可以提供类型作为局部变量, 且不会有副作用如参数里的++问题

4, typeof关键字: #define min(x,y) \

                ({const typeof(x) __x = (x); const typeof(y) __y == (y);(void)(&__x == &__y); __x < __y ? __x : __y;})

5, 可变参数: #define pr_debug(fmt, arg...) \

            printk(fmt, ##arg) //用了##以后当没有arg则不会有多余的逗号

6, 标号元素: unsigned char data[MAX] = {[0 ... MAX - 1] = 0}

7, 当前函数名: __FUNCTION__ //原名 __PRETTY_FUNCTION__ //带语言特色的名

8, 特殊属性: 1, __attribute__((noreturn)) //无返回

           2, __attribute__((formate(printf, 1, 2))) 按照printf检查格式

           3, __attribute__((aligned(4))) 4字节对齐

           4, __attribute__((packed)) 最小内存

9, 内建函数:

        1, __builtin_return_address(LEVEL)  返回函数的返回地址 指定几层

        2, __builtin_constant_p(EXP)  返回是否编译常量

        3, __builtin_expect(EXP, C)  为编译器提供分支预测信息?

10, do{} while(0): 好处是后面可以加;

linux信号 -------------------------------------------------------------------

1   SIGHUP  控制终端挂起或者断开连接

2   SIGINT  中断信号, 通常由 Ctrl+C 发送

3   SIGQUIT 退出信号, 通常由 Ctrl+\ 发送

4   SIGILL  非法指令信号

5   SIGTRAP 跟踪异常信号

6   SIGABRT 中止信号

7   SIGBUS  总线错误信号

8   SIGFPE  浮点错误信号

9   SIGKILL 强制退出信号

10  SIGUSR1 用户定义信号1

11  SIGSEGV 段错误信号

12  SIGUSR2 用户定义信号2

13  SIGPIPE 管道破裂信号

14  SIGALRM 闹钟信号

15  SIGTERM 终止信号

16  SIGSTKFLT   协处理器栈错误信号

17  SIGCHLD 子进程状态改变信号

18  SIGCONT 继续执行信号

19  SIGSTOP 暂停进程信号

20  SIGTSTP 终端停止信号

21  SIGTTIN 后台进程尝试读取终端输入信号

22  SIGTTOU 后台进程尝试写入终端输出信号

23  SIGURG  套接字上的紧急数据可读信号

24  SIGXCPU 超时信号

25  SIGXFSZ 文件大小限制超出信号

26  SIGVTALRM   虚拟定时器信号

27  SIGPROF 分析器定时器信号

28  SIGWINCH    窗口大小变化信号

29  SIGIO   文件描述符上就绪信号

30  SIGPWR  电源失效信号

31  SIGSYS  非法系统调用信号

32  SIGRTMIN    实时信号最小编号

... ... ...

64  SIGRTMAX    实时信号最大编号

新型物联网通讯协议

ZigBee 内网无线通讯协议 -------------------------------------------------------

1, 低功耗。在低耗电待机模式下, 2节5号干电池可支持1个节点工作6~24个月, 甚至更长。这是ZigBee的突出优势。相比较, 蓝牙能工作数周、WiFi可工作数小时。

    TI公司和德国的Micropelt公司共同推出新能源的ZigBee节点。该节点采用Micropelt公司的热电发电机给TI公司的ZigBee提供电源。

2, 低成本。通过大幅简化协议(不到蓝牙的1/10), 降低了对通信控制器的要求, 按预测分析, 以8051的8位微控制器测算, 全功能的主节点需要32KB代码, 子功能节点少至4KB代码, 而且ZigBee免协议专利费。每块芯片的价格大约为2美元。

3, 低速率。ZigBee工作在20~250kbps的速率, 分别提供250kbps(2.4GHz)、40kbps(915MHz)和20kbps(868MHz)的原始数据吞吐率, 满足低速率传输数据的应用需求。

4, 近距离。传输范围一般介于10~100m之间, 在增加发射功率后, 亦可增加到1~3km。这指的是相邻节点间的距离。如果通过路由和节点间通信的接力, 传输距离将可以更远。

5, 短时延。ZigBee的响应速度较快, 一般从睡眠转入工作状态只需15ms, 节点连接进入网络只需30ms, 进一步节省了电能。相比较, 蓝牙需要3~10s、WiFi需要3s。

6, 高容量。ZigBee可采用星状、片状和网状网络结构, 由一个主节点管理若干子节点, 最多一个主节点可管理254个子节点;同时主节点还可由上一层网络节点管理, 最多可组成65000个节点的大网。

7, 高安全。ZigBee提供了三级安全模式, 包括安全设定、使用访问控制清单(Access Control List, ACL) 防止非法获取数据以及采用高级加密标准(AES 128)的对称密码, 以灵活确定其安全属性。

8, 免执照频段。使用工业科学医疗(ISM)频段, 915MHz(美国), 868MHz(欧洲), 2.4GHz(全球)。

9, 为局域网协议, 连接广域网还要其他设备, 如4G, wifi

NB-IoT 窄带物联网 ----------------------------------------------------------------

1, NB-IoT构建于蜂窝网络,只消耗大约180kHz的带宽,可直接部署于GSM网络、UMTS网络或LTE网络,以降低部署成本、实现平滑升级

2, 提供非常全面的室内蜂窝数据连接覆盖。

3, 一是NB-IoT是蜂窝产业应对万物互联的一个重要机会

BLE 蓝牙低能耗 --------------------------------------------------------------------

1, 是蓝牙技术联盟设计和销售的一种个人局域网技术,旨在用于医疗保健、运动健身、信标、安防、家庭娱乐等领域的新兴应用

2, 相较经典蓝牙,低功耗蓝牙旨在保持同等通信范围的同时显著降低功耗和成本。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Potcutre

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值