6.2 linux内核原子操作

Linux内核最容易的办法,就是确保这样的操作在芯片级是原子的。任何一个这样的操作都必须以单个指令执行,不能中断,且避免其他的CPU访问同一存储器单元。这些很小的原子操作可以建立在其他更灵活进制的基础之上以创建临界区。

在你编写C代码程序时,并不能保证编译器会为a = a +1 或甚至像 a ++这样的操作使用一个原子指令。因此Linux内核提供了一个专门的 atomic_t 类型和一些专门的函数和宏, 这些函数和宏作用于atomic_t 类型的变量,并当做单独的、原子的汇编语言指令来使用。在多处理器系统中,每条这样的指令都有一个lock字节的前缀。

typedef struct {
    int counter;
} atomic_t;

原子操作分为两种:整形原子操作和位原子操作

整形原子操作常用的操作如下

void atomic_set(atomic_t *v,int i);    //设置原子变量v的值为i
atomic_t v = ATOMIC_INIT(0);     //定义原子变量v,并初始化为0;

atomic_read(atomic_t* v);     //返回原子变量v的值;

void atomic_add(int i, atomic_t* v);     //原子变量v增加i;
void atomic_sub(int i, atomic_t* v);    

void atomic_inc(atomic_t* v);     //原子变量增加1;
void atomic_dec(atomic_t* v);     

 //先自增1,然后测试其值是否为0,若为0,则返回true,否则返回false;
int atomic_inc_and_test(atomic_t* v);    
//先自减1,然后测试其值是否为0,若为0,则返回true,否则返回false;
int atomic_dec_and_test(atomic_t* v);    
//先减i,然后测试其值是否为0,若为0,则返回true,否则返回false;  
int atomic_sub_and_test(int i, atomic_t* v);     
//注意:只有自加1,没有加i操作

//v的值加i后返回新的值;
int atomic_add_return(int i, atomic_t* v);   
int atomic_sub_return(int i, atomic_t* v);  

位原子操作常用操作如下

 //设置地址addr的第nr位,所谓设置位,就是把位写为1;
void set_bit(int nr, volatile void* addr);   
//清除地址addr的第nr位,所谓清除位,就是把位写为0;    
void clear_bit(int nr, volatile void* addr);      
//把地址addr的第nr位反转;
void change_bit(int nr, volatile void* addr);     
//返回地址addr的第nr位;
int test_bit(int nr, volatile void* addr);    
//将*addr 的第n位设置成1,并返回原来这一位的值
int test_and_set_bit(int nr, volatile void* addr);    
//将*addr 的第n位设置成0,并返回原来这一位的值
int test_and_clear_bit(int nr, volatile void* addr); 
//将*addr 的第n位翻转,并返回原来这一位的值
int test_and_change_bit(int nr, volatile void* addr); 

实例

kernel/time/hrtimer.c

struct hrtimer_cpu_base {
  ......
  atomic_t          timer_waiters;
  ......
}

static void hrtimer_sync_wait_running(struct hrtimer_cpu_base *cpu_base,
                      unsigned long flags)
{
    if (atomic_read(&cpu_base->timer_waiters)) {
        raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
        spin_unlock(&cpu_base->softirq_expiry_lock);
        spin_lock(&cpu_base->softirq_expiry_lock);
        raw_spin_lock_irq(&cpu_base->lock);
    }
}

void hrtimer_cancel_wait_running(const struct hrtimer *timer)
{
    /* Lockless read. Prevent the compiler from reloading it below */
    struct hrtimer_clock_base *base = READ_ONCE(timer->base);

    /*
     * Just relax if the timer expires in hard interrupt context or if
     * it is currently on the migration base.
     */
    if (!timer->is_soft || is_migration_base(base)) {
        cpu_relax();
        return;
    }

    /*
     * Mark the base as contended and grab the expiry lock, which is
     * held by the softirq across the timer callback. Drop the lock
     * immediately so the softirq can expire the next timer. In theory
     * the timer could already be running again, but that's more than
     * unlikely and just causes another wait loop.
     */
    atomic_inc(&base->cpu_base->timer_waiters);
    spin_lock_bh(&base->cpu_base->softirq_expiry_lock);
    atomic_dec(&base->cpu_base->timer_waiters);
    spin_unlock_bh(&base->cpu_base->softirq_expiry_lock);
}

drivers/staging/most/video/video.c

struct most_video_dev {
      ......
    atomic_t access_ref;
      ......
};

static int comp_probe_channel(struct most_interface *iface, int channel_idx,
                  struct most_channel_config *ccfg, char *name,
                  char *args)
{
    ......
  atomic_set(&mdev->access_ref, -1);
    ......
}
static int comp_vdev_open(struct file *filp)
{
    if (!atomic_inc_and_test(&mdev->access_ref)) {
        v4l2_err(&mdev->v4l2_dev, "too many clients\n");
        ret = -EBUSY;
        goto err_dec;
    }
    .....
err_dec:
    atomic_dec(&mdev->access_ref);
}
static int comp_vdev_close(struct file *filp)
{
    .....
atomic_dec(&mdev->access_ref);
    .....
}

drivers/video/fbdev/s3c-fb.c

struct s3c_fb {
        ......
    unsigned long        irq_flags;
        ......
};
static void s3c_fb_enable_irq(struct s3c_fb *sfb)
{
    void __iomem *regs = sfb->regs;
    u32 irq_ctrl_reg;

    if (!test_and_set_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {
        /* IRQ disabled, enable it */
        irq_ctrl_reg = readl(regs + VIDINTCON0);

        irq_ctrl_reg |= VIDINTCON0_INT_ENABLE;
        irq_ctrl_reg |= VIDINTCON0_INT_FRAME;

        irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL0_MASK;
        irq_ctrl_reg |= VIDINTCON0_FRAMESEL0_VSYNC;
        irq_ctrl_reg &= ~VIDINTCON0_FRAMESEL1_MASK;
        irq_ctrl_reg |= VIDINTCON0_FRAMESEL1_NONE;

        writel(irq_ctrl_reg, regs + VIDINTCON0);
    }
}
static void s3c_fb_disable_irq(struct s3c_fb *sfb)
{
    void __iomem *regs = sfb->regs;
    u32 irq_ctrl_reg;

    if (test_and_clear_bit(S3C_FB_VSYNC_IRQ_EN, &sfb->irq_flags)) {
        /* IRQ enabled, disable it */
        irq_ctrl_reg = readl(regs + VIDINTCON0);

        irq_ctrl_reg &= ~VIDINTCON0_INT_FRAME;
        irq_ctrl_reg &= ~VIDINTCON0_INT_ENABLE;

        writel(irq_ctrl_reg, regs + VIDINTCON0);
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容