linux 原子操作 原理,GCC数值原子操作API原理及应用

一.前言

C/C++中数值操作,如自加(n++)自减(n–-)及赋值(n=2)操作都不是原子操作,如果是多线程程序需要使用全局计数器,程序就需要使用锁或者互斥量,对于较高并发的程序,会造成一定的性能瓶颈。

二.gcc原子操作api

**1.**概要

为了提高赋值操作的效率,gcc提供了一组api,通过汇编级别的代码来保证赋值类操作的原子性,相对于涉及到操作系统系统调用和应用层同步的锁和互斥量,这组api的效率要高很多。

**2.**n++类

type __sync_fetch_and_add(type *ptr, type value, ...); // m+n

type __sync_fetch_and_sub(type *ptr, type value, ...); // m-n

type __sync_fetch_and_or(type *ptr, type value, ...); // m|n

type __sync_fetch_and_and(type *ptr, type value, ...); // m&n

type __sync_fetch_and_xor(type *ptr, type value, ...); // m^n

type __sync_fetch_and_nand(type *ptr, type value, ...); // (~m)&n

/* 对应的伪代码 */

{ tmp = *ptr; *ptr op= value; return tmp; }

{ tmp = *ptr; *ptr = (~tmp) & value; return tmp; } // nand

3.++n类

type __sync_add_and_fetch(type *ptr, type value, ...); // m+n

type __sync_sub_and_fetch(type *ptr, type value, ...); // m-n

type __sync_or_and_fetch(type *ptr, type value, ...); // m|n

type __sync_and_and_fetch(type *ptr, type value, ...); // m&n

type __sync_xor_and_fetch(type *ptr, type value, ...); // m^n

type __sync_nand_and_fetch(type *ptr, type value, ...); // (~m)&n

/* 对应的伪代码 */

{ *ptr op= value; return *ptr; }

{ *ptr = (~*ptr) & value; return *ptr; } // nand

4.CAS类

bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval, ...);

type __sync_val_compare_and_swap (type *ptr, type oldval, type newval, ...);

/* 对应的伪代码 */

{ if (*ptr == oldval) { *ptr = newval; return true; } else { return false; } }

{ if (*ptr == oldval) { *ptr = newval; } return oldval; }

三.程序实例

1.test.c

例子不是并发的程序,只是演示各api的使用参数和返回。由于是gcc内置api,所以并不需要任何头文件。

#include

int main() {

int num = 0;

/*

* n++;

* __sync_fetch_and_add(10, 3) = 10

* num = 13

*/

num = 10;

printf("__sync_fetch_and_add(%d, %d) = %d\n", 10, 3, __sync_fetch_and_add(&num, 3));

printf("num = %d\n", num);

/*

* ++n;

* __sync_and_add_and_fetch(10, 3) = 13

* num = 13

*/

num = 10;

printf("__sync_and_add_and_fetch(%d, %d) = %d\n", 10, 3, __sync_add_and_fetch(&num, 3));

printf("num = %d\n", num);

/*

* CAS, match

* __sync_val_compare_and_swap(10, 10, 2) = 10

* num = 2

*/

num = 10;

printf("__sync_val_compare_and_swap(%d, %d, %d) = %d\n", 10, 10, 2, __sync_val_compare_and_swap(&num, 10, 2));

printf("num = %d\n", num);

/*

* CAS, not match

* __sync_val_compare_and_swap(10, 3, 5) = 10

* num = 10

*/

num = 10;

printf("__sync_val_compare_and_swap(%d, %d, %d) = %d\n", 10, 3, 5, __sync_val_compare_and_swap(&num, 1, 2));

printf("num = %d\n", num);

return 0;

}

四.Reference

Linux信号量的实现原理是基于操作系统提供的semaphore.h头文件中的sem_t类型。要使用信号量,首先需要创建一个sem_t类型的变量,并对其进行初始化。 在Linux中,信号量的操作主要包括P操作和V操作。P操作被称为申请操作,它的本质是尝试获取临界资源的使用权限。当执行流申请信号量时,如果信号量的计数器大于0,表示临界资源可用,执行流可以继续执行;如果计数器等于0,表示临界资源已被占用,执行流将被挂起等待。P操作的实质是将计数器减一。 V操作被称为释放操作,它的本质是归还临界资源的使用权限。当执行流释放信号量时,计数器加一。如果有其他执行流在等待信号量,被挂起的执行流将被唤醒并获得资源的使用权限。 为了保证P和V操作的原子性,需要使用互斥锁来对这些操作进行加锁和解锁。这样可以保证在多个执行流同时申请或释放信号量时,操作的正确性和一致性。当信号量的计数器为0时,执行流会被挂起等待,直到有其他执行流释放信号量并唤醒它们。 在实际使用中,需要注意的是,在初始化信号量时,可以设置pshared参数来指定信号量是线程间共享还是进程间共享。如果pshared参数为0,则表示信号量在线程间共享;非零值表示信号量在进程间共享。同时,需要设置value参数来指定信号量的初始值,即计数器的初始值。 综上所述,Linux信号量的实现原理是基于semaphore.h头文件中的sem_t类型。通过对计数器的加减操作和互斥锁的应用,实现了对临界资源的申请和释放控制。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Linux —— 信号量](https://blog.csdn.net/sjsjnsjnn/article/details/126307485)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值