linux内核计数函数,linux中的内核引用计数器

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函数来对不再使用的结构体释放,然后返会正数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/541734.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jQuery常用的全局方法源码

下面常用方法的详细使用请查看:http://www.cnblogs.com/moqiutao/p/4775725.html 1.$.noConflict()方法 语法:jQuery.noConflict(removeAll) removeAll:布尔值。指示是否允许彻底将 jQuery 变量还原。 源码: var// Map over jQuer…

isinstance_Java类class isInstance()方法及示例

isinstance类class isInstance()方法 (Class class isInstance() method) isInstance() method is available in java.lang package. isInstance()方法在java.lang包中可用。 isInstance() method is used to check whether the given object is an instance with the object d…

Linux比较大文件内容,Linux系统最大文件打开数优化,解决Too many open files报错

这是一个Linux系统常见的故障,网络上也能轻易的找到解决办法,我也只是在工作中遇到了这个问题,所以在博客记录下,以备不时之需。一、报错截图:图为resin的报错日志,很明显提示了Too many open files&#x…

java日历类add方法_Java日历computeFields()方法及示例

java日历类add方法日历类的computeFields()方法 (Calendar Class computeFields() method) computeFields() method is available in java.util package. 在java.util包中提供了validateFields()方法 。 computeFields() method is used to convert current ms(milliseconds) t…

Varnish缓存代理简介与配置

一、varnish原理:1)Varnish简介:varnish缓存是web应用加速器,同时也作为http反向缓存代理。你可以安装varnish在任何http的前端,同时配置它缓存内容。与传统的 squid 相比,varnish 具有性能更高、速度更快、…

Linux允许61440端口,释放对某端口的占用

释放对某端口的占用假如我们需要确定谁占用了我们的9050端口在windows命令行窗口下执行:1.查看所有的端口占用情况C:\>netstat -ano协议 本地地址 外部地址 状态 PIDTCP 127.0.0.1:1434 0.0.…

as_hash ruby_Hash.merge(other_hash)方法与Ruby中的示例

as_hash rubyHash.merge(other_hash)方法 (Hash.merge(other_hash) Method) In this article, we will study about Hash.merge(other_hash) Method. The working of the method can’t be assumed because it’s quite a different name. Let us read its definition and unde…

linux 安装nfs 客户端,在CentOS 7上安装NFS服务器和客户端

NFS服务器和客户端安装在CentOS 7上版本1.0作者:Srijan Kishore 在Twitter上关注howtoing最后编辑 16 / Dec / 2014本指南介绍如何在CentOS 7.0中配置NFS服务器网络文件系统(NFS)是一种流行的分布式文件系统协议,可让用户在其服务器上安装远程目录。 该系…

安装ORACLE 时报错 /jre/1.4.2/lib/i386/libawt.so:

最近在linux下安装oracle 10g时,碰到如下问题: /tmp/OraInstall2011-09-11_02-16-11PM/jre/1.4.2/lib/i386/libawt.so: libXp.so.6: cannot open shared object file: No such file or directory occurred.. 网上找了下,真让人费解呀&am…

Java线程start()vs run()方法及示例

Java | 线程start()vs run()方法 (Java | Thread start() vs run() Methods) When we call the start() method, it leads to the creation of a new thread. Then, it automatically calls the run() method. If we directly call the run() method, then no new thread will …

linux安装卸载mysql,Linux6 系列 安装、卸载mysql

Linux6 系列 安装、卸载mysqlLinux6 系列 安装、卸载mysqlLinux环境下载mysql:https://blog.csdn.net/weixin_40816738/article/details/90111456一、安装环境依赖:yum install -y cmake make gcc gcc-c libaio ncurses ncurses-devel二、安装流程1、软件…

Python | 如何使用pip升级所有Python软件包?

While using Python as a programming language, its a very common scenario to use a virtual environment and PIP, a package manager for python. 当使用Python作为编程语言时,使用虚拟环境和PIP (Python的程序包管理器)是一种非常常见的情况。 Its a common …

linux下enum类型占几个字节,enum大小问题

问题描述板卡有两个CPU,ARMMIPS,同时运行三个系统REE(linux) TEE(SierraTEE) SEE(TDS)。TEE跟SEE通过RPC进行通信,有enum成员的结构体信息传递会出错,如下结构体:struct sTag {enum A;enum B;int C;enum D;};问题分析…

ASP.NET导出word实例

ASP.NET导出word实例 最近遇到一个题目就是如何在asp.net中将数据导出到word中,由于数据是动态的,所以需要在后台拼出想要的的格式,翻遍了网页找出了一个比较满意的代码,感谢那位高手。代码如下: public void Download…

Java LocalDate类| toString()方法与示例

LocalDate类toString()方法 (LocalDate Class toString() method) toString() method is available in java.time package. toString()方法在java.time包中可用。 toString() method is used to represent this LocalDate as a String by using the standards ISO-8601 format.…

linux14.04 Apache,Ubuntu 14.04编译安装Apache

Ubuntu下编译安装apache需要预先编译安装多个依赖件,包括:apr, apr-util,pcre,zlib-devel,等,相当麻烦,记录于此备查.由于Ubuntu系统默认安装时没有安装C,所以也需要先安装c编译需要相关的组件。[注]apt-ca…

Android Jenkins自动化构建之路

install Jenkins 添加Jenkins的源(repository): sudo wget -O /etc/yum.repos.d/jenkins.repo http://jenkins-ci.org/redhat/jenkins.repo sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key复制代码yum install Jenkins复制代码…

java 根据类名示例化类_Java即时类| plusMillis()方法与示例

java 根据类名示例化类即时类plusMillis()方法 (Instant Class plusMillis() method) plusMillis() method is available in java.time package. plusMillis()方法在java.time包中可用。 plusMillis() method is used to add the given duration in milliseconds to this Insta…

linux dd入门,Linux基础知识:Linux中DD命令详解

1.dd命令简介功能:把指定的输入文件拷贝到指定的输出文件中,并且在拷贝过程中可以进行格式转换。可以用该命令实现DOS下的diskcopy命令的作用。先用dd命令把软盘上的数据写成硬盘的一个寄存文件,再把这个寄存文件写入第二张软盘上&#xff0c…

CSS 字体(font)实例

1、设置文本字体 font-family:"Times New Roman",Georgia,Serif font-family:Arial,Verdana,Sans-serif 2、设置字体尺寸 font-size: 100% 3、设置字体风格 font-style:normal font-style:italic font-style:oblique 4、设置字体的异体 font-variant:normal text-var…