C语言扩展-嵌入式C语言
文章平均质量分 83
Hcoco_me
一个搞技术的读书人,妄图分享最干货的技术知识与世界运转的底层逻辑。GZH:TrustZone
展开
-
【C函数】__init
需要注意的是,**__init标记不仅用于函数,也可以用于变量。**带有__init标记的变量会在系统初始化时被赋予一个默认值。**它被用来指示一个函数在系统初始化阶段会被调用。然而,我必须指出,这个解释是基于Linux内核编程的背景。一旦系统已经启动并运行,这些函数就不会再次被调用。,通常用于内核模块编程。原创 2023-11-27 15:01:16 · 351 阅读 · 0 评论 -
【C函数】读寄存器为什么要用ioremap_nocache
ioremap_nocache是一个在Linux内核中。这个函数通常用于访问硬件设备上的内存映射寄存器。与ioremap函数相比,。在某些情况下,例如对硬件寄存器的访问,。因此,使用ioremap_nocache可以。原创 2023-11-27 09:40:45 · 195 阅读 · 0 评论 -
C语言的本质(六):链接详解-定义和声明
以上两节关于变量、函数的定义和声明只介绍了最基本的规则,在写代码时掌握这些基本规则就够用了,但其实C语言关于定义和声明还有很多复杂的规则,在维护别人的代码时还是有必要了解这些规则的。首先看关于函数声明的规则,表1和表2出自参考文献[4],我做了一些修改。前面我说“extern关键字表示这个标识符具有External Linkage”其实是不准确的,准确地说应该是Previous Linkage。原创 2023-08-19 16:24:32 · 126 阅读 · 0 评论 -
C语言的本质(五):链接详解
每个段的描述格式都是“段名 : { 组成 }”,例如.plt : { *(.plt) *(.iplt) },左边表示链接生成的文件的.plt段,右边表示所有目标文件的.plt段和.iplt段,意思是链接生成的文件的.plt段由各目标文件的.plt段和.iplt段组成。另外链接脚本还要把一些特殊地址定义成符号,例如__bss_start代表.bss段的起始地址,_end代表.bss段的结束地址,这些符号会出现在可执行文件的符号表中,加载器可以由这些符号得知.bss段的地址范围,以便把它清零。原创 2023-08-19 16:23:33 · 116 阅读 · 0 评论 -
C语言的本质(四):volatile限定符
本节探讨一下编译器优化会对生成的指令产生什么影响,在此基础上介绍C语言的volatile限定符。首先看下面的例子。我们用recv和send这两个全局变量来模拟设备寄存器。假设某种平台采用内存映射I/O,串口发送寄存器和串口接收寄存器位于固定的内存地址,而recv和send这两个全局变量也有固定的内存地址,所以在这个例子中把它们假想成串口接收寄存器和串口发送寄存器。在main函数中,首先从串口接收三个字节存到buf数组,然后把这三个字节取反,依次从串口发送出去。原创 2023-08-19 16:23:05 · 192 阅读 · 0 评论 -
C语言的本质(三):结构体和联合体
我们再用反汇编的方法研究一下C语言的结构体:从访问结构体成员的指令可以看出,结构体的四个成员在栈上的排列如图18.5所示。虽然,但,这一点和数组类似。但有一点和数组不同,结构体的各成员并不是一个紧挨一个排列的,中间有空隙,称为填充(Padding),不仅如此,在这个结构体的末尾也有三个字节的填充,所以sizeof(s)的值是12。为什么编译器要这样处理呢?原创 2023-08-19 16:22:26 · 55 阅读 · 0 评论 -
C语言的本质(二):汇编与C
C程序编译之后的汇编是什么样的,C语言的各种语法分别对应什么样的指令,从而更深入地理解C语言。gcc还提供了一种扩展语法可以在C程序中内嵌汇编指令,这在内核代码中很常见.1研究函数的调用过程如果在编译时加上-g选项(在之前讲过-g选项),那么用objdump反汇编时可以把C代码和汇编代码穿插起来显示,这样C代码和汇编代码的对应关系看得更清楚。反汇编的结果很长,以下只列出我们关心的部分。要查看编译后的汇编代码,其实还有一种办法是gcc -S main.c,这样只生成汇编代码main.s,而不生成二进制的目原创 2023-08-19 16:21:34 · 191 阅读 · 0 评论 -
C语言的本质(一):计算机中数
一、计算机中数的表示1 加法器与乘法器的实现2 不同进制之间的换算3 整数的加减法运算4 浮点数二、数据类型1 整型2 浮点型3 类型转换三、运算符1 位运算2 其他运算符3 Side Effect与Sequence Point4 运算符总结原创 2023-08-19 16:25:11 · 75 阅读 · 0 评论 -
Linux内核中常用的C语言技巧(三)
在现代操作系统架构中,内核空间和用户空间之间增加了一个中间层,这就是系统调用层。系统调用层主要有如下作用。为用户空间程序提供一层硬件抽象接口。。例如,当需要读写一个文件时,应用程序编写者不用去关心磁盘类型和介质,以及文件存储在磁盘哪个扇区等底层硬件信息。保证系统稳定和安全。应用程序要访问内核必须通过系统调用层,那么,这样可以避免应用程序不正确地访问内核。可移植性。可以让应用程序在不修改源代码的情况下,原创 2023-08-02 01:04:24 · 107 阅读 · 0 评论 -
Linux内核中常用的C语言技巧(二)
Linux内核代码中**广泛使用了数据结构和算法**,其中**最常用的两个是链表和红黑树**。原创 2023-08-02 00:21:04 · 121 阅读 · 0 评论 -
Linux内核中常用的C语言技巧(一)
Linux内核是基于C语言编写的,熟练掌握C语言是深入学习Linux内核的基本要求。# GNU C语言的扩展GCC的C编译器除了支持ANSI C标准之外,还对C语言进行了很多的扩充。**这些扩充对代码优化、目标代码布局以及安全检查等方面提供了很强的支持,因此支持GNU扩展的C语言称为GNU C语言**。Linux内核采用GCC编译器,所以Linux内核的代码自然使用了很多GCC的新扩充特性。本章介绍一些GCC C语言扩充的新特性,希望读者在学习Linux内核时特别留意。原创 2023-07-22 22:49:21 · 209 阅读 · 2 评论 -
# 工欲善其事必先利其器-C语言拓展--嵌入式C语言(十一)
C99标准已经支持了这个特性,但是其他编译器不太给力,对C99标准的支持不是很好,只有GNUC标准支持这个功能,所以有时候我们也把这个可变参数宏看作GNUC标准的一个语法扩展。上面实现的LOG()变参函数,如果我们想使用一个可变参数宏实现,就可以直接这样定义。可变参数宏的实现形式其实和变参函数差不多用…表示变参列表,变参列表由不确定的参数组成,各个参数之间用逗号隔开。......原创 2022-07-17 22:57:31 · 302 阅读 · 0 评论 -
# 工欲善其事必先利其器-C语言拓展--嵌入式C语言(十)
内建函数,顾名思义,就是编译器内部实现的函数。**这些函数和关键字一样,可以直接调用,**无须像标准库函数那样,要先声明后使用。**内建函数的函数命名,通常以__builtin开头。**这些函数主要在编译器内部使用,主要是为编译器服务的。内建函数的主要用途如下。●用来处理变长参数列表。●用来处理程序运行异常、编译优化、性能优化。●查看函数运行时的底层信息、堆栈信息等。●实现C标准库的常用函数。在Linux内核中也经常使用这些函数,所以我们很有必要了解Linux内核中常用的一些内建函数。......原创 2022-07-17 22:25:20 · 763 阅读 · 0 评论 -
# 工欲善其事必先利其器-C语言拓展--嵌入式C语言(九)
如果你曾经看过这个玩意,你会发现其很多的内联函数都在头文件中,为什么?还非得用个static修饰一下。因为它是一个内联函数,可以像宏一样使用,任何想使用这个内联函数的源文件,都不必亲自再去定义一遍,直接包含这个头文件,即可像宏一样使用。使用inline定义的内联函数,编译器不一定会内联展开,那么当一个工程中多个文件都包含这个内联函数的定义时,编译时就有可能报重定义错误。而使用static关键字修饰,则可以将这个函数的作用域限制在各自的文件内,避免重定义错误的发生。......原创 2022-07-17 19:01:16 · 418 阅读 · 0 评论 -
# 工欲善其事必先利其器-C语言拓展--嵌入式C语言(八)
在链接阶段,链接器会到其他文件中找这些符号的定义,若未找到,则报未定义错误。当函数被声明为一个弱符号时,会有一个奇特的地方当链接器找不到这个函数的定义时,也不会报错。编译器会将这个函数名,即弱符号,设置为0或一个特殊的值。只有当程序运行时,调用到这个函数,跳转到零地址或一个特殊的地址才会报错,产生一个内存错误。为了防止函数运行出错,我们可以在运行这个函数之前,先进行判断,看这个函数名的地址是不是0,然后决定是否调用和运行,这样就可以避免段错误了。......原创 2022-07-17 16:33:17 · 263 阅读 · 0 评论 -
# 工欲善其事必先利其器-C语言拓展--嵌入式C语言(七)
_attribute__扩展的format属性,来指定变参函数的参数格式检查。这个作用其实就是自定义打印。因为我们在项目中需要实现一些自定义的调试函数。用户在调用这些接口函数时参数往往不固定,所以就需要用到这个format属性现在来解释解释上面的代码定义一个LOG()变参函数,用来实现日志打印功能。编译器在编译程序时,如何检查LOG()函数的参数格式是否正确呢?方法其实很简单,通过给LOG()函数添加__attribute__((format(printf,1,2)))属性声明就可以了。......原创 2022-07-17 15:38:49 · 241 阅读 · 0 评论 -
# 工欲善其事必先利其器-C语言拓展–嵌入式C语言(六)
于是我们可以对结构体加packed属性,保证每个结构体的成员是连续的。原创 2022-07-17 10:44:16 · 121 阅读 · 0 评论 -
# 工欲善其事必先利其器-C语言拓展--嵌入式C语言(五)
我知道不耻下问是个好事,而且身边那么多大牛,不能算不耻,但是把一些能自己学习搞定的事情,拿去打扰别人,我觉得一点也酷。原创 2022-07-16 23:28:44 · 169 阅读 · 0 评论 -
# 工欲善其事必先利其器-C语言拓展--嵌入式C语言(四)
首先肯定长度是为0的数组ANSIC规定定义一个数组长度必须为一个常数,那么就是这个数组的长度在编译的时候就确定了。inta[10];但是在C99标准中规定可以定义一个变长的数组。intlen;inta[len]这样可以让我们的数组在运行的时候再确定,这是变长。GUNC编译器支持这样定义inta[0];零长度数组不占用内存存储空间。可以用sizeog进行测试哦。零长度一般很少单独使用,常常作为结构体的一个成员,构成一个变长结构体。intlen;inta[0];};...原创 2022-07-16 20:21:19 · 191 阅读 · 0 评论 -
# 工欲善其事必先利其器-C语言拓展--嵌入式C语言(三)
这里需要你先知道结构体怎么存储的。结构体作为一个复合类型数据,它里面可以有多个成员。当我们定义一个结构体变量时,编译器要给这个变量在内存中分配存储空间。根据每个成员的数据类型和字节对齐方式,编译器会按照结构体中各个成员的顺序,在内存中分配一片连续的空间来存储它们。将数字0通过强制类型转换,转换为一个指向结构体类型为student的常量指针,然后分别打印这个常量指针指向的各成员地址。运行结果如下。因为常量指针的值为0,即可以看作结构体首地址为0,container_of最后会返回这个地址值给宏的调用者。...原创 2022-07-16 19:08:29 · 358 阅读 · 2 评论 -
# 工欲善其事必先利其器-C语言拓展--嵌入式C语言(二)
提起宏,脑子里第一时间想到的就是但是最近在看Linux内核代码的时候,我简直了,各种花里胡哨的宏定义,我只能说有点东西。这里的宏加入了语句表达式,一下子宏就变得精彩了。xy那么我们来测试一下?MAX(1,2)MAX(2,1)MAX(2,2)MAX(1!=1,1!=2)如,测试第4行语句,当宏的参数是一个表达式,发现实际运行结果为max=0,和我们预期结果max=1不一样。这是因为,宏展开后,变成如下样子。=1>1!=2?1!=11!=2);因为**比较运算符>的优先级为6,大于!(x)......原创 2022-07-16 19:05:39 · 283 阅读 · 0 评论 -
工欲善其事必先利其器-C语言拓展–嵌入式C语言(一)
我是golang出生,当时做项目的时候java、c++、js、python都折腾过,但是关于c语言的接触,基本上都停留在大一的那个暑假。后来因为要学习协议栈,也捡了一点基础关于c语言,自我感觉还可以啊,直到我打开内核的源码,我一脸懵逼,这都是啥?说它不是c吧,这玩意又在.c文件放着,说它是吧,这都是啥,我开始想念多年前那个下天的c语言了。因为内核和底层是挨着很近的,所以在靠近这个地方,有很多的代码有一些约定成俗的写法,下面一起来瞅瞅?我可叫你一起学习了哈。......原创 2022-07-16 19:04:06 · 403 阅读 · 0 评论