引用计数解决资源初始化及回收问题

1 问题背景

考虑下面这个场景:

在升级芯片固件的时候,例如CPLD芯片,在BMC没有访问CPLD的时候,CPLD和BMC的Jtag没有相连,BMC在升级CPLD的时候需要先把Jtag切到BMC侧,保证两者之间的正常通信,升级完成后需要再把Jtag切换到底座上。BMC通过Jtag升级CPLD时,其他的线程也可能通过Jtag访问CPLD。那么该如何管理这个Jtag的切换呢?

首先可以肯定的是,BMC去通过Jtag访问CPLD的时候,肯定是要先把Jtag切换到BMC侧的。但是问题是什么时候需要把Jtag切回底座上去呢?因为有可能通过硬件Jtag烧录器去烧录,这个时候就需要Jtag切换到底座。

那如果在每次使用完之后直接切换会底座上呢?同样会有问题,假如有两个线程,一个是升级线程,负责升级CPLD;另外一个线程通过Jtag获取CPLD的信息,例如CPLD IDCode等。在线程1升级CPLD的过程中,此时线程2访问CPLD,线程2访问完成后直接把Jtag切换到底座上。紧接着到线程1继续升级CPLD,但是Jtag不是在BMC上,导致无法访问到CPLD,升级失败

2 解决方案

这个问题很C++中常遇到的智能指针的问题如出一辙。当我们在堆上new创建一个对象实例后,其他的线程可能会使用到该对象指针,那么就不能在某个线程使用完后随意销毁。一般都会使用shared_ptr智能指针,shared_ptr有个成员变量是个原子变量,通过引用计数的方式来决定是否销毁shared_ptr所管理的指针。举一反三,我们也可以通过引用计数的方式来管理Jtag。引用计数是通过原子变量的自增和自减来表示使用和释放的,当引用计数大于0,则说明有线程正在使用,不可以切换Jtag;当引用计数等于0时,则说明当前没有现成在使用,可以把引用计数切换到底座上

int cpld_jtag_open(struct inode *inode, struct file *file)
{
    /* 自增1后返回旧值,旧值为0,则切换Jtag到BMC */
    if (atomic_fetch_add(1, &jtag_ref_cnt) == 0) {
        // printk("Set jtag mux to BMC now!");
        set_jtag_mux(SELECT_JTAG_TO_BMC);
    }
    return 0;
}
int cpld_jtag_release(struct inode *inode, struct file *file)
{
    /* 自减后返回新值,新值为0,表示Jtag使用完成,切换Jtag到Connector */
    if(atomic_dec_and_test(&jtag_ref_cnt)) {
        // atomic_read(&jtag_ref_cnt);
        // printk("Select Jtag to connector, ref cnt: %d\n", jtag_ref_cnt.counter);
        set_jtag_mux(SELECT_JTAG_TO_CON);
    }
	return 0;
}

3 常用的原子变量操作

3.1 加操作

原型 说明 返回值
void atomic_inc(atomic_t *v) 自增1
int atomic_inc_return(atomic_t *v) 自增1 返回新值
int atomic_fetch_inc(atomic_t *v) 自增1 返回旧值
void atomic_add(int i, atomic_t *v) 自增i
int atomic_add_return(int i, atomic_t *v) 自增i 返回新值
int atomic_fetch_add(int i, atomic_t *v) 自增i 返回旧值
bool atomic_inc_and_test(atomic_t *v) 自增1 如果新值为0,返回true;否则返回false
bool atomic_add_negative(int i, atomic_t *v) 自增i 如果新值为负数,返回true;否则返回false
int atomic_fetch_add_unless(atomic_t *v, int a, int u) unless表示if not。如果旧值跟u不等,自增a,返回旧值;
如果旧值等于u,只返回旧值,不会进行自增操作
返回旧值
bool atomic_add_unless(atomic_t *v, int a, int u) 如果旧值跟u不等,自增a,返回true
如果旧值等于u,返回旧值false,不会进行自增操作
表示是否进行了自增
bool atomic_inc_not_zero(atomic_t *v) 如果旧值不等于0,那么自增1,返回true;如果旧值等于0,返回false 表示是否进行了自增
bool atomic_inc_unless_negative(atomic_t *v) 如果旧值不是负数,那么自增1,返回true;如果旧值为负数,返回false 表示是否进行了自增

3.2 减操作

原型 说明 返回值
void atomic_dec(atomic_t *v) 自减1
int atomic_dec_return(atomic_t *v) 自减1 返回新值
int atomic_fetch_dec(atomic_t *v)
void atomic_sub(int i, atomic_t *v) 自减i
int atomic_sub_return(int i, atomic_t *v) 自减i 返回新值
int atomic_fetch_sub(int i, atomic_t *v) 自减i 返回旧值
bool atomic_sub_and_test(int i, atomic_t *v) 自减i 如果新值为0,返回true;否则返回false
bool atomic_dec_and_test(atomic_t *v) 自减1 如果新值为0,返回true;否则返回false
bool atomic_dec_unless_positive(atomic_t *v) 如果旧值不是正数(<=0),那么自减1,返回true;如果旧值是正数(>0),返回false 表示是否进行了自减操作

Reference

1 linux内核原子操作学习 - dolinux - 博客园

2 C语言引用计数 - zhangwju - 博客园

posted @ 2025-09-22 11:00  cockpunctual  阅读(5)  评论(0)    收藏  举报