应用层程序
#include
#include
#include
#include
#include
#include
#include
#include
int fd;
void my_signal_fun(int signum)
{
unsigned char key_val;
read(fd, &key_val, 1);
printf("key_val: 0x%x\n", key_val);
}
int main(int argc, char **argv)
{
unsigned char key_val;
int ret;
int Oflags;
/* 注册信号处理函数 */
signal(SIGIO, my_signal_fun);
fd = open("/dev/buttons", O_RDWR);
if (fd < 0) {
printf("can't open!\n");
}
/* Set the process ID or process group ID that will receive SIGIO and
* SIGURG signals for events on file descriptor fd to the ID given in arg.
* A process ID is specified as a positive value; a process group ID is
* specified as a negative value. Most commonly, the calling process
* specifies itself as the owner (that is, arg is specified as getpid(2)).
* 设置这个进程会接受文将描述符fd发出到SIGIO和SIGURG信号
*/
fcntl(fd, F_SETOWN, getpid());
Oflags = fcntl(fd, F_GETFL);
/* 最终会调用驱动程序的 .fasync 函数(后面分析) */
fcntl(fd, F_SETFL, Oflags | FASYNC);
while (1) {
sleep(1000);
}
return 0;
}
下面主要先对 fcntl(fd, F_SETOWN, getpid());
这个调用进行分析
应用层调用 fcntl
来进行文件描述符相关操作,会调用内核对应的 sys_fcntl
函数
asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
{
struct file *filp;
long err = -EBADF;
filp = fget(fd);
if (!filp)
goto out;
/* 这个security_file_fcntl在不开启selinux时,默认操作是直接 return 0
* 主要关于安全方面的东西,可以忽视它
*/
err = security_file_fcntl(filp, cmd, arg);
if (err) {
fput(filp);
return err;
}
/* 主要工作在这个函数里面执行 */
err = do_fcntl(fd, cmd, arg, filp);
fput(filp);
out:
return err;
}
static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
struct file *filp)
{
long err = -EINVAL;
/* 根据 cmd 的类型,调用对应的 fcntl_cmd函数进行处理
*/
switch (cmd) {
...
case F_SETFL:
err = setfl(fd, filp, arg);
break;
case F_GETLK:
err = fcntl_getlk(filp, (struct flock __user *) arg);
break;
case F_SETLK:
case F_SETLKW:
err = fcntl_setlk(fd, filp, cmd, (struct flock __user *) arg);
break;
case F_GETOWN:
/*
* XXX If f_owner is a process group, the
* negative return value will get converted
* into an error. Oops. If we keep the
* current syscall conventions, the only way
* to fix this will be in libc.
*/
err = f_getown(filp);
force_successful_syscall_return();
break;
case F_SETOWN:
err = f_setown(filp, arg, 1);
break;
case F_GETSIG:
err = filp->f_owner.signum;
break;
...
default:
break;
}
return err;
}
我们主要关注的是 F_SETOWN
命令
case F_SETOWN:
/* 我们关注这个函数 */
err = f_setown(filp, arg, 1);
break;
/* arg为我们在应用层设置的 flags
* filp文件描述对应的文件对象指针
* force为1
*/
int f_setown(struct file *filp, unsigned long arg, int force)
{
enum pid_type type;
struct pid *pid;
int who = arg;
int result;
type = PIDTYPE_PID;
if (who < 0) {
type = PIDTYPE_PGID;
who = -who;
}
rcu_read_lock();
pid = find_pid(who);
/* 主要是这个函数 */
result = __f_setown(filp, pid, type, force);
rcu_read_unlock();
return result;
}
int __f_setown(struct file *filp, struct pid *pid, enum pid_type type,
int force)
{
int err;
err = security_file_set_fowner(filp);
if (err)
return err;
/* 继续往下分析 */
f_modown(filp, pid, type, current->uid, current->euid, force);
return 0;
}
/* pid 当前进程的pid
* type 为PIDTYPE_PGID
* uid 当前进程的uid
* euid 当前进程的euid
*/
static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
uid_t uid, uid_t euid, int force)
{
write_lock_irq(&filp->f_owner.lock);
if (force || !filp->f_owner.pid) {
put_pid(filp->f_owner.pid);
filp->f_owner.pid = get_pid(pid);
filp->f_owner.pid_type = type;
filp->f_owner.uid = uid;
filp->f_owner.euid = euid;
}
write_unlock_irq(&filp->f_owner.lock);
}
一路分析下来,我们可以知道最终 fcntl(fd, F_SETOWN, getpid())
会将当前进程的各种信息放入文件描述符的文件对象 file
中,这个为了后面驱动程序发送信号时,可以通过 filp->f_owner
中的信息找到应用程序。
下面我们再对 fcntl(fd, F_SETFL, Oflags | FASYNC)
进行分析,加入了一个 FASYNC
标志,那么 fcntl
最终会调用驱动程序的file_operations
中实现的 .fasync
函数。
在此之前,fcntl
根据 cmd
中指定的为 F_SETFL
由 sys_fcntl()
调用 setfl()
进入 setfl(fd, filp, arg)
下面对 setfl()
进行详细的分析。
static int setfl(int fd, struct file * filp, unsigned long arg)
{
struct inode * inode = filp->f_path.dentry->d_inode;
int error = 0;
/*
* O_APPEND cannot be cleared if the file is marked as append-only
* and the file is open for write.
*/
if (((arg ^ filp->f_flags) & O_APPEND) && IS_APPEND(inode))
return -EPERM;
/* O_NOATIME can only be set by the owner or superuser */
if ((arg & O_NOATIME) && !(filp->f_flags & O_NOATIME))
if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER))
return -EPERM;
/* required for strict SunOS emulation */
if (O_NONBLOCK != O_NDELAY)
if (arg & O_NDELAY)
arg |= O_NONBLOCK;
if (arg & O_DIRECT) {
if (!filp->f_mapping || !filp->f_mapping->a_ops ||
!filp->f_mapping->a_ops->direct_IO)
return -EINVAL;
}
if (filp->f_op && filp->f_op->check_flags)
error = filp->f_op->check_flags(arg);
if (error)
return error;
lock_kernel();
if ((arg ^ filp->f_flags) & FASYNC) {
if (filp->f_op && filp->f_op->fasync) {
/* 从这里可以看到,调用了fasync函数
* 那么控制权就交给的filp的驱动程序
*/
error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
if (error < 0)
goto out;
}
}
filp->f_flags = (arg & SETFL_MASK) | (filp->f_flags & ~SETFL_MASK);
out:
unlock_kernel();
return error;
} // end of setfl
/* 驱动程序实现的fasync函数,从这里可以看到,主要是调用了
* fasync_helper函数进行处理。
* button_async函数是一个fasync_struct的结构体,
* 可以暂时将其理解为一个存储信息的结构体(后面详细说明)
*/
static struct fasync_struct *button_async;
static int fifth_drv_fasync (int fd, struct file *filp, int on)
{
printk("driver: fifth_drv_fasync\n");
return fasync_helper (fd, filp, on, &button_async);
}
struct fasync_struct {
int magic;
int fa_fd;
struct fasync_struct *fa_next; /* singly linked list */
struct file *fa_file;
};
接下来,我们对 fasync_helper(fd, filp, on, &button_async)
进行分析。
/* on 用于表示是否开启异步模式,开启时传入为正值(一个True值)
* fapp 指向button_async结构体
*/
int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
{
struct fasync_struct *fa, **fp;
struct fasync_struct *new = NULL;
int result = 0;
if (on) {
/* 如果开启,则从内存中分配一块fasync_cache,用于存储button_async结构体 */
new = kmem_cache_alloc(fasync_cache, GFP_KERNEL);
if (!new)
return -ENOMEM;
}
write_lock_irq(&fasync_lock);
for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
if (fa->fa_file == filp) {
/* 如果文件描述符已经分配了存储空间,则将这次的fasync_cache进行释放 */
if(on) {
fa->fa_fd = fd;
kmem_cache_free(fasync_cache, new);
} else { /* 释放button_async的存储空间 */
*fp = fa->fa_next;
kmem_cache_free(fasync_cache, fa);
result = 1;
}
goto out;
}
}
/* 将数据存放进结构体中 */
if (on) {
new->magic = FASYNC_MAGIC;
new->fa_file = filp;
new->fa_fd = fd;
new->fa_next = *fapp;
*fapp = new;
result = 1;
}
out:
write_unlock_irq(&fasync_lock);
return result;
}
由 fifth_drv_fasync
和 fasync_helper
的代码,我们可以知道当应用程序设置一个 FASYNC
标志后,会在驱动程序创建一个 struct fasync_struct
的对象,那么这对象到底有什么用呢?这里我们就需要从中断触发后,执行的代码进行分析。
在韦东山的s3c2440开发板实现了一个驱动,主要关注是 kill_fasync()
函数。
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
struct pin_desc * pindesc = (struct pin_desc *)dev_id;
unsigned int pinval;
pinval = s3c2410_gpio_getpin(pindesc->pin);
if (pinval)
{
/* 松开 */
key_val = 0x80 | pindesc->key_val;
}
else
{
/* 按下 */
key_val = pindesc->key_val;
}
ev_press = 1; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
/* 当驱动发生后会执行buttons_irq函数,
* 在这里我们通过kill_fasync (&button_async, SIGIO, POLL_IN)
* 向应用程序发送一个SIGIO信号,要找到应用程序,
* 就需要用button_async中存储的信息
*/
kill_fasync (&button_async, SIGIO, POLL_IN);
return IRQ_RETVAL(IRQ_HANDLED);
}
void kill_fasync(struct fasync_struct **fp, int sig, int band)
{
/* First a quick test without locking: usually
* the list is empty.
*/
if (*fp) {
read_lock(&fasync_lock);
/* reread *fp after obtaining the lock */
__kill_fasync(*fp, sig, band); /* 主要是这个函数 */
read_unlock(&fasync_lock);
}
}
void __kill_fasync(struct fasync_struct *fa, int sig, int band)
{
while (fa) {
struct fown_struct * fown;
if (fa->magic != FASYNC_MAGIC) {
printk(KERN_ERR "kill_fasync: bad magic number in "
"fasync_struct!\n");
return;
}
/* 这个f_owner是我们调用fcntl(fd, F_SETOWN, getpid())
* 设置进文件对象fa_file中的
*/
fown = &fa->fa_file->f_owner;
/* Don't send SIGURG to processes which have not set a
queued signum: SIGURG has its own default signalling
mechanism. */
if (!(sig == SIGURG && fown->signum == 0))
send_sigio(fown, fa->fa_fd, band); /* 主要是这个函数 */
fa = fa->fa_next;
}
}
void send_sigio(struct fown_struct *fown, int fd, int band)
{
struct task_struct *p;
enum pid_type type;
struct pid *pid;
read_lock(&fown->lock);
type = fown->pid_type;
pid = fown->pid;
if (!pid)
goto out_unlock_fown;
read_lock(&tasklist_lock);
do_each_pid_task(pid, type, p) {
/* 从这里我们可以看到,send_sigio_to_task对进程发送了信号
* (内核用task来描述进程)
* 我们不难想象,这个信号会最终导致应用程序用signal注册的信号处理函数被调用
*/
send_sigio_to_task(p, fown, fd, band);
} while_each_pid_task(pid, type, p);
read_unlock(&tasklist_lock);
out_unlock_fown:
read_unlock(&fown->lock);
}
至此,我们就完成了对整个异步驱动程序执行流程的分析。
open
打开驱动程序创建设备时,内核会为这个进程创建一个文件描述符,用于在进程中修饰这个设备。fcntl(fd, F_SETOWN, getpid())
将文件描述符的 f_owner
设置为当前进程的信息。fcntl(fd, F_SETFL, Oflags | FASYNC)
最终会调用驱动程序的 .fasync
函数,这个函数中会创建一个 fasync_struct
的数据结构,用于存储文件描述信息。kill_fasync()
使用 fasync_struct
内存储的信息,向文件描述符所有者——应用程序发送信号,最终应用程序捕获这个信号,然后执行 signal
中注册的信号处理程序。驱动程序源码