1. 程式人生 > >全誌A33 lichee Linux內核原子操作(附實測代碼)

全誌A33 lichee Linux內核原子操作(附實測代碼)

type stdio.h 實驗 upload module 分割 sym mod 去掉

全誌A33 lichee Linux內核原子操作(附實測代碼)
技術分享圖片

開發平臺

*  芯靈思SinlinxA33開發板

淘寶店鋪: https://sinlinx.taobao.com/

技術分享圖片

嵌入式linux 開發板交流 641395230

原子操作是指不會被線程調度機制打斷的操作;這種操作一旦開始,就一直運行到結束,中間不會有任何線程切換。

原子操作是不可分割的,在執行完畢之前不會被任何其它任務或事件中斷。在單處理器系統(UniProcessor)中,能夠在單條指令中完成的操作都可以認為是" 原子操作",因為中斷只能發生於指令之間。這也是某些CPU指令系統中引入了test_and_set、test_and_clear等指令用於臨界資源互斥的原因。但是,在對稱多處理器(Symmetric Multi-Processor)結構中就不同了,由於系統中有多個處理器在獨立地運行,即使能在單條指令中完成的操作也有可能受到幹擾。我們以decl (遞減指令)為例,這是一個典型的"讀-改-寫"過程,涉及兩次內存訪問。設想在不同CPU運行的兩個進程都在遞減某個計數值,可能發生的情況是:

  • ⒈ CPU A(CPU A上所運行的進程,以下同)從內存單元把當前計數值⑵裝載進它的寄存器中;
  • ⒉ CPU B從內存單元把當前計數值⑵裝載進它的寄存器中。
  • ⒊ CPU A在它的寄存器中將計數值遞減為1;
  • ⒋ CPU B在它的寄存器中將計數值遞減為1;
  • ⒌ CPU A把修改後的計數值⑴寫回內存單元。
  • ⒍ CPU B把修改後的計數值⑴寫回內存單元。

我們看到,內存裏的計數值應該是0,然而它卻是1。如果該計數值是一個共享資源的引用計數,每個進程都在遞減後把該值與0進行比較,從而確定是否需要釋放該共享資源。這時,兩個進程都去掉了對該共享資源的引用,但沒有一個進程能夠釋放它--兩個進程都推斷出:計數值是1,共享資源仍然在被使用。

Linux原子操作大部分使用匯編語言實現,因為c語言並不能實現這樣的操作。
原子操作需要硬件的支持,因此是架構相關的,其API和原子類型的定義都定義在內核源碼樹的 include/asm/atomic.h 文件中

原子操作相關API

atomic.h 這個文件中包含了和具體芯片架構相關的原子操作頭文件arch\arm\include\asm\atomic.h

ATOMIC_INIT(v);
作用: 初始化一個個原子變量,一般比較少用。

atomic_read(atomic_t * v);
作用: 讀取原子變量中的值

atomic_set(atomic_t * v, int i);
作用: 設置原子變量值為i

