linux中的内核引用计数器文档 /Documentation/kref.txt翻译。

krefs能让你往你的对象中添加一个引用计数器。如果你有一些需要在多处被使用和传递的对象,而你并没有给这些对象中添加引用计数器的话,你的代码肯定会有某些缺陷,会出现一些问题。如果你想使用引用计数器的话,krefs是你可以使用的方法。

为了使用一个kref,你需要向你的数据结构体中嵌入一个struct kref结构体。

可以使用如下的方式:

struct my_data

{

.

.

struct kref  refcount; //向结构体中加入了一个引用计数器。

.

.

};

这个struct kref结构体可以出现在这个数据结构体内的任何地方。

在你分配了这个struct kref结构体之后,你必须要对其进行初始化。为了完成对struct kref结构体的初始话,你可以调用 kref_init():

struct my_data *data;

data = kmalloc(sizeof(struct my_data), GFP_KERNEL);

if(!data)

return -ENOMEM;

kref_init(&data->refcount);

kref_init()函数的原型: void kref_init(struct kref *refcount);

kref_init()函数所做的工作:将struct my_data中的struct kref refcount成员设置为1;

一旦你完成了对struct kref结构体(引用计数器)的初始化,你必须遵守下面的这些规则:

规则一: 如果你对一个指针做了一次非临时性的拷贝,特别是如果这个指针可能被传递到另一个可执行的线程中,那么你必须要在这个拷贝完成之前或这个指针被传递到另一个可执行线程之前,调用用 kref_get(struct kref *refcount)函数对这个引用计数器进行自加操作。如果你已经有了一个指向这个内嵌的struct kref结构体的指针的话(这个引用计数器的值不可 能为零)。

规则二:当你对一个指针的使用结束以后,你必须使用 kref_put()函数:kref_put(&data->refcount, data_release);如果这是对这个指针的最后一次引用的话,这个releas程序将被调用。如果代码不再保持有一个有效的指针,不会再去获取这个被嵌入的struct kref结构体的地址话,那么 直接通过调用 kref_put()函数而不需要使用锁,是不会有问题。

规则三:如果代码没有持有一个有效的指针,而试图去获取对一个嵌入的struct kref结构体的引用的话,代码必须连续访问一个地方,这个地方在使用kref_get()过程中不能有kref_put()函数的出现,并且这个结构体必要要在kref_get()过程中保持有效。

例如:如果你分配了一些数据,然后把它们传递给一个线程去处理:

//当引用计数器的值为0时,通过这个函数来释放不再使用的结构体。

void data_release(struct kref *ref)

{

struct my_data *data = container_of(ref, struct my_data, refcount);

kfree(data);

}

//这是一个线程实体:

void more_data_handling(void *cb_data)

{

struct my_data *data = cb_data;

.

. do stuff with data here

// kref_put(struct kref *refcount, void (*func)(struct kref *refcount));

// kref_put()函数完成两件事情:

1.对引用计数器进行自减;

2.注册一个函数,当引用计数器的值为0时,启动func函数对不再使用的结构体进行释放。

kref_put(&data->refcount, data_release);

}

int my_data_handler(void)

{

int rv = 0;

struct my_data *data;

struct task_struct *task; // struct task_struct *task 用于指向一个新建的线程;

data = kmalloc(sizeof(*data), GFP_KERNEL);

if (!data)

return -ENOMEM;

kref_init(&data->refcount); // kref_init(struct kref *refcount)

// 对引用计数器进行初始化,即将refcount设置为1;

//在下面的more_data_handling线程中,要用到struct my_data类型的结构体,所以

//先使用 kref_get(struct kref *refcount)函数对引用计数器先进行一次自加操作。

kref_get(&data->refcount);

// struct task_struct * kthread_run(void (*kthread)(void *data),                                                          void *data, thread_name)

// kthread     : 线程函数;

// data        : 传递给线程的参数;

// thread_name : 线程的名称;

// 使用kthread_run()函数创建的线程会立刻运行;

task = kthread_run(more_data_handling, data, "more_data_handling");

// ERR_PTR(int errno) 函数将 错误码转换称为一个指针;

if (task == ERR_PTR(-ENOMEM)) {

rv = -ENOMEM;

goto out;

}

.

. do stuff with data here

.

out:

kref_put(&data->refcount, data_release);

return rv;

}

