《嵌入式Linux C编程入门》阅读笔记&书评

作者: 华清远见嵌入式培训中心 出版社: 人民邮电出版社
全称:嵌入式Linux C编程入门(第二版)
CUIT索书码:TP316.81/H759-1/E2(盘)

CH1 嵌入式系统的基础知识

• 冯诺依曼结构:具有公用的数据存储空间和程序存储空间,他们共享存储总线,这也是以往设计时常用的方式。
• 哈佛结构具有分离的数据和程序空间以及分离的访问总线。哈佛结构在指令执行时,取值和取数可以并行,因此具有更高的执行效率。

CH2 嵌入式Linux C语言开发工具

vi是Linux系统的第一个全屏幕交互式编辑程序,按不同的使用方式可以分为3种状态:命令行模式、插入模式、底行模式。(编辑)
GCC编译器家族:支持c/c++,java等语言。(编译)
GDB:GNU开源组织发布的一个强大的Linux下的程序调试工具。(调试)
Makefile:自动编译管理器,能够根据文件时间戳自动发现更新过的文件,减少编译的工作量。(管理)
Emacs:集编辑、编译、调试于一体的开发环境。(综合)

CH3 构造嵌入式Linux系统

CH4 嵌入式Linux C语言基础——数据、表达式

数据类型
分为三类:
• 基本类型
○ 整形
○ 浮点型
○ 字符型
○ 枚举型
○ 指针型
• 空类型
• 构造类型
○ 数组
○ 结构体
○ 共用体
字符串与字符常量
“a”“D”为字符串,‘a’为字符常量。
与字符常量相对应的是字符变量:char,占用一个字节的空间。
Const
在关键字后面加上“const”即可表示定义的是常量:
int const a=10;则a的值只能是10,不能修改。
const涉及指针的用法:
Int const *a; 表示const的是整形数组,所以a指向的数组(当然也有可能是数据)是不能改变的。
Int *const a; 表示const的是a,表示指向a的地址是不能变的,但是但是a指向的数据可以改变。
地址/偏移
32为的线性地址:
页全局目录入口 页中间目录入口 页表项 偏移
Linux内核建立页面主要通过三个while循环创建页表地址映射规律。

CH5 嵌入式Linux C语言基础——控制语句及函数

3中程序结构
顺序结构、分支结构、循环结构。
输入输出函数
• 字符串输入输出:gets,puts
• 字符输入输出:getchar,putchar
• 格式输入输出:scanf,printf
字符输入输出格式:
输入:getchar();
输出:putchar(字符);
格式输入输出格式:
输入:scanf(格式说明,&变量);如:printf(“a=%d”,a);
输出:printf(格式说明,输出变量);如:scanf(“a=%d”,&a);
其中,格式说明由%和格式字符组成,具体形式如下表:
十进制整数 d
八进制整数 o
十六进制整数 x
unsigned型数据 u
一个字符 c
一个字符串 s
实数(单双精度) f
指数形式 e
条件函数
switch:
switch函数的选择由case和default构成,一般习惯把default放在最后,但放在前面也不影响。选择语句的后面通常都有break,否则会无视下面的case和default,一直运行到switch的最后一行语句。在确实不需要加break的地方,建议使用/no break/进行标识,便于检查。
break和continue:
Break Continue
用在循环体或switch中,表示跳出当前循环体或当前的switch。 用在循环体中,表示跳过当前循环的其余代码,不跳出循环,而是进入下一组。
【注】不能跳出if
形参与实参
函数调用时,调用语句后面括号里面的参数是实参,函数执行时,函数体中的参数是形参。在函数调用时,实参的数据赋值给形参,这个赋值是单向的,即:只能实参→形参。不论函数运行时形参的值怎样改变,都不能改变实参的值,若想将计算的值传递出来,只能使用return或指针(指针作为函数参数)。

递归:递归函数可以使代码变短,但因为执行时需要保存每一步的参数、变量等,所以会产生很多内部开销,“非必要不使用”。

CH6 嵌入式Linux C语言基础——数组、指针与结构

