Linux内核信号量和定时器问题请教:

时间:2022-04-23 23:30:05

正在学习用户态线程和内核态定时器的使用,有个问题请教大家:

我在用户空间建立了一个线程,内核做一个字符驱动模块,通过ioctl调用来不断读取内核定时器有没有

到期。在设备结构体里面加了一个信号量,在open调用的时候把信号量拿到,使用一个内核定时器,在内

核定时器到期的处理函数里面释放信号量,这样用户线程调ioctl的时候就获取不到信号量了,等定时器

超时了,就会释放信号量,这样ioctl就返回到用户空间了,可是下次再调用ioctl的时候就不等待信号量了,不知道为什么??

不知道这样做行不行,看书上好像都是两个内核线程间用信号量来通信,请大家指教,谢谢!

void timer_handle(void)
{
  int i = 0;
  mod_timer(&KDA_devp->s_timer,jiffies + HZ * 5);  /*重新改变定时器的超时时间*/      
  up(&KDA_devp->sem);  /*释放信号量,ioctl调用可以获取到该信号量*/

}

int KDA_open(struct inode *inode, struct file *filp)
{

struct KDA_dev *dev; /* device information */

dev = container_of(inode->i_cdev, struct KDA_dev, cdev);
filp->private_data = dev; /* for other methods */

 init_timer(&KDA_devp->s_timer);
 KDA_devp->s_timer.function = &timer_handle;
 KDA_devp->s_timer.expires = jiffies + HZ * 5;
 add_timer(&KDA_devp->s_timer); /*添加(注册)定时器*/
 down_interruptible(&dev->sem); /*获取信号量*/

return 0;

}


int KDA_ioctl(struct inode *inode, struct file *filp,
                 unsigned int cmd, unsigned long arg)
{
int err = 0;
int isr_num = 100;

struct KDA_dev *dev = filp->private_data; 

printk("want to get sem \n");

down_interruptible(&dev->sem);

switch(cmd)
{
case  1 :
printk("the case 1 \n");
break;

case 2 :
printk("the case 2 \n");
break;

case 3 :
        __put_user(KDA_devp->ISR_flag, (int __user *)arg);               
break;
default:
printk("the default case  \n");
}

}

6 个解决方案

#1


我个人觉得你这样做应该能实现你要的效果,但是逻辑上有点问题,理由如下:

1、信号量适用于进程间通信,而你这里实际上只有一个进程,即用户态创建的进程(你写成了线程)。这个进程和内核之间进行通过驱动接口进行数据交流,这个过程中并没有引入新的进程。其实简单化来看:这个模型的工作就是在进程里面先down()了一下,然后再up()一下,在down和up这段时间间隔内,轮询的查看信号量是否可用。但是信号量的真正作用确是用于进程间的通信,所以说你的效果应该能实现,但是逻辑上有点问题。

2、再分析下你这个模型,轮询的查看,这个很浪费CPU的资源。而根据你的设计,似乎用condition更合适一些。即先down()一下,然后用户态的进程就在那里阻塞着,什么时候定时器超时了,这个时候再产生一个condition,通知用户态的进程:我完事了,到你了。这时候用户态的进程再执行其他的操作。这样似乎效率更高一些。逻辑上也合理一些。

以上只是个人分析,未必全面,欢迎拍砖。

PS:这个确实是个挺不错的问题!

#2


   一般性思路: 应用程序调用驱动,通常在驱动里加入同步机制,例如用信号量加条件变量。
有数据需要应用程序处理时,驱动就唤醒应用程序,否则使应用程序睡眠(挂起)。这种异步的做法比较节省资源,比应用程序轮询高效得多。




#3


谢谢大家的回复,学习ing!

轮询确实很耗CPU,如果使用应用程序调用驱动的时候,要是不满足情况,应用程序就阻塞睡眠,等条件满足了,就由驱动唤醒应用程序,这样就比较好了。

要实现这个驱动唤醒应用程序,我需要看那些资料或者什么思路实现啊,谢谢大家!

#4


看看我去年写的一个手机平台,MP3编解码驱动: http://blog.csdn.net/wenxy1/archive/2008/10/30/3182116.aspx
这个,主要看你的悟性了。

#5


