linux驱动学习(4)--阻塞和非阻塞

发布时间:2024-08-14  

以串口的应用程序为例:

阻塞地都取串口一个字符

非阻塞地都取串口一个字符

char buf; 
fd = open('/dev/ttys',O_RDWR);
.. .. 
res = read(fd,&buf,1); //当串口上有输入时才返回 
if(res == 1) 

     printf('%cn',buf); 
}
char buf;
fd = open('/dev/ttys',O_RDWR | O_NONBLOCK);
.. .. 
while( read(fd,&buf,1) !=1); //当串口上无输入也返回,所
                                                //以要循环尝试读取串口
printf('%cn',buf);

现在我们有了阻塞的方式读取,那么阻塞的进程因为没有获得资源会进入休眠状态,现在就要聊聊有关唤醒的事了。在Linux设备驱动中,可以使用等待队列(wait queue)来实现阻塞进程的唤醒.等待队列能够用于实现内核中的异步事件通知机制。

 

Linux提供了有关等待队列的操作:

1)wait_queue_head_t my_queue; //定义等待队列头

2) init_waitqueue_head(&my_queue);   //初始化队列头

如果觉得上边两步来的麻烦,可以直接使用

DECLARE_WAIT_QUEUE_HEAD(name)   //定义并初始化

 

3) DECLARE_WAITQUEUE(name,tsk); //定义等待队列

4) void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);

    void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);

    用于将等待队列wait添加到等待队列头指向的等待队列链表中 。

5) wait_event(queue, conditon);

     wait_event_interruptible(queue, condition);  //可以被信号打断

     wait_event_timeout(queue, condition, timeout);

     wait_event_interruptible_timeout(queue, condition, timeout); //不能被信号打断

     queue:作为等待队列头的等待队列被唤醒

     conditon:必须满足,否则阻塞

     timeout和conditon相比,有更高优先级

6) void wake_up(wait_queue_head_t *queue);

     void wake_up_interruptible(wait_queue_head_t *queue);

     上述操作会唤醒以queue作为等待队列头的所有等待队列中所有属于该等待队列头的等待队列对应的进程。

7) sleep_on(wait_queue_head_t *q);

     interruptible_sleep_on(wait_queue_head_t *q);

     sleep_on作用是把目前进程的状态置成TASK_UNINTERRUPTIBLE,并定义一个等待队列,之后把他附属到等待队列头q,直到资源可用,q引导的等待队列被唤醒。interruptible_sleep_on作用是一样的, 只不过它把进程状态置为TASK_INTERRUPTIBLE.

    这两个函数的流程是首先,定义并初始化等待队列,把进程的状态置成TASK_UNINTERRUPTIBLE或TASK_INTERRUPTIBLE,并将对待队列添加到等待队列头。

