先了解一下概念:
Linux内核同步机制API函数:宏:spin_lock_init ( )
在内核源码中的位置:Linux-2.6.30/include/linux/spinlock.h
宏定义格式:# define spin_lock_init(lock) \
do { *(lock) = SPIN_LOCK_UNLOCKED; } while (0)
宏功能描述:
宏spin_lock_init( ):初始化自旋锁lock,其实是将自旋锁指针lock 指向SPIN_LOCK_UNLOCKED宏,该宏的定义在内核文件linux-2.6.30/include/linux/spinlock_types.h中,它表示自旋锁的状态为未加锁。
何谓自旋锁?它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。
本案例为系统内所有子卡的公共数据,均为静态数据,包含子卡的ID、基本信息、连接信息以及与外面不同类型板卡相连是的不同setting.我们把每一类卡的这些信息,全部放在一个struct里,叫做card_info_t .
const card_info_t *card_info_A;
const card_info_t *card_info_B;
系统内所有的卡做成一个list:
const card_info_t **card_info_list;
这样就把所有卡的信息全部放到list里面了,从功能角度,先把客户需求完完全全处理好。
接下来,我们把用户的数据与kernel module 的结构联系起来,用一个全局的结构体:
typedef struct
kernel_glb_ {
int major;spinlock_t spinlock;
const card_info_t **card_info_list;
const card_info_t *card_info;
} kernel_glb_t;
static kernel_glb_t global;
kernel_glb_t * gbl = &global;
其中的spinlock自旋锁是kernel用来控制资源使用的;major是用来获取kernel module的符号设备的设备号的;
接下来我们还需要定义符号文件的操作API,在linux kernel里,定义了一个file_operations的结构体,用来处理字符设备的操作API,诸如:read/write/lock/unlock/ioctl等;这个里面的任何一个API,我们都可以自行写好,然后挂接到相应的指针上即可。
由于此系统数据比较多,不同类型子卡的数据又有很多种类,我们获取的内容组合也多,不是简单地读写文件,所以我们用out-of-band的ioctl来操作,通过传不同的命令码来实现获取不同的数据;现在ioctl已经不怎么用了,我们用unlocked_ioctl,具体他们之间是什么区别,可以参考一下回答:
ioctl() is one of the remaining parts of the kernel which runs under the Big Kernel Lock (BKL). In the past, the usage of the BKL has made it possible for long-running ioctl()methods to create long latencies for unrelated processes.
Follows an explanation of the patch that introduced unlocked_ioctl and compat_ioctl into 2.6.11. The removal of the ioctl field happened a lot later, in 2.6.36.
Explanation: When ioctl was executed, it took the Big Kernel Lock (BKL), so nothing else could execute at the same time. This is very bad on a multiprocessor machine, so there was a big effort to get rid of the BKL. First, unlocked_ioctl was introduced. It lets each driver writer choose what lock to use instead. This can be difficult, so there was a period of transition during which old drivers still worked (using ioctl) but new drivers could use the improved interface (unlocked_ioctl). Eventually all drivers were converted and ioctl could be removed.
compat_ioctl is actually unrelated, even though it was added at the same time. Its purpose is to allow 32-bit userland programs to make ioctl calls on a 64-bit kernel. The meaning of the last argument to ioctl depends on the driver, so there is no way to do a driver-independent conversion.
这里我们把挂在unlocked_ioctl的API叫做:card_data_ioctl;
long
card_data_ioctl (struct file *filep, unsigned int cmd, unsigned long params)
{
//用户空间拷贝数据到内核空间这里,这里用户的params和内核的P都是data_ioctl_t的类型,这个类型根据实际需求自己定义即可;
if (copy_from_user(&p, (void *) params, sizeof(data_ioctl_t)))
switch(cmd) {
case GET_CARD_ID:
get_card_id(p); break;
case GET_CARD_DATA:
get_card_DATA(p); break;
};
if (copy_to_user((void *) params, &p, sizeof(data_ioctl_t)));最后把数据给用户空间;
}
那么我们需要的 file_operations,就是这样定义的:
static struct file_operations fops = {
owner : THIS_MODULE,
unlocked_ioctl : card_data_ioctl,
};
最后我们在init函数里,就可以注册这个设备了,在linux里,注册的函数是这个:
int register_chrdev ( unsigned int major,
const char * name,
const struct file_operations * fops
);
int
data_init_module (void)
{
spin_lock_init(&gbl->spinlock);
if ((gbl->major = register_chrdev(gbl->major, KLM_NAME, &fops)) < 0) {
return (gbl->major);
}
return (0);
}
int
data_cleanup_module(void)
{
unregister_chrdev(gbl->major, KLM_NAME);
}
moudle_init(data_init_moudle);
module_exit(data_cleanup_moudle);
如何用这个kernel module:
用的方法就很简单了,直接调用到card_data_ioctl即可。一下就是一个例子:
if ((gbl->fd = open(KLM_NAME, O_RDWR)) == -1){}
if ((rc_ = ioctl(gbl->fd, GET_CARD_DATA, params_p_)) != 0) {}