我看了Ldd3 里面的一个阻塞IO的例子:
ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
        printk(KERN_DEBUG "process %i (%s) going to sleep\n",
               current->pid, current->comm);
        wait_event_interruptible(wq, flag != 0);
        flag = 0;
        printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
        return 0; /* EOF */
}

“似乎用condition更合适一些。即先down()一下,然后用户态的进程就在那里阻塞着,什么时候定时器超时了,这个时候再产生一个condition,通知用户态的进程。”

这个是这个意思吗,呵呵,不太懂,请指教!
ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
        printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
               current->pid, current->comm);
        flag = 1;
        wake_up_interruptible(&wq);
        return count; /* succeed, to avoid retrial */

}
是不是使用这样的例子,在内核定时器的超时处理函数里面来唤醒wake_up_interruptible(&wq);这个就可以实现驱动通知应用程序了啊?


#6


谢谢fetag和wenxy1的回复,看了wenxy1程序,有看了Ldd3中阻塞IO的部分,现在可以实现在用户态调用就阻塞,等定时器到了,就让用户态醒来的功能了,谢谢大家,结贴给分!

#1


我个人觉得你这样做应该能实现你要的效果,但是逻辑上有点问题,理由如下:

1、信号量适用于进程间通信,而你这里实际上只有一个进程,即用户态创建的进程(你写成了线程)。这个进程和内核之间进行通过驱动接口进行数据交流,这个过程中并没有引入新的进程。其实简单化来看:这个模型的工作就是在进程里面先down()了一下,然后再up()一下,在down和up这段时间间隔内,轮询的查看信号量是否可用。但是信号量的真正作用确是用于进程间的通信,所以说你的效果应该能实现,但是逻辑上有点问题。

2、再分析下你这个模型,轮询的查看,这个很浪费CPU的资源。而根据你的设计,似乎用condition更合适一些。即先down()一下,然后用户态的进程就在那里阻塞着,什么时候定时器超时了,这个时候再产生一个condition,通知用户态的进程:我完事了,到你了。这时候用户态的进程再执行其他的操作。这样似乎效率更高一些。逻辑上也合理一些。

以上只是个人分析,未必全面,欢迎拍砖。

PS:这个确实是个挺不错的问题!

#2


   一般性思路: 应用程序调用驱动,通常在驱动里加入同步机制,例如用信号量加条件变量。
有数据需要应用程序处理时,驱动就唤醒应用程序,否则使应用程序睡眠(挂起)。这种异步的做法比较节省资源,比应用程序轮询高效得多。




#3


谢谢大家的回复,学习ing!

轮询确实很耗CPU,如果使用应用程序调用驱动的时候,要是不满足情况,应用程序就阻塞睡眠,等条件满足了,就由驱动唤醒应用程序,这样就比较好了。

要实现这个驱动唤醒应用程序,我需要看那些资料或者什么思路实现啊,谢谢大家!

#4


看看我去年写的一个手机平台,MP3编解码驱动: http://blog.csdn.net/wenxy1/archive/2008/10/30/3182116.aspx
这个,主要看你的悟性了。

#5


我看了Ldd3 里面的一个阻塞IO的例子:
ssize_t sleepy_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
        printk(KERN_DEBUG "process %i (%s) going to sleep\n",
               current->pid, current->comm);
        wait_event_interruptible(wq, flag != 0);
        flag = 0;
        printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
        return 0; /* EOF */
}

“似乎用condition更合适一些。即先down()一下,然后用户态的进程就在那里阻塞着,什么时候定时器超时了,这个时候再产生一个condition,通知用户态的进程。”

这个是这个意思吗,呵呵,不太懂,请指教!
ssize_t sleepy_write (struct file *filp, const char __user *buf, size_t count, loff_t *pos)
{
        printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
               current->pid, current->comm);
        flag = 1;
        wake_up_interruptible(&wq);
        return count; /* succeed, to avoid retrial */

}
是不是使用这样的例子,在内核定时器的超时处理函数里面来唤醒wake_up_interruptible(&wq);这个就可以实现驱动通知应用程序了啊?


#6


谢谢fetag和wenxy1的回复,看了wenxy1程序,有看了Ldd3中阻塞IO的部分,现在可以实现在用户态调用就阻塞,等定时器到了,就让用户态醒来的功能了,谢谢大家,结贴给分!