LinuxKernel(一)

首先,回顾一下基础的宏操作:

C语言宏

###

  1. #的作用是字符串化:在一个宏中的参数前面使用一个#,预处理器会把这个参数转换为一个字符数组
#define ERROR_LOG(info) fprintf(stderr,"error:"#info"\n");

则有:

ERROR_LOG("add"); ---> fprintf(stderr,"error: "add"\n");
ERROR_LOG(devied =0); ---> fprintf(stderr,"error: devied=0\n");
  1. #是一种分隔连接方式,它的作用是先分隔,然后进行强制连接。 例如:
#define XNAME(n) x##n

那么 XNAME(4)就会展开为 x4.

do{/*codes*/}while(0)

采用这种方式是为了防范在使用宏过程中出现错误,主要有如下几点:

(1)空的宏定义避免 warning:

#define foo() do{}while(0)

(2)存在一个独立的 block,可以用来进行变量定义,进行比较复杂的实现。
(3)如果出现在判断语句过后的宏,这样可以保证作为一个整体来是实现:

#define foo() \
    action1(); \
    action2();

在遇到分支语句时:

if(NULL == pPtr)
  foo();

foo()中的两个语句就不会都被执行。

(4)为何不用单独的 {}

#define switch(x,y) {int tmp; tmp=x;x=y;y=tmp;}
if(x>y)
 switch(x,y);
else
 op();

在把宏引入代码中,会多出一个分号,从而会报错。

变参宏: ···__VA_ARGS__

某些函数接受可变的参数例如 printf(),在头文件 stdvar.h中有工具可以自定义变参宏。

把宏参数列表中最后的参数用 ···省略,而 __VA_ARGS__可用在替换部分,表面省略号代表的东西。

#define PR(···)  printf(__VA_ARGS__)

例如:

PR("THIS IS __VA_ARGS__");

会被展开为:

printf("THIS IS __VA_ARGS__");

预定义符号

符号 样例值 含义 __FILE__ "test.c"

进行编译的文件名 __LINE__ 25

当前行的行号 __DATE__ "Jan 31 2001"

被编译的日期 __TIME__ "23:17:24"

被编译的时间 __STDC__ 1

是否遵循 ANSI C __FUNCTION__ main

所在函数名称

这些宏与编译器有关,有些支持有些不支持.

如下程序:

LinuxKernel(一)

运行结果为:

LinuxKernel(一)

注意到: 如果用函数或内联函数,每次的行号便都会相同。

下面是内核中,常见的两个宏:

Linux常用的两个宏

offsetof

该宏的定义如下:

#define __offsetof__(type, member) ( ( size_t ) & ( ( type * ) 0 )->member )

作用是获取结构体某成员变量的偏移量。

分析如下:

  1. (type *)0 将0转化为该类型的指针,即地址为0x00000000
  2. ((type *)0)->member 访问成员 member
  3. &(((type *)0)->member) 获取该成员地址(也就是其偏移量)
  4. (size_t)&(((type *)0)->member) 将地址转化为 size_t类型 即偏移量

这里访问0指针为何不会报错,取决于 gcc对于该过程的优化,不会直接访问空间而是直接获得地址.

container_of

该宏的定义如下:

#define __container_of__(ptr, type, member) ({\
                const typeof ( ( ( type * ) 0 ) -> member ) *__mptr=(ptr);\
                ( type * )( ( char * )__mptr - __offsetof__( type, member ) );\
                })

要理解这段宏,需要知道几个 GCC C EXTENSIONS,查阅 GCC MANUAL:

  1. Statements and Declarations in Expressions

    LinuxKernel(一)
    LinuxKernel(一)
  2. Referring to a Type with typeof

LinuxKernel(一)
手册中也给出了一个典型的用法示例:

LinuxKernel(一)

这段宏的分析如下:

  1. typeof()GNU C ,获得变量类型
  2. typeof (((type *)0 )->member) 起始地址为 0 再获取 member 最后返回member类型
  3. const typeof (((type *)0 )->member) * __mptr=(ptr) 定义 __mptr 指针,指向ptr指向的地址,并成为常量指针
  4. (char *)__mptr __mptr转化为字符型指针(运算以1个字节为单位)
  5. - __offsetof__(type,member)) 减去该成员的偏移量
  6. (type*)( ( char * )__mptr - __offsetof__(type,member)) 最后转化为指向该类型的指针(指向该类型的首地址)

关于上述两个宏的一段程序如下:

#include
#include

/**
 * 获取结构体变量成员的偏移量
 * @param type 类型(struct)
 * @param member 成员
 */
#define __offsetof__(type, member) ( ( size_t ) & ( ( type * ) 0 )->member )

/**
 * 获取指向整个结构体的指针
 * @param ptr 指向成员(member)变量的指针
 * @param type 类型(struct)
 * @param member 成员变量
 */
#define __container_of__(ptr, type, member) ({\
                const typeof ( ( ( type * ) 0 ) -> member ) *__mptr=(ptr);\
                ( type * )( ( char * )__mptr - __offsetof__( type, member ) );\
                })

typedef struct Student {
    char gender;
    int id;
    int age;
    char name[20];
    double score;
} Stu;

