日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux中原子操作atomic_read、atomic_set、atomic_add、atomic_sub

發(fā)布時(shí)間:2023/12/16 linux 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux中原子操作atomic_read、atomic_set、atomic_add、atomic_sub 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

原子操作
所謂原子操作,就是該操作絕不會(huì)在執(zhí)行完畢前被任何其他任務(wù)或事件打斷,也就說,它的最小的執(zhí)行單位,不可能有比它更小的執(zhí)行單位。因此這里的原子實(shí)際是使用了物理學(xué)里的物質(zhì)微粒的概念。

原子操作需要硬件的支持,因此是架構(gòu)相關(guān)的,其API和原子類型的定義都定義在內(nèi)核源碼樹的include/asm/atomic.h文件中,它們都使用匯編語言實(shí)現(xiàn),因?yàn)镃語言并不能實(shí)現(xiàn)這樣的操作。

原子操作主要用于實(shí)現(xiàn)資源計(jì)數(shù),很多引用計(jì)數(shù)(refcnt)就是通過原子操作實(shí)現(xiàn)的。原子類型定義如下:

typedef struct? {?volatile int counter;? }? atomic_t;

volatile修飾字段告訴gcc不要對該類型的數(shù)據(jù)做優(yōu)化處理,對它的訪問都是對內(nèi)存的訪問,而不是對寄存器的訪問。

atomic_inc(&v)對變量v用鎖定總線的單指令進(jìn)行不可分解的"原子"級(jí)增量操作, 避免v的值由于中斷或多處理器同時(shí)操作造成不確定狀態(tài)。typedef struct { volatile int counter; } atomic_t;volatile修飾字段告訴gcc不要對該類型的數(shù)據(jù)做優(yōu)化處理,對它的訪問都是對內(nèi)存的訪問, 而不是對寄存器的訪問。?原子操作API包括:?atomic_read(atomic_t * v);該函數(shù)對原子類型的變量進(jìn)行原子讀操作,它返回原子類型的變量v的值。?atomic_set(atomic_t * v, int i);該函數(shù)設(shè)置原子類型的變量v的值為i。?void atomic_add(int i, atomic_t *v);該函數(shù)給原子類型的變量v增加值i。?atomic_sub(int i, atomic_t *v);該函數(shù)從原子類型的變量v中減去i。?int atomic_sub_and_test(int i, atomic_t *v);該函數(shù)從原子類型的變量v中減去i,并判斷結(jié)果是否為0,如果為0,返回真,否則返回假。?void atomic_inc(atomic_t *v);該函數(shù)對原子類型變量v原子地增加1。?void atomic_dec(atomic_t *v);該函數(shù)對原子類型的變量v原子地減1。?int atomic_dec_and_test(atomic_t *v);該函數(shù)對原子類型的變量v原子地減1,并判斷結(jié)果是否為0,如果為0,返回真,否則返回假。?int atomic_inc_and_test(atomic_t *v);該函數(shù)對原子類型的變量v原子地增加1,并判斷結(jié)果是否為0,如果為0,返回真,否則返回假。?int atomic_add_negative(int i, atomic_t *v);該函數(shù)對原子類型的變量v原子地增加I,并判斷結(jié)果是否為負(fù)數(shù),如果是,返回真,否則返回假。?int atomic_add_return(int i, atomic_t *v);該函數(shù)對原子類型的變量v原子地增加i,并且返回指向v的指針。?int atomic_sub_return(int i, atomic_t *v);該函數(shù)從原子類型的變量v中減去i,并且返回指向v的指針。?int atomic_inc_return(atomic_t * v);該函數(shù)對原子類型的變量v原子地增加1并且返回指向v的指針。?int atomic_dec_return(atomic_t * v);該函數(shù)對原子類型的變量v原子地減1并且返回指向v的指針。?

  原子操作通常用于實(shí)現(xiàn)資源的引用計(jì)數(shù),在TCP/IP協(xié)議棧的IP碎片處理中,就使用了引用計(jì)數(shù),碎片隊(duì)列結(jié)構(gòu)struct ipq描述了一個(gè)IP碎片,字段refcnt就是引用計(jì)數(shù)器,它的類型為atomic_t,當(dāng)創(chuàng)建IP碎片時(shí)(在函數(shù)ip_frag_create中),使用atomic_set函數(shù)把它設(shè)置為1,當(dāng)引用該IP碎片時(shí),就使用函數(shù)atomic_inc把引用計(jì)數(shù)加1。?
