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.pswp.cn/news/541734.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/541734.shtml
英文地址,請注明出處:http://en.pswp.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…