假如驱动里实现对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