linux内核编程container of()函数介绍

前言

在linux 内核编程中,会经常见到一个宏函数container_of(ptr,type,member), 但是当你通过追踪源码时,像我们这样的一般人就会绝望了(这一堆都是什么呀? 函数还可以这样定义??? 怎么还有0呢???  哎,算了,还是放弃吧。。。)。 这就是内核大佬们厉害的地方,随便两行代码就让我们怀疑人生,凡是都需要一个过程,慢慢来吧。

其实,原理很简单:  已知结构体type的成员member的地址ptr,求解结构体type的起始地址。

type的起始地址 = ptr - size      (这里需要都转换为char *,因为它为单位字节)。

到此,该函数已经讲完,是不是很简单??? 其实也不是,这里并没有提到size如何计算,而令我们头晕的正是这里。

好吧,先上container of函数原型:

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

其次为 offserof 函数原型:

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

怎么样,是不是很炫?  好吧,下面开始揭开面纱:

(一)0 指针的使用    (自己给的名字,不知有木问题)

让事实说话:

#include<stdio.h>

struct test
{
	char i ;
	int j;
	char k;
};

int main()
{
	struct test temp;
	printf("&temp = %p\n",&temp);
	printf("&temp.k = %p\n",&temp.k);
	printf("&((struct test *)0)->k = %d\n",((int)&((struct test *)0)->k));

}

编译运行,可以得到如下结果:

&temp = 0xbf9815b4
&temp.k = 0xbf9815bc
&((struct test *)0)->k = 8

什么意思看到了吧,自定义的结构体有三个变量:i,j,k。 因为有字节对齐要求,所以该结构体大小为4bytes * 3 =12 bytes.   而&((struct test *)0)->k 的作用就是求 k到结构体temp起始地址的字节数大小(就是我们的size)。在这里0被强制转化为struct test *型, 它的作用就是作为指向该结构体起始地址的指针,就是作为指向该结构体起始地址的指针,就是作为指向该结构体起始地址的指针, 而&((struct test *)0)->k  的作用便是求k到该起始指针的字节数。。。其实是求相对地址,起始地址为0,则&k的值便是size大小(注:打印时因为需要整型,所以有个int强转)所以我们便可以求我们需要的 size 了  。 好吧,一不小心把 offsetof() 函数的功能给讲完了:::

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

这次再看就顺眼了吧(底层为什么是这样我还是不懂。。。只知道这样确实可以) ,  所以offsetof()的作用就是求我们梦寐以求的size, 并以size_t形式返回(size_t: 无符号整型)。

(二) 内核编程的严谨性

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

这里我们只看第二行:

const typeof( ((type *)0)->member ) *__mptr = (ptr);

它的作用是什么呢? 其实没什么作用(勿喷勿喷,让我把话说完),但就形式而言 _mptr = ptr,  那为什么要要定义一个一样的变量呢??? 其实这正是内核人员的牛逼之处:如果开发者使用时输入的参数有问题:ptr与member类型不匹配,编译时便会有warnning, 但是如果去掉改行,那个就没有了,而这个警告恰恰是必须的(防止出错有不知道错误在哪里)。。。这严谨性可以吧

typeof( ((type *)0)->member )

它的作用是获取member的类型仅此而已。至此基本结束

(三) 总结

container_of(ptr, type,member)函数的实现包括两部分:

1.  判断ptr 与 member 是否为同意类型

2.  计算size大小,结构体的起始地址 = (type *)((char *)ptr - size)   (注:强转为该结构体指针)

现在我们知道container_of()的作用就是通过一个结构变量中一个成员的地址找到这个结构体变量的首地址。

container_of(ptr,type,member),这里面有ptr,type,member分别代表指针、类型、成员。