?
  當(dāng)不需要引用該IP碎片時(shí),就使用函數(shù)ipq_put來釋放該IP碎片,ipq_put使用函數(shù)atomic_dec_and_test把引用計(jì)數(shù)減1并判斷引用計(jì)數(shù)是否為0,如果是就釋放IP碎片。函數(shù)ipq_kill把IP碎片從ipq隊(duì)列中刪除,并把該刪除的IP碎片的引用計(jì)數(shù)減1(通過使用函數(shù)atomic_dec實(shí)現(xiàn))。

static inline int atomic_add_return(int i, atomic_t *v) {unsigned long tmp;int result;__asm__ __volatile__("@ atomic_add_return/n" "1: ? ? ldrex ? %0, [%2]/n" " ? ? ? add ? ? %0, %0, %3/n" " ? ? ? strex ? %1, %0, [%2]/n" " ? ? ? teq ? ? %1, #0/n" " ? ? ? bne ? ? 1b": "=&r" (result), "=&r" (tmp): "r" (&v->counter), "Ir" (i): "cc");return result;

Linux中的基本原子操作

宏或者函數(shù)說明
atomic_read返回原子變量的值。
atomic_set設(shè)置原子變量的值。
atomic_add原子的遞增計(jì)數(shù)的值。
atomic_sub原子的遞減計(jì)數(shù)的值。
atomic_cmpxchg原子比較并交換計(jì)數(shù)值。
atomic_clear_mask原子的清除掩碼。

除此以外,還有一組操作64位原子變量的變體,以及一些位操作宏及函數(shù)。這里不再羅列。

/*** 返回原子變量的值。* 這里強(qiáng)制將counter轉(zhuǎn)換為volatile int并取其值。目的就是為了避免編譯優(yōu)化。*/ #define atomic_read(v) ? (*(volatile int *)&(v)->counter)/*** 設(shè)置原子變量的值。*/ #define atomic_set(v,i) ? ?(((v)->counter) = (i))原子遞增的實(shí)現(xiàn)比較精妙,理解它的關(guān)鍵是需要明白ldrex、strex這一對指令的含義。/*** 原子的遞增計(jì)數(shù)的值。*/ static inline void atomic_add(int i, atomic_t *v) {unsigned long tmp;int result;/*** __volatile__是為了防止編譯器亂序。與"#define atomic_read(v) ? ? ? ? ?(*(volatile int *)&(v)->counter)"中的volatile類似。*/__asm__ __volatile__("@ atomic_add\n"/*** ldrex是arm為了支持多核引入的新指令,表示"排它性"加載。與mips的ll指令一樣的效果。* 它與"排它性"存儲(chǔ)配對使用。*/ "1: ? ?ldrex ? ? ? ? %0, [%3]\n"/*** 原子變量的值已經(jīng)加載到寄存器中,這里對寄存器中的值減去指定的值。*/ " ? ? ? add ?%0, %0, %4\n"/*** strex是"排它性"的存儲(chǔ)寄存器的值到內(nèi)存中。類似于mips的sc指令。*/ " ? ? ? strex ? ? ? ? %1, %0, [%3]\n"/*** 關(guān)鍵代碼是這里的判斷。如果在ldrex和strex之間,其他核沒有對原子變量變量進(jìn)行加載存儲(chǔ)操作,* 那么寄存器中值就是0,否則非0.*/ " ? ? ? teq ? %1, #0\n"/*** 如果其他核與本核沖突,那么寄存器值為非0,這里跳轉(zhuǎn)到標(biāo)號(hào)1處,重新加載內(nèi)存的值并遞增其值。*/ " ? ? ? bne ?1b": "=&r" (result), "=&r" (tmp), "+Qo" (v->counter): "r" (&v->counter), "Ir" (i): "cc"); }

atomic_add_return遞增原子變量的值,并返回它的新值。它與atomic_add的最大不同,在于在原子遞增前后各增加了一句:smp_mb();

這是由linux原子操作函數(shù)的語義規(guī)定的:所有對原子變量的操作,如果需要向調(diào)用者返回結(jié)果,那么就需要增加多核內(nèi)存屏障的語義。通俗的說,就是其他核看到本核對原子變量的操作結(jié)果時(shí),本核在原子變量前的操作對其他核也是可見的。

理解了atomic_add,其他原子變量的實(shí)現(xiàn)也就容易理解了。這里不再詳述。

atomic_cmpxchg()函數(shù)實(shí)現(xiàn)了一個(gè)比較+交換的原子操作(原子就是說cpu要不就不做,要做就一定要做完某些操作才能干別的事情,對應(yīng)這里就是比較和交換要一次過做完)。

atomic_cmpxchg()比較kgdb_active->count的值是否等用-1,如果是則把cpu的值賦給kgdb_active->count,否則不修改它的值atomic_cmpxchg返回kgdb_active->count賦值前的值。kgdb_active是一個(gè)全局原子變量,定義在kernel/kgdb.c中,用來記錄當(dāng)前正在執(zhí)行kgdb代碼的cpu號(hào),它起到一個(gè)鎖的作用,因?yàn)橥粫r(shí)間只能有一個(gè)cpu執(zhí)行kgdb的代碼,這是可以想象得到的,如果兩個(gè)cpu在兩個(gè)不同斷點(diǎn)被觸發(fā),那究竟是誰和遠(yuǎn)端gdb通信呢?前一條命令被 cpu1拿了,后一條卻去了cpu2那里,那還得了。

