專業(yè)北京網(wǎng)站建設(shè)自己怎么做網(wǎng)站優(yōu)化
一.原子操作的概念
首先看一下原子操作,原子操作就是指不能再進(jìn)一步分割的操作,一般原子操作用于變量或者位操作。假如現(xiàn)在要對(duì)無符號(hào)整形變量 a 賦值,值為 3,對(duì)于 C 語言來講很簡單,直接就是: a=3
但是 C 語言要先編譯為成匯編指令, ARM 架構(gòu)不支持直接對(duì)寄存器進(jìn)行讀寫操作,比如要借助寄存器 R0、 R1 等來完成賦值操作。假設(shè)變量 a 的地址為 0X3000000,“a=3”這一行 C語言可能會(huì)被編譯為如下所示的匯編代碼:
ldr r0, =0X30000000 /* 變量 a 地址 */
ldr r1, = 3 /* 要寫入的值 */
str r1, [r0] /* 將 3 寫入到 a 變量中 */
示例代碼 只是一個(gè)簡單的舉例說明,實(shí)際的結(jié)果要比示例代碼復(fù)雜的多。從上述代碼可以看出, C 語言里面簡簡單單的一句“a=3”,編譯成匯編文件以后變成了 3 句,那么程序在執(zhí)行的時(shí)候肯定是按照示例代碼 中的匯編語句一條一條的執(zhí)行。假設(shè)現(xiàn)在線程 A要向 a 變量寫入 10 這個(gè)值,而線程 B 也要向 a 變量寫入 20 這個(gè)值,我們理想中的執(zhí)行順序如圖 所示:

按照?qǐng)D所示的流程,確實(shí)可以實(shí)現(xiàn)線程 A 將 a 變量設(shè)置為 10,線程 B 將 a 變量設(shè)置為 20。但是實(shí)際上的執(zhí)行流程可能如下圖所示:

按照?qǐng)D 所示的流程,線程 A 最終將變量 a 設(shè)置為了 20,而并不是要求的 10!線程B 沒有問題。這就是一個(gè)最簡單的設(shè)置變量值的并發(fā)與競爭的例子,要解決這個(gè)問題就要保證示例代碼 中的三行匯編指令作為一個(gè)整體運(yùn)行,也就是作為一個(gè)原子存在。 Linux 內(nèi)核提供了一組原子操作 API 函數(shù)來完成此功能, Linux 內(nèi)核提供了兩組原子操作 API 函數(shù),一組是對(duì)整形變量進(jìn)行操作的,一組是對(duì)位進(jìn)行操作的,我們接下來看一下這些 API 函數(shù)。
二.原子操作的函數(shù)介紹
1.原子整形操作API函數(shù)
Linux 內(nèi)核定義了叫做 atomic_t 的結(jié)構(gòu)體來完成整形數(shù)據(jù)的原子操作,在使用中用原子變量來代替整形變量,此結(jié)構(gòu)體定義在 include/linux/types.h 文件中,定義如下:
typedef struct {int counter;
} atomic_t;

2.原子位操作API函數(shù)


三.實(shí)驗(yàn)
我們這個(gè)還是之前的思路,如果不牽扯到硬件操作,那么我們就在ubuntu pc做實(shí)驗(yàn),這樣高效,比較方便
driver
#include <linux/types.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/device.h>#define CHRDEVBASE_MAJOR 200
uint8_t kernel_buffer[1024] = {0};
static struct class *hello_class;
atomic_t lock;static int hello_world_open(struct inode * inode, struct file * file)
{int count;printk("hello_world_open\r\n");count = atomic_read(&lock);printk("count:%d\r\n",count);if(count){printk("already open ,return fail\r\n");}elseatomic_inc(&lock);return 0;
}static int hello_world_release (struct inode * inode, struct file * file)
{printk("hello_world_release\r\n");atomic_dec(&lock);return 0;
}static const struct file_operations hello_world_fops = {.owner = THIS_MODULE,.open = hello_world_open,.release = hello_world_release,.read = NULL,.write = NULL,
};static int __init hello_driver_init(void)
{int ret;printk("hello_driver_init\r\n");atomic_set(&lock,0);ret = register_chrdev(CHRDEVBASE_MAJOR,"hello_driver",&hello_world_fops);hello_class = class_create(THIS_MODULE,"hello_class");device_create(hello_class,NULL,MKDEV(CHRDEVBASE_MAJOR,0),NULL,"hello"); /* /dev/hello */return 0;
}static void __exit hello_driver_cleanup(void)
{printk("hello_driver_cleanup\r\n");device_destroy(hello_class,MKDEV(CHRDEVBASE_MAJOR,0));class_destroy(hello_class);unregister_chrdev(CHRDEVBASE_MAJOR,"hello_driver");}module_init(hello_driver_init);
module_exit(hello_driver_cleanup);
MODULE_LICENSE("GPL");
app
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>/* mknod /dev/chrdevbase c 200 0 */uint8_t buffer[512] = {0};int main(int argc, char *argv[])
{int fd;int ret;fd = open(argv[1], O_RDWR);printf("sleep\r\n");sleep(60);printf("wake up\r\n");close(fd);}
Makefile
KERNELDIR := /lib/modules/$(shell uname -r)/build
CURRENT_PATH := $(shell pwd)
obj-m := atomic_driver.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules$(CROSS_COMPILE)gcc -o test_app test_app.c
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) cleanrm -rf test_app
參考:
1.【韋東山】嵌入式Linux應(yīng)用開發(fā)完全手冊(cè)V4.0_韋東山全系列視頻文檔-IMX6ULL開發(fā)板.docx
2.【正點(diǎn)原子】I.MX6U嵌入式Linux驅(qū)動(dòng)開發(fā)指南V1.4.pdf