全誌A33 lichee Linux內核原子操作(附實測代碼)
全誌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內核原子操作(附實測代碼)