`void atomic_add(int i, atomic_t *v)
作用: 把原子變量值加上i

void atomic_sub(int i, atomic_t *v)
作用: 把原子變量值減去i

atomic_sub_and_test(i, v)
作用: 把原子變量v的值減去i,判斷相減後的原子變量值是否為0,如果為0返回真

atomic_inc(v);
作用: 把原子變量v加上1

atomic_dec(v)
作用: 把原子變量v減去1

atomic_dec_and_test(v)
作用: 把原子變量v的值減去1,判斷相減後的原子變量值是否為0,如果為0返回真

atomic_inc_and_test(v)
作用: 把原子變量v的值加1,判斷相加後的原子變量值是否為0,如果為0返回真

atomic_add_negative(i,v)
作用: 把原子變量v的值加i,判斷相加後的原子變量值是否為負數,如果為負數返回真

int atomic_add_return(int i, atomic_t *v)
作用: 把原子變量v的值加i,返回相加後原子變量的結果

int atomic_sub_return(int i, atomic_t *v)
作用: 把原子變量v的值減i,返回相減後原子變量的結果

atomic_inc_return(v)
作用: 把原子變量v的值加1後,返回結果

atomic_dec_return(v)·
作用: 把原子變量v的值減1後返回結果

實驗現象:當多個APP調用同一個驅動時,不會發生混亂,依次執行

技術分享圖片
未實現原子操作,所有進程會都執行
技術分享圖片

驅動代碼:

#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/fs.h>  
#include <linux/init.h>  
#include <linux/delay.h>  
#include <linux/uaccess.h>  
#include <asm/irq.h>  
#include <asm/io.h>  
#include <asm/types.h>  
#include <linux/of.h>  
#include <linux/of_device.h>  
#include <linux/of_platform.h>  
#include <linux/atomic.h>  
static int major;  
static struct class *led_class;  
volatile unsigned long *gpio_con = NULL;  
volatile unsigned long *gpio_dat = NULL;  
//定義原子變量  ,初始化值為1  
atomic_t  atomic_v = ATOMIC_INIT(1);  
static int led_open (struct inode *node, struct file *filp)  
{  
    // atomic_dec_and_test(v),判斷減1結果是否0,為0返回真。  
    if( !atomic_dec_and_test(&atomic_v) ){  
      printk("done done done \n");  
        return -1;  
    }  
  
    /* PB7 - 0x01C20824 */  
    if (gpio_con) {  
        printk("ioremap  0x%x\n", gpio_con);  
    }  
    else {  
        return -EINVAL;  
    }  
    printk(" open open open  \n");  
    return 0;  
}  
  
static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *off)  
{  
    unsigned char val;          
    copy_from_user(&val, buf, 1);  
  
    if (val)  
    {  
        *gpio_dat |= (1<<7);  
    }  
    else  
    {  
        *gpio_dat &= ~(1<<7);  
    }  
    printk(" write write write  \n");  
    return 1;   
}  
  
static int led_release (struct inode *node, struct file *filp)  
{  
    //釋放信號量  
    atomic_set(&atomic_v,1);   
    printk("iounmap(0x%x)\n", gpio_con);  
    iounmap(gpio_con);  
    printk(" release release release  \n");  
    return 0;  
}  
  
  
static struct file_operations myled_oprs = {  
    .owner = THIS_MODULE,  
    .open  = led_open,  
    .write = led_write,  
    .release = led_release,  
};  
static int myled_init(void)  
{  
   major = register_chrdev(0, "myled", &myled_oprs);  
   led_class = class_create(THIS_MODULE, "myled");  
   device_create(led_class, NULL, MKDEV(major, 0), NULL, "ledzzzzzzzz");   
   gpio_con = (volatile unsigned long *)ioremap(0x01C20824, 1);   //0x01C20824  
   gpio_dat = gpio_con + 4;     //0x01C20834          
   *gpio_con &= ~(7<<28);  
   *gpio_con |=  (1<<28);  
   *gpio_dat &= ~(1<<7);  
   return 0;  
}  
module_init(myled_init);  
module_exit(led_release);  
MODULE_LICENSE("GPL");

APP代碼:

#include <sys/stat.h>  
#include <fcntl.h>  
#include <stdio.h>  
/* ledtest on 
 *  *   * ledtest off 
 *   *     */  
int main(int argc, char **argv)  
{  
    int fd;  
    unsigned char val = 1;  
  
        fd = open("/dev/ledzzzzzzzz", O_RDWR);  
        if (fd < 0)  
        {  
            printf("can‘t open!\n");  
        }  
        if (argc != 2)  
        {  
            printf("Usage :\n");  
            printf("%s <on|off>\n", argv[0]);  
            return 0;  
        }  
  
        if (strcmp(argv[1], "on") == 0)  
        {  
            val  = 1;  
        }  
        else  
        {  
            val = 0;  
        }  
        write(fd, &val, 1);  
  
    return 0;  
}  

全誌A33 lichee Linux內核原子操作(附實測代碼)