到此这篇关于linux内核编程container of()函数的文章就介绍到这了,更多相关linux container of()函数 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • linux内核编程container of()函数介绍

    前言 在linux 内核编程中,会经常见到一个宏函数container_of(ptr,type,member), 但是当你通过追踪源码时,像我们这样的一般人就会绝望了(这一堆都是什么呀? 函数还可以这样定义??? 怎么还有0呢???  哎,算了,还是放弃吧...). 这就是内核大佬们厉害的地方,随便两行代码就让我们怀疑人生,凡是都需要一个过程,慢慢来吧. 其实,原理很简单:  已知结构体type的成员member的地址ptr,求解结构体type的起始地址. type的起始地址 = ptr - s

  • 详解Linux内核中的container_of函数

    前言 在linux 内核中,container_of 函数使用非常广,例如 linux内核链表 list_head.工作队列work_struct中. 在linux内核中大名鼎鼎的宏container_of() ,其实它的语法很简单,只是一些指针的灵活应用,它分两步: 第一步,首先定义一个临时的数据类型(通过typeof( ((type *)0)->member )获得)与ptr相同的指针变量__mptr,然后用它来保存ptr的值. 第二步,用(char *)__mptr减去member在结构体

  • C语言container of()函数案例详解

          在linux 内核编程中,会经常见到一个宏函数container_of(ptr,type,member), 但是当你通过追踪源码时,像我们这样的一般人就会绝望了(这一堆都是什么呀? 函数还可以这样定义??? 怎么还有0呢???  哎,算了,还是放弃吧...). 这就是内核大佬们厉害的地方,随便两行代码就让我们怀疑人生,凡是都需要一个过程,慢慢来吧.         其实,原理很简单:  已知结构体type的成员member的地址ptr,求解结构体type的起始地址.        

  • 简析Linux网络编程函数

    目录 1,创建套接字socket 2,绑定套接字bind 3,创建监听:listen 4,等待连接accept 5, 收发消息send和recv 6,关闭套接字描述符close 7,基于tcp协议的C/S服务器模型 8,实现代码 网络编程的一些基本函数:也是实现tcp协议通讯的基本步骤,实现代码在最后,IP需要修改为自己的IP,即可通信: 1,创建套接字socket 函数原型: #include<sys/types.h> #include<sys/socket.h> int soc

  • 详解Linux内核进程调度函数schedule()的触发和执行时机

    内核的调度操作分为触发和执行两个部分,触发时仅仅设置一下当前进程的TIF_NEED_RESCHED标志,执行的时候则是通过schedule()函数来完成进程的选择和切换.当前进程的thread_info->flags中TIF_NEED_RESCHED位表示需要调用schedule()函数进行调度.内核在两种情况下会设置该标志,一个是在时钟中断进行周期性的检查时,另一个是在被唤醒进程的优先级比正在运行的进程的优先级高时. 周期性地更新当前任务的状态时: 定时中断处理函数中会调用schedule_t

  • Linux内核漏洞浅析

    与Windows相比,Linux被认为具有更好的安全性和其他扩展性能.这些特性使得Linux在操作系统领域异军突起,得到越来越多的重视.随着Linux应用量的增加,其安全性也逐渐受到了公众甚或黑客的关注.那么,Linux是否真的如其支持厂商们所宣称的那样安全呢?本期我们请到了启明星辰信息技术有限公司积极防御实验室工程师赵伟,对Linux进行专业的漏洞技术分析. Linux内核精短.稳定性高.可扩展性好.硬件需求低.免费.网络功能丰富.适用于多种cpu等特性,使之在操作系统领域异军突起.其独特的魅

  • 解析Linux内核的基本的模块管理与时间管理操作

    内核模块管理 Linux设备驱动会以内核模块的形式出现,因此学会编写Linux内核模块编程是学习linux设备驱动的先决条件. Linux内核的整体结构非常庞大,其包含的组件非常多.我们把需要的功能都编译到linux内核,以模块方式扩展内核功能. 先来看下最简单的内核模块 #include <linux/init.h> #include <linux/module.h> static int __init hello_init(void) { printk(KERN_ALERT &

  • Linux Socket 编程简介和实现

    在 TCP/IP 协议中,"IP地址 + TCP或UDP端口号" 可以唯一标识网络通讯中的一个进程,"IP地址+端口号" 就称为 socket.本文以一个简单的 TCP 协议为例,介绍如何创建基于 TCP 协议的网络程序. TCP 协议通讯流程 下图描述了 TCP 协议的通讯流程(此图来自互联网): 下图则描述 TCP 建立连接的过程(此图来自互联网): 服务器调用 socket().bind().listen() 函数完成初始化后,调用 accept() 阻塞等待

  • Linux内核宏container_of的深度剖析

    1.前面说的 我在好几年前读linux 驱动代码的时候看到这个宏,百度了好久,知道怎么用了,但是对实现过程和原理还是一知半解. container_of宏 在linux内核代码里面使用次数非常非常多,对于喜欢linux编程的同学来说,了解其实现方法,对以后看内核代码,写内核驱动的帮助都非常大,当然,我不是说了解这个就可以为所欲为了,内核博大精深,先宏观再微观去学习,不积跬步何以致千里,不要想着一口就能吃成一个胖子,我这篇文章主要剖析一下这个函数的实现原理,希望对大家学习过程中有所帮助. andr

  • linux内核copy_{to, from}_user()的思考

    目录 一.什么是copy_{to,from}_user() 1.copy_{to,from}_user()对比memcpy() 2.函数定义 二.CONFIG_ARM64_SW_TTBR0_PAN原理 三.测试 四.总结 一.什么是copy_{to,from}_user() 它是kernel space和user space沟通的桥梁.所有的数据交互都应该使用类似这种接口.但是他的作用究竟是什么呢?我们对下提出疑问: 为什么需要copy_{to,from}_user(),它究竟在背后为我们做了什

随机推荐