然后通过schedule(放弃CPU,调度其他进程执行。最后,当进程被其他地方唤醒,将等待队列移除等待队列头。

    在Linux内核中,使用set_current_state()和__add_wait_queue()函数来实现目前进程状态的改变,直接使用current->state = TASK_UNINTERRUPTIBLE

类似的语句也是可以的。

    因此我们有时也可能在许多驱动中看到,它并不调用sleep_on或interruptible_sleep_on(),而是亲自进行进程的状态改变和切换。

  

    废话少说,直接上代码,实际代码很简单。


#include

#include

#include

#include

 

/*for spinlock and semaphore*/

#include

#include

#include

 

/*for task management*/

#include

#include

 

/*

 * xiaoyang yi @HIT 2011-9-25

 * char device driver with blocked testing

 */

 

MODULE_LICENSE('GPL');

 

static int MAJOR_NUM=0;

static struct semaphore sem;

static int global_var = 0;

static int global_var_count = 0;

static spinlock_t spin;

/*waiting queue for kernel*/

static wait_queue_head_t wqueue;

static int flag = 0;

 

static ssize_t globalvar_read(struct file*,char*,size_t, loff_t*);

static ssize_t globalvar_write(struct file*,const char*,size_t, loff_t*);

static int globalvar_open(struct inode*node, struct file* fp);

static int globalvar_release(struct inode*node, struct file* fp);

 

/*init the file_operation structure*/

static struct file_operations globalvar_fpos={

    .read = globalvar_read,

    .write = globalvar_write,

    .open = globalvar_open,

    .release = globalvar_release,

};

 

static int __init globalvar_init(void)

{

    int ret;

 

    printk('register globalvar:[blocked testing]');

    /*register device drivre*/

    ret = register_chrdev(MAJOR_NUM,'globalvar',&globalvar_fpos);

    if(ret < 0){

        printk('globalvar reg failed!n');

    }else{

        spin_lock_init(&spin);

    }

     

    if(MAJOR_NUM == 0){

        MAJOR_NUM = ret;

    }

 

    sema_init(&sem,1);

    init_waitqueue_head(&wqueue);

    return ret;

}

 

static void __exit globalvar_exit()

{

    unregister_chrdev(MAJOR_NUM,'globalvar');

}

 

static ssize_t globalvar_read(struct file* fp, char* buf, size_t len, loff_t* off)

{

    /*wait until condition become true*/

    if( wait_event_interruptible(wqueue,flag!=0) ){

        return -ERESTARTSYS;

    }

 

    /*get semaphore*/

    if(down_interruptible(&sem)){

        return -ERESTARTSYS;

    }

     

    /*copy from kernel to user space*/

    if(copy_to_user(buf,&global_var,sizeof(int)) != 0){

        /*release semaphore*/

        up(&sem);

        return -EFAULT;

    }

    /*data unaccessible flag*/

    flag = 0;

     

    /*release semaphore*/

    up(&sem);

    return sizeof(int);

}

 

static ssize_t globalvar_write(struct file* fs,const char* buf, size_t len, loff_t* off)

{

    /*get semaphore*/

    if(down_interruptible(&sem)){

        return -ERESTARTSYS;

    }

     

    printk('down_interruptible ok!n');

    if(copy_from_user(&global_var,buf,sizeof(int) != 0)){

        /*release semaphore*/

        up(&sem);

        return -EFAULT;

    }

 

    /*release semaphore*/

    up(&sem);

 

    /*data ready*/

    flag = 1;

    /*wake up the waiting task*/

    wake_up_interruptible(&wqueue);

    return sizeof(int);

}

 

/*

 * open device with checking busy.

 * if busy,count++;else return 0

 */

static int globalvar_open(struct inode*node, struct file* fp)

{

    /*get spinlock*/

    //spin_lock(&spin);

     

    /*reach criticle section*/

    //if(global_var_count){

        //spin_unlock(&spin);

        //printk('[debug]:globalvar open fialed!n');

        //return -EBUSY;

    //}

     

    /*release spinlock*/

    global_var_count++;

     

    //spin_unlock(&spin);

    return 0;

}

 

static int globalvar_release(struct inode*node, struct file* fp)

{

    //spin_lock(&spin);

    global_var_count--;

    //spin_unlock(&spin);

    return 0;

}

 

/*module setting*/

module_init(globalvar_init);

module_exit(globalvar_exit);

 

/*this is end of file*/

这里的信号量和wait_queue操作要注意顺序,否则可能形成死锁。


如:


//对于读

 

down(sem)

 

wait_queue(...)

 

up(sem)

 

//对于写:

 

down(sem)

 

up(sem)

 

wake_up_queue(..)

      这种情况下可能读进程永远在wait_queue里睡眠,而写进程由于无法得到sem永远不会执行wake_up...  


      ....


      下面是测试用的应用程序,包括了读写两个分支。


    


#include

#include

#include

#include

#include

 

/*

 * xiaoyang yi @HIT 2011.9.24

 * this is a test for char device 'globalvar'

 */

 

int main(int argc, char** args) {

    int fd, num;

 

    if (argc >= 2) {

        if (strcmp(args[1], '0') == 0) {

            printf('mode read!n');

            /*opemn device*/

            fd = open('/dev/globalvar', O_RDWR, S_IRUSR | S_IWUSR);

            if (fd != -1) {

                while (1) {

                    read(fd, &num, sizeof(int));

                    printf('globalvar=%dn',num);

                    if (num == 0) {

                        close(fd);

                        break;

                    }

                }//while

            } else {

                printf('error:device open error!n');

            }

        } else if (strcmp(args[1], '1') == 0) {

            printf('mode write!n');

 

            /*opemn device*/

            fd = open('/dev/globalvar', O_RDWR, S_IRUSR | S_IWUSR);

            if (fd != -1) {

                while (1) {

                    /*writing test*/

                    printf('print number to write: ');

                    scanf('%d', &num);

                    write(fd, &num, sizeof(int));

                    if (num == 0) {

                        close(fd);

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>