zoukankan      html  css  js  c++  java
  • 死锁

        死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象。死锁要产生必须具备四个必要条件:1. 互斥条件 2. 请求和保持条件 3.不可剥夺条件  4. 环路等待条件。由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象死锁。

    一下举一个Linux环境下产生死锁的程序(首先是驱动部分):

    #include <linux/module.h> 
    #include <linux/init.h> 
    #include <linux/fs.h> 
    #include <asm/uaccess.h> 
    #include <linux/wait.h> 
    #include <linux/semaphore.h> 
    #include <linux/device.h>
    #include <linux/cdev.h>
    #include <linux/sched.h>
     
    MODULE_LICENSE("GPL"); 
    
    #define init_MUTEX(LOCKNAME) sema_init(LOCKNAME,1)
    
     
    #define DEVICE_NAME  "CDEV_ZHU"
    static struct class *cdev_class; 
    
    struct cdev  dev_c;
    dev_t  dev;
     
    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*); 
     
    struct file_operations globalvar_fops = 
    { 
        read: globalvar_read, write: globalvar_write, 
    }; 
     
    static int global_var = 0; 
    static struct semaphore sem; 
    static wait_queue_head_t outq; 
    static int flag = 0; 
     
    static int __init globalvar_init(void) 
    { 
        int ret,err; 
        ret = alloc_chrdev_region(&dev,0,1,DEVICE_NAME) ;
        if (ret) 
        { 
            printk("globalvar register failure"); 
        } 
        else 
        { 
            
            cdev_init(&dev_c,&globalvar_fops);
            
            err = cdev_add(&dev_c,dev,1);
            
            if(err)
            {
                printk(KERN_NOTICE "error %d adding FC_dev\n",err);
                unregister_chrdev_region(dev, 1);
                return err;
            }
            else
            {
                printk("device register success! \n");
            }
            
            cdev_class = class_create(THIS_MODULE,DEVICE_NAME);
            if(IS_ERR(cdev_class))
            {
                printk("ERR:cannot create a cdev_class\n");  
                unregister_chrdev_region(dev, 1);
                return -1;
            }
            device_create(cdev_class, NULL, dev, 0, DEVICE_NAME);
            
            init_MUTEX(&sem); 
            init_waitqueue_head(&outq); 
        } 
        return ret; 
    }  
    
     
    static void __exit globalvar_exit(void) 
    { 
        device_destroy(cdev_class,dev);
        class_destroy(cdev_class);
        unregister_chrdev_region(dev,1);
        printk("globalvar exit \n");
    } 
     
     
    static ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff_t *off) 
    { 
        
       /* 
    //正常情况下这里不应该注释,顺序应该是先wait_event_interruptible,再down_interruptible才不会导致死锁
    if (wait_event_interruptible(outq, flag != 0)) 
        { 
            return    - ERESTARTSYS; 
        } 
       */
        if (down_interruptible(&sem)) 
        { 
            return    - ERESTARTSYS; 
        } 
    
    /*
    交换了 down_interruptible 和 wait_event_interruptible 会造成死锁通过添加打印语句,可以发现会打印 “size semaphore”其它的像“wake up”, 
    global_write()函数中的”write_down”和”waking up” 都不会打印,说明在up(&sem)之后 global_read()会立刻获取该信号量,然后进入睡眠。
    */ printk(“size semaphore \n”); if (wait_event_interruptible(outq, flag != 0)) { return - ERESTARTSYS; } printk("wake up !\n"); flag = 0; if (copy_to_user(buf, &global_var, sizeof(int))) { up(&sem); return - EFAULT; } up(&sem); return sizeof(int); } static ssize_t globalvar_write(struct file *filp, const char *buf, size_t len, loff_t *off) { if (down_interruptible(&sem)) { return - ERESTARTSYS; } if (copy_from_user(&global_var, buf, sizeof(int))) { up(&sem); return - EFAULT; } up(&sem); flag = 1; printk("write done!\n"); wake_up_interruptible(&outq); printk("waking up \n"); return sizeof(int); } module_init(globalvar_init); module_exit(globalvar_exit);

    说明:该驱动和前一篇文章“Linux字符驱动中动态分配设备号与动态生成设备节点”中的效果是相同的,只不过这里使用了信号量对globalvar_write 和 globalvar_read函数进行了阻塞控制,当上层应用程序写入驱动改变globalvar之后,读操作才可以进行。

    上面代码为什么会出现死锁呢,可以查阅内核中的up()函数的定义:

    /*  /kernel/semaphore.c  */
    void up(struct semaphore *sem)
    {
        unsigned long flags;
    
        spin_lock_irqsave(&sem->lock, flags);
        if (likely(list_empty(&sem->wait_list)))
            sem->count++;
        else
            __up(sem);
        spin_unlock_irqrestore(&sem->lock, flags);
    }
    
    static noinline void __sched   __up(struct semaphore *sem)
    {
        struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
                            struct semaphore_waiter, list);
        list_del(&waiter->list);
        waiter->up = 1;
        wake_up_process(waiter->task);
    }

    由 __up() 函数中最后执行了wake_up_process() 唤醒另外一个进程,所以globalvar_write()函数中在up()后面的语句都没有执行,于是flag的值还是为0,当然globalvar_write()后面的打印语句也不会执行。当唤醒正在操作globalvar_read()函数的进程之后,globalvar_read()开始执行,globalvar_read() 立刻持有信号量sem,由于此时flag的值为0,wait_event_interruptible()将导致进程进入睡眠,此时就产生了死锁——该进程持有信号量睡眠后将不能释放该信号量。就会导致globalvar_write()函数得不到执行,自然flag的值也不会变为1,globalvar_read()也不会醒,因此产生了死锁。(后面会有文章介绍wait_event_interruptible的原理)

    注意到list_first_entry()这个函数可以获得链表的第一个元素,在内核中这是非常经典的一个东东,分析内核必会。后面还会专门介绍内核中的list.

    下面给出应用程序:

    首先是globalvar_read.c

    View Code
    #include <sys/types.h> 
    #include <sys/stat.h> 
    #include <stdio.h> 
    #include <fcntl.h> 
    int main() 
    { 
        int fd, num; 
     
        fd = open("/dev/CDEV_ZHU", O_RDWR, S_IRUSR | S_IWUSR); 
        if (fd !=    - 1) 
        { 
            while (1) 
            { 
                read(fd, &num, sizeof(int)); //程序将阻塞在此语句,除非有针对 globalvar 的输入 
                printf("The globalvar is %d\n", num); 
     
                //如果输入是 0,则退出 
                if (num == 0) 
                { 
                    close(fd); 
                    break;             } 
            } 
        } 
        else 
        { 
            printf("device open failure\n"); 
        } 
        return 0;
    } 

    然后是globalvar_write.c

    View Code
    #include <sys/types.h> 
    #include <sys/stat.h> 
    #include <stdio.h> 
    #include <fcntl.h> 
    int main() 
    { 
        int fd, num; 
     
        fd = open("/dev/CDEV_ZHU", O_RDWR, S_IRUSR | S_IWUSR); 
        if (fd != -1) 
        { 
            while (1) 
            { 
                printf("Please input the globalvar:\n"); 
                scanf("%d", &num); 
                write(fd, &num, sizeof(int)); 
     
                //如果输入 0,退出 
                if (num == 0) 
                { 
                    close(fd); 
                    break; 
                } 
            } 
        } 
        else 
        { 
            printf("device open failure\n"); 
        } 
        return 0;
    } 

    使用这两个程序可以互斥的对驱动程序中的globalvar进行读写,但是注意驱动中globalvar_read()函数中,代码需要改为先wait_event_interruptible 再 down_interruptible()。

  • 相关阅读:
    Codeforces 787D. Legacy 线段树优化建图+最短路
    Codeforces 1051E. Vasya and Big Integers
    BZOJ3261 最大异或和
    BZOJ3531 SDOI2014 旅行
    洛谷P2468 SDOI 2010 粟粟的书架
    2018 ICPC 焦作网络赛 E.Jiu Yuan Wants to Eat
    HDU6280 From Tree to Graph
    HDU5985 Lucky Coins 概率dp
    (HDU)1334 -- Perfect Cubes (完美立方)
    (HDU)1330 -- Deck (覆盖物)
  • 原文地址:https://www.cnblogs.com/zhuyp1015/p/2524730.html
Copyright © 2011-2022 走看看