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)

大家都在看

  • WEB自动化-05-Cypress-元素交互

    5 元素交互 元素识别和操作是UI自动化测试的基础,下面一起来学习一下在Cypress中的元素交互操作吧。 5.1 元素定位器选择 每一个测试用例都包含对元素的定位识别和操作等。因…

    Linux 2023年6月7日
    0100
  • MySQL数据库高可用方案

    一.什么是高可用性: 高可用性=可靠性,它的本质就是通过技术和工具提高可靠性,尽可能长时间保持数据可用和系统运行,实现高可用性的原则,首先要消除单点故障,其次通过冗余机制实现快速恢…

    Linux 2023年6月6日
    0108
  • shell脚本并发执行

    简单的并发脚本 如果shell不能执行,或者报格式错误,记得用 Original: https://www.cnblogs.com/phpdragon/p/10511256.htm…

    Linux 2023年5月28日
    0101
  • Java50个关键字之static

    关键字static主要有两种作用:第一,为某特定数据类型或对象分配单一的存储空间,而与创建对象的个数无关。第二,希望某个方法或属性与类而不是对象关联在一起,也就是说,在不创建对象的…

    Linux 2023年6月7日
    097
  • Linux下如何修复陈旧的第三方微信版本electronic-wechat

    因为现在的Linux发行版软件库太新的缘故,导致陈旧的electronic-wechat的文本引擎库不能正确运行,一般表现为harfbuzz too old等错误。 即使你把har…

    Linux 2023年6月14日
    0109
  • Linux三剑客命令—sed

    一、概念说明 官方概念说明: stream editor for filtering and transforming text字符流过滤器编辑和文本字符流转换工具 [En] Ch…

    Linux 2023年5月27日
    0123
  • phpcms安装

    【快速安装开始】 下载解压phpcms,复制安装文件到站点目录”/opt/html”里,给予权限(官网无法访问了,所以下载地址需自行寻找上传) cd /us…

    Linux 2023年6月6日
    072
  • zabbix快速安装(yum)

    1、先卸载系统自带数据库 [root@bogon ~]# rpm -e mariadb-libs-5.5.56-2.el7.x86_64 –nodeps 2、安装mys…

    Linux 2023年6月6日
    083
  • Ubuntu系统中MySQL安装后基本配置

    mysql8.0安装好后并不会让你输入root密码,而是采用默认账户+默认密码的方式保护数据库安全,但开发环境并不需要这样做。那么怎么找到这个默认账户和密码,又如何修改数据库使它可…

    Linux 2023年6月14日
    081
  • CentOS下配置NTP时间服务器

    配置ntp.conf [root@server ~]# vim /etc/ntp.conf For more information about this file, see th…

    Linux 2023年6月13日
    0101
  • 重启电脑后Mysql无法在cmd运行

    问题描述:如果在cmd窗口显示 ‘mysql’不是内&#x90…

    Linux 2023年6月15日
    0141
  • Redis分布式锁实战

    背景 目前开发过程中,按照公司规范,需要依赖框架中的缓存组件。不得不说,做组件的大牛对CRUD操作的封装,连接池、缓存路由、缓存安全性的管控都处理的无可挑剔。但是有一个小问题,该组…

    Linux 2023年5月28日
    092
  • XCTF pwn新手区解题记录

    一、前言 闲来无事,刷刷ctf题 二、题目: level0 1、下载好题目后,拖入到kali中去,用 file 和 checksec 查看一下,可以发现该程序是 64&#x…

    Linux 2023年6月8日
    0106
  • 【Leetcode】198. 打家劫舍

    你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统, 如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动…

    Linux 2023年6月6日
    098
  • frp实现Windows远程连接(docker)

    服务端配置 服务端拉镜像 docker pull snowdreamtech/frps 在某个目录下新建配置文件 frps.ini ,比如我是在/home/docker/frp目录…

    Linux 2023年6月8日
    093
  • Linux pssh安装与使用

    说明:我这是没有在密钥认证的情况下操作 1、安装pssh [root@libin ansible]# yum install -y pssh [root@libin ansible…

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