28 atomic_t原子数与原子位操作

假如驱动里实现对write操作进行计算次数,当进程读操作时输出次数.
test.c:

#include 
#include 
#include 
#include 

int count = 0;
ssize_t myread(struct file *fl, char __user *buf, size_t len, loff_t *off)
{
    //读时,输出count的值
    printk("count = %d\n", count);
    return 0;
}

ssize_t mywrite(struct file *fl, const char __user *buf, size_t len, loff_t *off)
{
    //每个进程调用时,count的值++

    count++;
    return len; //表示写操作成功
}

struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = myread,
    .write = mywrite,
};

struct miscdevice mymdev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "mymdev",
    .fops = &fops,
};

static int __init test_init(void)
{
    return misc_register(&mymdev);
}

static void __exit test_exit(void)
{
    misc_deregister(&mymdev);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");


app.c

#include 
#include 
#include 

#define LEN  20000000 

//实现两个进程同时write操作
int main(void)
{
    int fd, i, ret;

    fd = open("/dev/mymdev", O_RDWR);
    if (fd < 0)
    {
        perror("open mymdev ");
        return 1;
    }

    ret = fork();

    for (i = 0; i < LEN; i++)
        write(fd, "kk", 2); //触发驱动里的write函数, 驱动里的count++

    if (ret)
        wait(NULL); //在父进程里回收子进程,确保都操作完成

    return 0;
}

操作:

    insmod test.ko
    ./a.out
    cat /dev/mymdev 
    [ 2313.811542] count = 31942372  //输出的值是不正确的,正常应是40000000。为什么会不正确?
    //应当一个进程进行++操作时,需先把值存在cpu的寄存器里,add操作后再把寄存器上的值写回去。两个进程有可能同时读出相同的值到寄存器,都add后,就会把相同的值写回去,这样就会少一次计数了.

///
为了解决上面计数的问题,内核里有针对计数并实现原子操作接口的类型:

typedef struct {
    int counter;
} atomic_t;


arch/arm/include/asm/atomic.h:

#define ATOMIC_INIT(i)  { (i) }  //用于初始化对象用:  atomic_t myat = ATOMIC_INIT(0);
#define atomic_read(v)  (*(volatile int *)&(v)->counter) //读原子数的值
#define atomic_set(v,i) (((v)->counter) = (i))  //设置原子数的值
// 注意上面三个操作都是没有保护作用的

//下面的操作函数是有保护的。都用到汇编指令"ldrex"/"strex",这两个指令可以确保对寄存器操作是原子操作的.
void atomic_add(int i, atomic_t *v);
int atomic_add_return(int i, atomic_t *v);
void atomic_sub(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
#define atomic_inc(v)       atomic_add(1, v)  // 自增
#define atomic_dec(v)       atomic_sub(1, v)  // 自减

//驱动里改成使用原子数计数
test.c

#include 
#include 
#include 
#include 
#include 

atomic_t myat = ATOMIC_INIT(0);
ssize_t myread(struct file *fl, char __user *buf, size_t len, loff_t *off)
{
    //读时,输出原子数的值
    printk("count = %d\n", atomic_read(&myat));
    return 0;
}

ssize_t mywrite(struct file *fl, const char __user *buf, size_t len, loff_t *off)
{
    //每个进程调用时,原子数的值++
    atomic_inc(&myat);
    return len; //表示写操作成功
}

struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = myread,
    .write = mywrite,
};

struct miscdevice mymdev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "mymdev",
    .fops = &fops,
};

static int __init test_init(void)
{
    return misc_register(&mymdev);
}

static void __exit test_exit(void)
{
    misc_deregister(&mymdev);
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

//测试后会发现结果是正确的.

//

通常情况下,在linux内核里用一位表示一种功能.unsigned int可表示32种功能.
一种功能使用一位来表示。 但200种功能呢? 需要200位.但没有200位大小的类型.
这种情况可以用一个数组来表示, 如: unsigned int bits[7]来表示200位.

进一步的操作: 如需要使用第189种功能,意味着第189位需设1.

    int n = 189;
    bits[n/32] |= 1 << (n%32); // n/32算出第几个元素, n%32算出在那个元素里的第几位

linux内核也提供了位操作并且是原子操作的接口函数(注意内核里是以unsigned long的数组来表示多位的):

#define set_bit(nr,p)           ATOMIC_BITOP(set_bit,nr,p) //在p指向的数组上设第nr位为1
#define clear_bit(nr,p)         ATOMIC_BITOP(clear_bit,nr,p) //在p指向的数组上设第nr位为0
#define change_bit(nr,p)        ATOMIC_BITOP(change_bit,nr,p) //把第nr位的值相反
#define test_and_set_bit(nr,p)      ATOMIC_BITOP(test_and_set_bit,nr,p)
#define test_and_clear_bit(nr,p)    ATOMIC_BITOP(test_and_clear_bit,nr,p)
#define test_and_change_bit(nr,p)   ATOMIC_BITOP(test_and_change_bit,nr,p)

#define ATOMIC_BITOP(name,nr,p)         \
    (__builtin_constant_p(nr) ? ____atomic_##name(nr, p) : _##name(nr,p))

//bit表示第几位需要设1, p指向一个unsigned long的数组(表示很多位)
static inline void ____atomic_set_bit(unsigned int bit, volatile unsigned long *p)
{
    unsigned long flags;
    unsigned long mask = 1UL << (bit & 31); //   1 << (bit%32)     

    p += bit >> 5; // p[bit/32]

    raw_local_irq_save(flags); //关闭中断,并保存中断状态
    *p |= mask;  //设值
    raw_local_irq_restore(flags); //恢复中断状态和中断的开启
}

用法:

    unsigned long keys[24]; //表示768个按键
    set_bit(124, keys); //第124位设1


除此之外,内核里还有:

#define BIT(nr)         (1UL << (nr))
#define BIT_MASK(nr)        (1UL << ((nr) % BITS_PER_LONG)) //算出在数组元素里第几位设1
#define BIT_WORD(nr)        ((nr) / BITS_PER_LONG) //算出数组里第几个元素
#define BITS_PER_BYTE       8
#define BITS_TO_LONGS(nr)   DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))

用法:

    unsigned long keys[24]; //表示768个按键
    keys[BIT_WORD(124)] |= BIT_MASK(124); //第124位设1

你可能感兴趣的:(OrangePi,H3,Linux设备驱动开发,atomic-t,set-bit)