在创建数组时赋值:
Char a[3]={‘a’,‘b’};
则a[2]被赋值为\0,若不是char类型的数组,则a[2]被赋值为0.
上述赋值也可用:char a[3]={“ab”};
二维数组的赋值:
Int a[2][2]={{1,2},{3,4}}; 或:
Int a[2][3]={1,2,3,4}
给P1分配动态内存:
Int p1;
If((p1=(int
)malloc(sizeof(int)))==NULL){
Perror(malloc);
Return;
}
释放:
Free(p1);
动态内存分配问题的函数原型:void malloc(size_t size);
指针作为函数参数
将指针作为实参,传递给函数,函数内部虽然不能改变指针的值,但可以通过指针找到指向的数据,从而对它进行更改,显示为对
p更改。
还是比较好理解的。

&a与&a[0]等价。
函数指针
形式:int (*p)();
函数名即为函数入口,所以对于p,可以换成别的函数名。

CH7 嵌入式Linux C语言基础——高级议题

预处理、文件包含、堆与栈。
起到函数作用的宏定义
eg:#define MAX(a,b) ((a>b)?a:b)
使用时直接使用MAX(x,y)即可表示(x>y)?x:y
条件编译
格式:
#ifdef a
{
printf(“1”);
}
#else
{
printf(“2”);
}
#endif
作用为:如果前面define了a(即使是#define a这样也算定义过),则输出1,否则输出2.
【注】后面要加上#endif,像VB一样。
条件编译不满足的部分是不会编译的,能缩短目标程序。
堆与栈
堆: 一般由程序员分配(malloc)和释放(free), 若程序员不释放,程序结束时可能由OS(操作系统)回收,分配方式倒是类似于链表。
栈:由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
【注】静态变量不入栈。
memory描述符

#CH8 嵌入式Linux C语言基础——ARM Linux内核常见数据结构
链表
四种链表的指标:
单向链表 双向链表 单向循环链表 双向循环链表
指针域 Next next,priv Next next,priv
结尾指针 NULL NULL 头指针 头指针
内存占用 较少 较多 较少 较多
操作灵活性 较不灵活 较为灵活 较为灵活 非常灵活
时间复杂度与空间复杂度 O(N) O(N) O(N) O(N)
在Linux中,关于链表的接口:
• 初始化:static inline int list_empty(const struct list_head *head))
• 插入:static inline void _list_add(stuct list_head *new, struct list_head *head)
• 删除:static inline void _list_del(struct list_head *prev,struct list_head *next)

树、二叉树、平衡树
二叉树:
• 特殊形态的二叉树:
○ 满二叉树:满的
○ 完全二叉树:除了最下面一层仅有从右向左缺失的若干节点,其余层都是满的。
• 顺序存储:空间利用率高、寻找孩子和双亲比较容易;但插入和删除节点不容易(需要整体移动数组)
○ 第一层,再第二层,再第三层。。。每层从左往右。
• 链式存储:顺序存储依靠编号寻找亲子关系,所以非完全二叉树需要填补空缺,效率低。
节点结构:
Lchild Item Rchild
○ 所有右孩子的数值大于根节点,所有左孩子的数值小于根节点
• 二叉树的遍历(名字里面先、中、后表示更节点的访问次序)
○ 先序遍历:先根,再左,再右
○ 中序遍历:先左,在根,再右
○ 后序遍历:先左,再右,再根
平衡树:因为二叉树各个子树之间的高度相差可能很大,容易造成平均性能下降,所以提出了“平衡树”。通常包括B树、AVL书、红黑树等,能保证最坏情况下为O(logN)的性能。
哈希表
哈希表构造方法:直接定址法、数字分析法、折叠法、除留余数法、随机数法。
冲突处理方法:开放定址法、再哈希法、链地址法、建立一个公共溢出区。

CH9 文件I/O相关实例

通用文件模型
• 超级块:super block,存放系统中已安装文件系统的有关信息。每个文件系统对应一个超级块对象。
• 索引节点:inode,每个文件对应一个索引节点。
• 目录项:dentry,存放目录项与对应文件链接的信息。
• 文件:file,存放打开文件与进程之间进行交互的有关信息。
拓扑:进程→文件→目录项→索引节点→(超级块→)磁盘文件
文件I/O
• Close
• Open
• Read
• Write
• lseek:用于将文件指针定位到相应的位置。
• Fcntl:功能较多(获得/设置文件标记、获得/设置记录锁等)
• Select:选择I/O复用的功能
串口

CH10 ARM Linux进程线程开发实例