int main() {
    int gender_offset,id_offset,age_offset,name_offset,score_offset;
    gender_offset = __offsetof__(struct Student, gender);
    id_offset     = __offsetof__(struct Student, id);
    age_offset    = __offsetof__(struct Student, age);
    name_offset   = __offsetof__(struct Student, name);
    score_offset  = __offsetof__(struct Student, score);
    printf("%d\t%d\t%d\t%d\t%d\n", gender_offset, id_offset, age_offset, name_offset, score_offset);

    Stu stu;
    Stu *pstu;
    stu.gender = '1';
    stu.id = 9527;
    stu.age = 18;
    stu.score = 98.2;
    strcpy(stu.name, "elioyang");

    pstu = __container_of__(&stu.id, Stu, id);

    printf("gender=%c\n", pstu->gender);
    printf("age=%d\n", pstu->age);
    printf("name=%s\n", pstu->name);
    printf("score=%lf", pstu->score);

    return 0;
}

运行结果如下:

0       4       8       12      32
gender=1
age=18
name=elioyang
score=98.200000

Original: https://www.cnblogs.com/oasisyang/p/13374833.html
Author: OasisYang
Title: LinuxKernel(一)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/585695/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Redis是如何实现高性能的?

    Redis到底有多快? redis到底有多快,可以通过 redis-benchmark 脚本进行基准测试。redis官方的性能基准测试报告 Redis为什么这么快? redis之所…

    Linux 2023年6月7日
    0108
  • Java面向对象之各种变量详解

    在Java中一定有很多变量让大家头疼,成员变量、类变量、局部变量等等,今天就来分别认识认识他们吧! Java面向对象之各种变量详解 前言 在 Java语言中, 根据定义变量位置的不…

    Linux 2023年6月13日
    084
  • 聊聊Mysql主从同步读写分离配置实现

    Hi,各位热爱技术的小伙伴您们好,好久没有写点东西了,今天写点关于mysql主从同步配置的操作日志同大家一起分享。最近自己在全新搭建一个mysql主从同步读写分离数据库简单集群,我…

    Linux 2023年6月14日
    099
  • Linux网络服务之部署YUM仓库

    镜像下载、域名解析、时间同步请点击阿里云开源镜像站 1 YUM简介 1.1 YUM简介 CentOS使用yum和dnf 解决rpm的包依赖关系。 YUM:rpm的前端程序,可解决软…

    Linux 2023年5月27日
    0124
  • MySQL常见问题以及解决方法

    MySQL常见问题以及解决方法 数据损坏或丢失 serverid不唯一 主从节点存在复制延迟 主从节点数据不一致 数据表主键冲突 数据损坏或丢失 Master:采用MHA+semi…

    Linux 2023年6月7日
    0112
  • 5.5 Vim移动光标命令汇总

    Vim 文本编辑器中,最简单的移动光标的方式是使用方向键,但这种方式的效率太低,更高效的方式使用快捷键。 Vim 移动光标常用的快捷键及其功能如下面各表所示,需要注意的是,表中所有…

    Linux 2023年6月7日
    0108
  • IDEA如何切换到括号外

    一般我们输入完括号后都会习惯性Enter或者Shift + Enter切换到括号外,结果却变成了: public void nothing() Enter → public voi…

    Linux 2023年6月13日
    097
  • 快速掌握 Base 64 | Java JS 密码系列

    Java 密码系列 – Java 和 JS Base 64 Base 64 不属于密码技术,仅是编码方式。但由于在 Java、JavaScript、区块链等出现的频率较…

    Linux 2023年6月7日
    0102
  • 记一次 namespace 卡在terminating

    Error from server (AlreadyExists): object is being deleted: namespaces “monitoring&#…

    Linux 2023年6月14日
    0114
  • 一篇文章Shell脚本快速入门

    Shell脚本基础入门 Bash注释 Bash只支持单行注释,使用 #开头的都被当作注释语句: 整行注释 echo hello world # 行尾注释 通过Bash的一些特性,可…

    Linux 2023年5月28日
    074
  • Linux下如何切割与合并大文件

    我们传输一个大文件时,有时网络比较慢,需要花费很长时间才能传输完成,或者传输的过程中,网络不稳定,有可能导致此次传输失败,针对这种情况,我们可以把大文件切分成小文件,再逐个的传输到…

    Linux 2023年6月13日
    0125
  • 实验一 密码引擎-3-电子钥匙功能测试

    任务详情 1 解压”龙脉密码钥匙驱动实例工具等”压缩包2 在Ubuntu中运行 “龙脉密码钥匙驱动实例工具等\mToken-GM3000\skf…

    Linux 2023年6月8日
    0111
  • [Git系列] Git 基本概念

    版本控制系统 版本控制系统是一种帮助软件开发者实现团队合作和历史版本维护的软件,一个版本控制系统应具备以下列出的这几个基本功能: 允许开发者并发工作; 不允许一个开发者覆写另一个开…

    Linux 2023年6月14日
    0108
  • 正则表达式测试

    本博客所有文章仅用于学习、研究和交流目的,欢迎非商业性质转载。 博主的文章没有高度、深度和广度,只是凑字数。由于博主的水平不高,不足和错误之处在所难免,希望大家能够批评指出。 博主…

    Linux 2023年6月13日
    094
  • C++ 多线程按顺序执行函数

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Linux 2023年6月11日
    093
  • Ubuntu下使用apt-get命令查询并安装指定版本的软件

    执行以下命令,查询软件所有的版本号 sudo apt-cache madison <package></package> 执行以下命令,安装指定版本的软件 …

    Linux 2023年6月6日
    0109
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球