我们不需要关心这两个线程处理数据的顺序,这个 kref_put()处理程序知道数据什么时候不再被引用并且释放它。

下面的做法是违反了规则一,所以是错误的做法;

task = kthread_run(more_data_handling, data, "more_data_handling");

if (task == ERR_PTR(-ENOMEM))

{

rv = -ENOMEM;

goto out;

}

else

kref_get(&data->refcount);// 这个函数应该在kthread_run()运行之前被调用;

在一些情况下,你可能要优化这gets和puts。例如,如果你使用完了一个对象,然后把这个对象传递给其他的函数或将这个对象插入一个队列中以便其他函数使用,就不能先进行一个get然后一个put。

下面的做法是错误的:

/* Silly extra get and put  十分愚蠢的get和put*/

kref_get(&obj->ref);

enqueue(obj);

kref_put(&obj->ref, obj_cleanup);

而正确的做法是:仅仅需要一个入队操作;

enqueue(obj);

而这个规则三是比较难以去处理的。例如:你有一个链表,并且链表中的每个项中都有一个嵌入的struct kref结构体,并且你希望从链表中获取第一个数据项。你不能让这个链表中的第一个数据脱离这个链表,然后kref_get()这个数据。那种做法违反了规则3,因为你并没有持有一个有效的指针。你必须添加一个互斥锁或其他的锁。例如:

static DEFINE_MUTEX(mutex); // DEFINE_MUTEX(mutex) 定义了一个mutex互斥锁并且对其进行初始化

static LIST_HEAD(q); // LIST_HEAD(q) 定义了一个q链表头并对其进行初始化;

struct my_data

{

struct kref refcount; //给struct my_data中加入了一个引用计数器;

struct list_head link;

};

static struct my_data *get_entry()

{

struct my_data *entry = NULL;

mutex_lock(&mutex); // mutex_lock(int *mutex) 用于获取一个互斥锁;

if (!list_empty(&q))  // 如果链路不为空;

{

entry = container_of(q.next, struct my_q_entry, link);

kref_get(&entry->refcount);//引用计数器值加1;

}

mutex_unlock(&mutex);// mutex_unlock(&mutex) mutex_unlock(int *mutex) 释放互斥量

return entry;

}

static void release_entry(struct kref *ref)

{

struct my_data *entry = container_of(ref, struct my_data, refcount);

list_del(&entry->link);

kfree(entry);

}

static void put_entry(struct my_data *entry)

{

mutex_lock(&mutex);

kref_put(&entry->refcount, release_entry);

mutex_unlock(&mutex);

}

如果你并不想在这个release操作过程中持有锁的话,可以使用kref_put()的返回值。

例如:

static void release_entry(struct kref *ref)

{

/* All work is done after the return from kref_put(). */

}

static void put_entry(struct my_data *entry)

{

mutex_lock(&mutex);

if (kref_put(&entry->refcount, release_entry))

{

list_del(&entry->link);

mutex_unlock(&mutex);

kfree(entry);

}

else

mutex_unlock(&mutex);

}

总结一下:这个文档主要介绍了三个函数

void kref_init(struct kref *refcount);//用于对内核引用计数器进行初始化;

void kref_get(struct kref *refcount); //对引用计数器进行加1操作;

int  kref_put(struct kref *refcount, void (*func)(struct kref *refcount));

// kref_put()先对引用计数器中的值进行减 1操作,返回0。

//当引用计数器的值为0的话,调用func函数来对不再使用的结构体释放,然后返会正数。

