ioctl在linux系统中属于原子操作, 实际就是带锁的操作.
在写内核或驱动时,使用ioctl和应用层交互要比/proc来得安全的多.
以下是对ioctl原子性的代码分析:
1) do_ioctl, 用户层调用ioctl都会陷入到这里.
static long do_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int error = -ENOTTY;
if (!filp->f_op)
goto out;
/**
* kernel 里有unlocked ioctl, 不加锁的ioctl,
* kernel 会先check file_operations 里是否有不加锁ioctl,
* 有则优先调用
*/
if (filp->f_op->unlocked_ioctl) {
error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
if (error == -ENOIOCTLCMD)
error = -EINVAL;
goto out;
} else if (filp->f_op->ioctl) {
/* 一般情况调用到这里: */
/* lock_kernel 调用ioctl之前加锁 */
lock_kernel();
/* 调用ioctl */
error = filp->f_op->ioctl(filp->f_dentry->d_inode,
filp, cmd, arg);
/* 解锁 */
unlock_kernel();
}
out:
return error;
}
2) lock_kernel 看看如何加锁
static inline void __lock_kernel(void)
{
/**
* 禁止内核抢占, 实际上是增加当前线程结构中的
* 抢占记数 current_thread_info()->preempt_count
* 不一定能真正disable preempt
*/
preempt_disable();
if (unlikely(!_raw_spin_trylock(&kernel_flag))) {
/*
* If preemption was disabled even before this
* was called, there's nothing we can be polite
* about - just spin.
*/
/**
* 如果获得spin lock失败, 则检查抢占记数,
* 如果抢占被禁止, 则直接等待对kernel_flag加锁
*/
if (preempt_count() > 1) {
_raw_spin_lock(&kernel_flag);
return;
}
/*
* Otherwise, let's wait for the kernel lock
* with preemption enabled..
*/
/**
* 如果抢占未被禁止, 则减少抢占记数, 使能抢占,
* 等待kernel_flag锁被释放. 不过这里的等待可以被抢占
* 释放CPU给其他内核路径
*/
do {
preempt_enable();
while (spin_is_locked(&kernel_flag))
cpu_relax(); /* 可以被抢占 */
preempt_disable();
} while (!_raw_spin_trylock(&kernel_flag));
}
}