进程
进程类型:
• 交互进程
• 批处理进程
• 守护进程
进程状态:
• 运行(running)
• 可中断(interruptible)
• 不可中断(uninterruptible)
• 僵死(zombie):进程运行结束,等待父进程销毁。
• 停止(stopped)
进程优先级在0139中间,其中实时进程(软实时)占用099,一般进程占用100~139。
线程
从内核角度看,Linux不存在线程的概念,所有线程都当做进程来实现,他们具有属于自己的task_stuct,只是这些线程会共享一些资源而已。

进程控制的一些API:
• fork:Linux中创建新进程的位移方法。从已存在的进程中创建一个新进程。
• exec(函数族,共有6个以exec开头的函数,具体的功能有细微差别):创建一个子进程,子进程几乎复制了父进程的全部内容。
使用情况:
○ 当进程认为自己没用时,调用任意一个exec函数,让这个进程重生。
○ 如果进程想执行另一个程序,可以调用fork函数新建一个进程,然后调用任何一个exec函数,看起来像是通过执行应用程序而产生了一个新进程。
• Exit/_exit:终止进程。
• Wait/waitpid:使父进程阻塞。
进程间通信:
使用较多的有:无名管道/有名管道、信号、消息队列、共享内存、信号量。
• 有名管道:int mkfifo(const char *filename,mode_t mode)
• 信号:在软件层面上对中断机制的一种模拟。一个完整的信号生命周期包括:信号产生、信号注册、信号注销、信号处理
○ 发送信号:kinll()、raise()
○ 捕获信号:alarm()、pause()
○ 处理信号:signal()
• 共享内存:最有用的进程间通信方式,但需要依靠同步机制。
• 消息队列:消息队列就是一个消息的链表
Pthread线程库:
• Pthread_creat:创建线程
• Pthread_exit:函数终止
• Pthread_join:等待一个特定的线程退出
• Pthread_mutex_init:创建一个互斥量
• Pthread_yield:主动释放CPU给其他进程
信号量线程控制(PV原语):P减一,V加一,当小于0时,P停止。
同步和互斥:

CH11 ARM Linux网络开发实例

TCP/IP
应用层 Application layer
传输层 Transport layer
互联网层 Internet layer
网络接口层 Network interface layer
应用层在操作系统外部,传输层在操作系统内部,互联层在IP地址上,网络接口层在物理地址上。
可靠性特性:IP协议不能保证IP保温传递的可靠性,但TCP协议面向连接的服务,体现了可靠性。
TCP/IP的特点是将不同的底层物理网络、拓扑结构隐藏起来,向用户和应用程序提供通用、统一的网络服务,使用户看起来整个TCP/IP互联网就是一个统一的整体,它独立于具体的各种物理网络技术,能够向用户提供一个通用的网络服务。

Socket
在Linux系统下,用户通过socket接口进行网络编程操作。常见的socket有:
• 流式(SOCK_STREAM):提供可靠的、面向连接的通信流,使用TCP协议,保证了数据传输的正确性和顺序性。
• 数据报(SOCK_DGRAM):无连接,使用UDP。
• 原始:功能强大但使用较为不便。
traceroute程序实例
在命令提示符下面直接使用tracert即可。可以获得到目标主机时中途经过的路由器信息(IP)。原理是UDP数据包&不同生存时间(TTL)的IP,当TTL为1时,经过一个路由器IP包即被抛弃,为2时可以经过两个路由器,以此类推,返回所有路由器的信息。

CH12 嵌入式Linux设备驱动开发

Linux中的设备驱动程序有如下特点:
• 内核代码
• 内核接口
• 内核机制和服务
• 可装载
• 可设置
• 动态性
字符设备
加载设备驱动模块:init_module()
卸载:cleanup_modele()

块设备
块设备包括:IDE硬盘、SCSI硬盘、光驱等。
每当用户进程对一个块设备发出一个读写请求时,首先调用块设备所公用的函数generic_file_read()和generic_file_write()。如果缓冲区有数据/缓冲区还可以存放数据,则进行数据的读/写。

LCD驱动编写实例
TFT显示屏:(Thin Film Transistor)即薄膜场效应晶体管。

2022年8月29日

书评

前面讲的比较浅显,后面还好(或许可以理解成叫“由浅入深”),学过单片机或C语言的可以当做复习来用。书里面有错误,且在讲到或与非的时候举的例子还不如不举。。。
再者,P163的例子,为了说明递归算法的用途,举了一个例子说除法取余数无法输出“1“”3“”2“”6“,那就分别/1000,/100%10,/10%10和%10啊,为什么非得用余数呢?这样的例子显示不出来后面递归算法的必要性。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Evand J

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值