linux内核计数函数,linux中的内核引用计数器相关推荐

  1. linux内核计数函数,Linux 内核学习笔记:malloc 函数

    Linux 0.11 实现的 malloc 函数实际上分配的是内核程序所用的内存,而不是用户程序的,因为源码中找不到类似系统调用 malloc 的代码.在 Linux 0.98 后,为了不与用户程序使 ...

  2. linux as 4 内核版本,Linux Redhat AS 4中编译内核与以往版本的区别(转载)

    曾经被问及为何在Redhat AS4中找不到内核源代码目录,开始时回答对方在安装时漏选了源码包,对方回答我已经重装过四遍,一个包一个包找过来眼都对起来了也没找到.真见鬼,我在电话里说要不你先到/usr ...

  3. Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 【转】...

    原文地址:Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinauni ...

  4. 【Linux 内核】Linux 内核体系架构 ( 进程调度 | 内存管理 | 中断管理 | 设备管理 | 文件系统 )

    文章目录 一.进程调度 二.内存管理 三.中断管理 四.设备管理 五.文件系统 一.进程调度 进程调度 : 进程 是 系统中 进行 资源分配 的 基本单位 ; 每个进程 在 运行时 , 都 感觉自己占 ...

  5. Linux、GUN/Linux、GUN、GPL以及各个发行版本详细介绍-扫盲篇

    什么是Linux? 也许很多人会不屑的说,Linux不就是个操作系统么.错!Linux不是一个操作系统,严格来讲,Linux只是一个操作系统中的内核.内核是什么?内核建立了计算机软件与硬件之间通讯的平 ...

  6. 的引用_java中的强引用,软引用,弱引用,虚引用

    一对象回收算法 Java在GC时判断对象是否存活有两种方式:第一种是引用计数方式,第二种是可达性分析算法: 引用计数器算法: 在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一:当引用 ...

  7. 引用计数器法 可达性分析算法_面试官:你说你熟悉jvm?那你讲一下并发的可达性分析...

    持续输出原创文章,点击蓝字关注我吧 上面这张图是我还是北漂的时候,在鼓楼附近的胡同里面拍的. 那天刚刚下完雨,路过这个地方的时候,一瞬间就被这五颜六色的门板和自行车给吸引了,于是拍下了这张图片.看到这 ...

  8. CVE-2021-20226:详解 Linux 内核 IO_URING 子系统中的引用计数漏洞

     聚焦源代码安全,网罗国内外最新资讯! 编译:奇安信代码卫士 趋势科技 ZDI 发布文章,详细描述了2020年6月收到的最近引入的 io_uring 子系统的引用计数漏洞情况.该漏洞导致在任意 fil ...

  9. 以下linux进程内存空间中按低地址,Linux 内核开发 - 进程空间

    1.1 虚拟内存 Linux 的系统.假设每一个任务都独立的占用内存,则实际的物理内存将非常快消耗殆尽.实际上对于前台正在执行的任务来说,所须要要的内存并不多,非常多任务基本不须要执行,也就没有必要一 ...

最新文章

  1. css flexbox模型_5分钟内学习CSS Flexbox-初学者教程
  2. Mac下编译ffmpeg出现“ERROR: x265 not found using pkg-config”
  3. 清除浮动的几种常见方法
  4. 解决Tk的窗口左上角位置设定不正确问题
  5. Java有趣的小功能_Java8 中有趣酷炫的小技巧你用到了那些
  6. 程序员40岁之后怎么办
  7. 使用TI 的低功耗C5x DSP的指纹识别方框图和解决方
  8. 永中向香港博览会主办方演示云办公(转载)
  9. Linux学习笔记-标准库中的管道操作
  10. 【Altium Designer2018设计简单的PCB文件实例】
  11. js读取excel,xlsx,xls 表格,转成JSON数据
  12. noi题库 1.7编程基础之字符串 :题解大礼包20180723
  13. 一文读懂什么是绿色工厂以及绿色工厂建设细解
  14. 加速度传感器检测物体倾角的原理
  15. 证件照背景蓝色变其他颜色——用python与OpenCV
  16. leetcode 73 矩阵归零
  17. Win10将绿色软件添加到开始菜单
  18. 微信小程序java python node医疗微服务系统医院预约挂号系统
  19. 9个很棒的CSS边框技巧
  20. 万能码,你的启明星(安全扫码专业委员会)

热门文章

  1. crtsiii型无砟轨道板_自主知识产权CRTSⅢ型轨道板助力,赣深铁路无砟轨道轨道板灌注第一工作面完成...
  2. 51单片机怎么显示当前时间_(进阶篇)51单片机之按键控制蜂鸣器、数码管、按键值移位显示...
  3. springboot怎么设置多个路径全部跳转首页_SpringBoot(四)—Web开发(二)
  4. ip地址转换pta题目_PTA「实验2-3-5 输出华氏-摄氏温度转换表」
  5. python怎么全选_有没有一种方法可以在Python网页上模拟“全选复制粘贴”?
  6. #2002 - 服务器没有响应 (or the local MySQL server's socket is not ...
  7. Net设计模式实例之代理模式(Proxy Pattern)
  8. java8-02-Stream-API
  9. 优化Android应用内存的若干方法
  10. spring boot 1.4默认使用 hibernate validator