kgdb_active的初始值為-1,-1表示當(dāng)前kgdb的處理函數(shù)并沒有被觸發(fā),相反如果kgdb已經(jīng)在運(yùn)行,那么kgdb_active就有它自己的值,這些處理都是針對多cpu的,如果只有一個(gè)cpu,這個(gè)世界就簡單多了。這里是防止多個(gè)kgdb的實(shí)例在不同cpu被觸發(fā)引起互相干擾??紤]這種情況,在cpu1上有一個(gè)斷點(diǎn)讓kgdb起來,這時(shí),kgdb_active還是-1,cpu1很順利就給kgdb_active賦值然后進(jìn)入后面的操作。這時(shí)cpu2中kgdb也被觸發(fā)。它也想進(jìn)入后面的操作,但是這時(shí)候kgdb_active已經(jīng)不再是-1,cpu2只能不斷地比較kgdb_active的值和執(zhí)行cpu_relax(),宏cpu_relax()可以簡化為一條pause匯編,通過引入一個(gè)很短的延遲,加快了緊跟在鎖后面的代碼的執(zhí)行并減少能源的消耗,實(shí)際上就是讓cpu2等。當(dāng)cpu1在退出kgdb_handle_exception()前會(huì)把 kgdb_active賦回-1, 這樣cpu2就可以進(jìn)行后面的操作了。kgdb使用大量的原子操作來完成鎖的功能,后面還會(huì)看到。atomic操作加上cpu_relax()跟一個(gè)自旋鎖很相似。?

總結(jié)

以上是生活随笔為你收集整理的linux中原子操作atomic_read、atomic_set、atomic_add、atomic_sub的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。