(linux自学笔记)linux驱动并发控制、阻塞/非阻塞IO、异步通知

 原文地址:

http://www.cnblogs.com/hebaichuanyeah/

1.关于并发控制

Linux 中多个进程对共享资源的并发访问,并发访问会导致竞态。

  在单 CPU范围内避免竞态的一种方法是在进入临界区之前屏蔽系统的中断。CPU一般都具备屏蔽中断和打开中断的功能,中断屏蔽将使得中断与进程之间的并发不再发生,由于Linux 内核的进程调度等操作都依赖中断来实现,内核抢占进程之间的并发也就得以避免了。

local_irq_disable() //屏蔽中断
...
critical section //临界区
...
local_irq_enable() //开中断

原子操作

原子操作指的是在执行过程中不会被别的代码路径所中断的操作。

原子操作驱动

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h> /* copy_to_user,copy_from_user */
#include <linux/miscdevice.h>
#include <linux/pci.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank-m.h>
#include <plat/gpio-cfg.h>

#define LED_MAJOR 240
//原子变量
atomic_t atomic_led = ATOMIC_INIT(0);


int led_open (struct inode *inode,struct file *filp)
{
    unsigned tmp;
    //减1兼测试
    if (!atomic_dec_and_test(&atomic_led))
    {
        //加1
        atomic_inc(&atomic_led);
        return -1;
    }


    tmp = readl(S3C64XX_GPMDAT);
    tmp &= 0xffff0000;
    tmp |= 0x00001111;
    writel(tmp, S3C64XX_GPMCON);
    writel(0xffffffff, S3C64XX_GPMDAT);
    printk("<0>open led
");
    return 0;
}

ssize_t led_read (struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
    return count;
}


ssize_t led_write (struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
    char wbuf[1];
    unsigned tmp;

    copy_from_user(wbuf,buf,count);

    tmp = readl(S3C64XX_GPMDAT);
    tmp &= 0xfffffff0;
    tmp |= wbuf[0];
    writel(tmp, S3C64XX_GPMDAT);

    return count;
}

int led_release (struct inode *inode, struct file *filp)
{
    atomic_inc(&atomic_led);

    return 0;
}


struct file_operations led_fops ={
    .owner = THIS_MODULE,
    .open = led_open,
    .read = led_read,
    .write = led_write,
    .release = led_release,

};

int __init led_init (void)
{
    int rc;
    printk ("<0>led init
");
    rc = register_chrdev(LED_MAJOR,"led",&led_fops);
    if (rc <0)
    {
        printk ("<0>register %s char dev error
","led");
        return -1;
    }

    //atomic_t atomic_led = ATOMIC_INIT(0);
    //原子变量置1
    atomic_set(&atomic_led, 1);
    printk ("<0>success
");
    return 0;
}

void __exit led_exit (void)
{
    unregister_chrdev(LED_MAJOR,"led");
    printk ("<0>module exit
");
    return ;
}

module_init(led_init);
module_exit(led_exit);

测试程序

#include <fcntl.h>
#include "stdio.h"
#include "pthread.h"
#include "stdlib.h"

void thread_one(void)
{
    int fd,count;
    char buf[]={~0x01,~0x02,~0x04,~0x08,0x00,0xff};


    while((fd = open("/dev/my_led",O_RDWR))<0)
    {
        printf ("pthread_one:Open /dev/my_led file error
");
        //pthread_exit(0);
        sleep(1);
    }
    for(count=0;count<3;count++)
    {
        write(fd,&buf[0],1);
        sleep(1);
        write(fd,&buf[5],1);
        sleep(1);
    }
    close (fd);
}

void thread_two(void)
{
    int fd,count;
    char buf[]={~0x01,~0x02,~0x04,~0x08,0x00,0xff};


    while ((fd = open("/dev/my_led",O_RDWR)) < 0)
    {
        printf ("pthread_two:Open /dev/my_led file error
");
        //pthread_exit(0);
    sleep(1);
    }
    for(count=0;count<3;count++)
    {
        write(fd,&buf[1],1);
        sleep(1);
        write(fd,&buf[5],1);
        sleep(1);
    }
    close(fd);
}

main()
{
    pthread_t thread_one_id,thread_two_id;
    int resurt;
    pthread_attr_t thread_one_attr,thread_two_attr;
    int thread_one_priority,thread_two_priority;



    thread_one_priority = 11;
    thread_two_priority = 11;

    pthread_attr_init(&thread_one_attr);
    pthread_attr_init(&thread_two_attr);

    //设置为指定调度算法
    pthread_attr_setinheritsched(&thread_one_attr, PTHREAD_EXPLICIT_SCHED);
    pthread_attr_setinheritsched(&thread_two_attr, PTHREAD_EXPLICIT_SCHED);

    //指定调度算法
    pthread_attr_setschedpolicy(&thread_one_attr, SCHED_RR);
    pthread_attr_setschedpolicy(&thread_two_attr, SCHED_RR);

    pthread_attr_setschedparam(&thread_one_attr, (struct sched_param *)&thread_one_priority);

    pthread_attr_setschedparam(&thread_two_attr, (struct sched_param *)&thread_two_priority);

    pthread_attr_setdetachstate(&thread_one_attr,PTHREAD_CREATE_JOINABLE);
    //sleep(1);
    if((resurt = pthread_create(&thread_one_id, &thread_one_attr, (void *)thread_one, NULL)) == -1)
    {
        perror("ss");
        printf("thread one creat error
 %d",resurt);
        exit(1);
    }


    if((resurt = pthread_create(&thread_two_id, &thread_two_attr, (void *)thread_two, NULL))== -1)
    {
        printf("thread two creat error
");
        exit(1);
    }



    //sleep(1);
    pthread_join(thread_one_id, NULL);
    pthread_join(thread_two_id, NULL);
}

运行结果

 自旋锁

  自旋锁是一种对临界资源进行互斥手访问的典型手段。为了获得一个自旋锁,在某CPU 上运行的代码需先执行一个原子操作,该操作测试并设置(test-and-set)某个内存变量,由于它是原子操作,所以在该操作完成之前其他执行单元不可能访问这个内存变量。

自旋锁保护临界段

    
    spinlock(&lock_led);
    if (count)/*已经打开*/
    {
        spin_unlock(&lock_led);
        return -1;
    }

    spin_unlock(&lock_led);

信号量

//获得信号量

void down(struct semaphore * sem);

//释放信号量

void up(struct semaphore * sem);

 2 .关于阻塞/非阻塞IO

阻塞和非阻塞I/O 是设备访问的两种不同模式,驱动程序可以灵活地支持用户空间对设备的这两种访问方式。

  阻塞操作是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足。而非阻塞操作的进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。

阻塞一个线程,直至另一个线程运行完。

include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h> /* copy_to_user,copy_from_user */
#include <linux/miscdevice.h>
#include <linux/pci.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank-m.h>
#include <plat/gpio-cfg.h>
#include <linux/wait.h>
#include <linux/sched.h>

#define LED_MAJOR 240

struct my_queue
{
    //定义等待列对头
    wait_queue_head_t queue_head;
    //定义自旋锁
    spinlock_t lock;
    //文件打开标志
    int file_flag;
}led_queue;


int led_open (struct inode *inode,struct file *filp)
{
    unsigned tmp;
    struct my_queue * dev = &led_queue;
    //自旋锁 临界段
    spin_lock(&dev->lock);

    //定义等待列对
    DECLARE_WAITQUEUE(wait,current);
    //进入等待列对头
    add_wait_queue(&dev->queue_head, &wait);
    dev->file_flag--;
    spin_unlock(&dev->lock);
    while(dev->file_flag < 0)
    {
        //改变进程状态
         __set_current_state(TASK_INTERRUPTIBLE);
        //
        schedule();
    }

    spin_lock(&dev->lock);
    dev->file_flag = 0;
    spin_unlock(&dev->lock);


    tmp = readl(S3C64XX_GPMDAT);
    tmp &= 0xffff0000;
    tmp |= 0x00001111;
    writel(tmp, S3C64XX_GPMCON);
    writel(0xffffffff, S3C64XX_GPMDAT);
    printk("<0>open led
");
    //移除
    remove_wait_queue(&dev->queue_head, &wait);
    return 0;
}

ssize_t led_read (struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
    return count;
}


ssize_t led_write (struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
    char wbuf[1];
    unsigned tmp;

    copy_from_user(wbuf,buf,count);

    tmp = readl(S3C64XX_GPMDAT);
    tmp &= 0xfffffff0;
    tmp |= wbuf[0];
    writel(tmp, S3C64XX_GPMDAT);
    return count;
}

int led_release (struct inode *inode, struct file *filp)
{
    struct my_queue * dev = &led_queue;
    spin_lock(&dev->lock);
    dev->file_flag = 1;
    spin_unlock(&dev->lock);

    wake_up_interruptible(&dev->queue_head);

    return 0;
}


struct file_operations led_fops ={
    .owner = THIS_MODULE,
    .open = led_open,
    .read = led_read,
    .write = led_write,
    .release = led_release,

};

int __init led_init (void)
{
    int rc;
    struct my_queue * dev = &led_queue;

    printk ("<0>led init
");
    rc = register_chrdev(LED_MAJOR,"led",&led_fops);
    if (rc <0)
    {
        printk ("<0>register %s char dev error
","led");
        return -1;
    }
    //初始化列对头
    init_waitqueue_head(&dev->queue_head);
    //初始化自旋锁
    spin_lock_init(&dev->lock);
    //文件打开标志
    dev->file_flag = 1;
    printk ("<0>success
");
    return 0;
}

void __exit led_exit (void)
{
    unregister_chrdev(LED_MAJOR,"led");
    printk ("<0>module exit
");
    return ;
}

module_init(led_init);
module_exit(led_exit);

非阻塞,poll轮循

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h> /* copy_to_user,copy_from_user */
#include <linux/miscdevice.h>
#include <linux/pci.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank-m.h>
#include <plat/gpio-cfg.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>

#define LED_MAJOR 240

struct drive_str
{
    //定义等待列对头
    wait_queue_head_t queue_head;
    //定义自旋锁
    spinlock_t lock;
    //文件打开标志
    int file_flag;
}led_driver;


int led_open (struct inode *inode,struct file *filp)
{
    unsigned tmp;


    tmp = readl(S3C64XX_GPMDAT);
    tmp &= 0xffff0000;
    tmp |= 0x00001111;
    writel(tmp, S3C64XX_GPMCON);
    writel(0xffffffff, S3C64XX_GPMDAT);
    printk("<0>open led
");
    //移除
    return 0;
}

ssize_t led_read (struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{
    struct drive_str * dev = &led_driver;
    //自旋锁 临界段
    spin_lock(&dev->lock);
    if(dev->file_flag == 0)
        return (0);

    dev->file_flag = 0;
    spin_unlock(&dev->lock);
    return (1);
}


ssize_t led_write (struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
    char wbuf[1];
    unsigned tmp;



    copy_from_user(wbuf,buf,count);

    tmp = readl(S3C64XX_GPMDAT);
    tmp &= 0xfffffff0;
    tmp |= wbuf[0];
    writel(tmp, S3C64XX_GPMDAT);
    return count;
}

int led_release (struct inode *inode, struct file *filp)
{
    struct drive_str * dev = &led_driver;

    spin_lock(&dev->lock);
    dev->file_flag = 1;
    spin_unlock(&dev->lock);

    return 0;
}

static unsigned int led_poll(struct file * filp,poll_table *wait)
{
    unsigned int mask = 0;
    struct drive_str *dev = &led_driver;

    poll_wait(filp, &dev->queue_head,wait);

    if(dev->file_flag)
    {
        mask = 1;
    }

    return (mask);
}


struct file_operations led_fops ={
    .owner = THIS_MODULE,
    .open = led_open,
    .read = led_read,
    .write = led_write,
    .release = led_release,
    .poll = led_poll,

};

int __init led_init (void)
{
    int rc;
    struct drive_str * dev = &led_driver;

    printk ("<0>led init
");
    rc = register_chrdev(LED_MAJOR,"led",&led_fops);
    if (rc <0)
    {
        printk ("<0>register %s char dev error
","led");
        return -1;
    }
    //初始化列对头
    init_waitqueue_head(&dev->queue_head);
    //初始化自旋锁
    spin_lock_init(&dev->lock);
    //文件打开标志
    dev->file_flag = 1;
    printk ("<0>success
");
    return 0;
}

void __exit led_exit (void)
{
    unregister_chrdev(LED_MAJOR,"led");
    printk ("<0>module exit
");
    return ;
}

module_init(led_init);
module_exit(led_exit);

测试程序

#include "stdio.h"
#include "pthread.h"
#include "stdlib.h"
#include <fcntl.h>
#include "poll.h"

void thread_one(void)
{
    int fd,count,ret;
    char buf[]={~0x01,~0x02,~0x04,~0x08,0x00,0xff};
    struct pollfd fds[1];

    fd = open("/dev/my_led",O_RDWR);
    fds[0].fd = fd;
    fds[0].events = POLLIN;


    if (fd < 0)
    {
        printf ("Open /dev/my_led file error
");
        pthread_exit(0);
    }
    if((ret = read(fd,&buf[1],1)) == 0)
    {
        ret = poll(fds,1,10000);
        if(ret == 0)
        {
            printf("time out
");
        printf("%u
",ret);
            pthread_exit(0);

        }

    }

    for(count=0;count<3;count++)
    {
        write(fd,&buf[0],1);
        sleep(1);
        write(fd,&buf[5],1);
        sleep(1);
    }
    close (fd);
}

void thread_two(void)
{
    int fd,count,ret;
    char buf[]={~0x01,~0x02,~0x04,~0x08,0x00,0xff};
    struct pollfd fds[1];



    fd = open("/dev/my_led",O_RDWR);
    fds[0].fd = fd;
    fds[0].events = POLLIN;
    if (fd == 1)
    {
        printf ("open /dev/my_led file error
");
        pthread_exit(0);
    }

    if((ret = read(fd,&buf[1],1)) == 0)
    {
        ret = poll(fds,1,10000);
        if(ret == 0)
        {
            printf("time out
");
        printf("%u
",ret);
            pthread_exit(0);

        }

    }
    for(count=0;count<3;count++)
    {
        write(fd,&buf[1],1);
        sleep(1);
        write(fd,&buf[5],1);
        sleep(1);
    }
    close(fd);
}

main()
{
    pthread_t thread_one_id,thread_two_id;
    int resurt;
    pthread_attr_t thread_one_attr,thread_two_attr;
    int thread_one_priority,thread_two_priority;



    thread_one_priority = 11;
    thread_two_priority = 11;

    pthread_attr_init(&thread_one_attr);
    pthread_attr_init(&thread_two_attr);

    //设置为指定调度算法
    pthread_attr_setinheritsched(&thread_one_attr, PTHREAD_EXPLICIT_SCHED);
    pthread_attr_setinheritsched(&thread_two_attr, PTHREAD_EXPLICIT_SCHED);

    //指定调度算法
    pthread_attr_setschedpolicy(&thread_one_attr, SCHED_RR);
    pthread_attr_setschedpolicy(&thread_two_attr, SCHED_RR);

    pthread_attr_setschedparam(&thread_one_attr, (struct sched_param *)&thread_one_priority);

    pthread_attr_setschedparam(&thread_two_attr, (struct sched_param *)&thread_two_priority);

    pthread_attr_setdetachstate(&thread_one_attr,PTHREAD_CREATE_JOINABLE);
    //sleep(1);
    if((resurt = pthread_create(&thread_one_id, &thread_one_attr, (void *)thread_one, NULL)) == -1)
    {
        printf("thread one creat error
 %d",resurt);
        exit(1);
    }


    if((resurt = pthread_create(&thread_two_id, &thread_two_attr, (void *)thread_two, NULL))== -1)
    {
        printf("thread two creat error
");
        exit(1);
    }



    //sleep(1);
    pthread_join(thread_one_id, NULL);
    pthread_join(thread_two_id, NULL);
}

3.异步通知

使用信号进行进程间通信是UNIX 系统中的一种传统机制。在Linux系统中,异步通知使用信号来实现。

使用异步通知,一个进程结束后发信号给另一个进程

驱动程序:

include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h> /* copy_to_user,copy_from_user */
#include <linux/miscdevice.h>
#include <linux/pci.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank-m.h>
#include <plat/gpio-cfg.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <asm/signal.h>
#include <asm-generic/siginfo.h>

#define LED_MAJOR 240

struct drive_str
{
    struct fasync_struct * async
}led_driver;



static int led_open (struct inode *inode,struct file *filp)
{
    unsigned tmp;


    tmp = readl(S3C64XX_GPMDAT);
    tmp &= 0xffff0000;
    tmp |= 0x00001111;
    writel(tmp, S3C64XX_GPMCON);
    writel(0xffffffff, S3C64XX_GPMDAT);
    printk("<0>open led
");

    return 0;
}

static ssize_t led_read (struct file *filp, char __user *buf, size_t count,loff_t *f_pos)
{

    return (count);
}


static ssize_t led_write (struct file *filp, const char __user *buf, size_t count,loff_t *f_pos)
{
    char wbuf[1];
    unsigned tmp;

    copy_from_user(wbuf,buf,count);

    tmp = readl(S3C64XX_GPMDAT);
    tmp &= 0xfffffff0;
    tmp |= wbuf[0];
    writel(tmp, S3C64XX_GPMDAT);
    return count;
}

static int led_fasync(int fd,struct file * filp,int mode)
{
    struct drive_str * dev = &led_driver;
    return (fasync_helper(fd,filp,mode,&dev->async));
}

static int led_release (struct inode *inode, struct file *filp)
{
    struct drive_str * dev = &led_driver;


    kill_fasync(&dev->async,SIGIO, POLL_IN);

    return (0);

}



struct file_operations led_fops ={
    .owner = THIS_MODULE,
    .open = led_open,
    .read = led_read,
    .write = led_write,
    .release = led_release,
    .fasync = led_fasync,

};

static int __init led_init (void)
{
    int rc;
    struct drive_str * dev = &led_driver;

    printk ("<0>led init
");
    rc = register_chrdev(LED_MAJOR,"led",&led_fops);
    if (rc <0)
    {
        printk ("<0>register %s char dev error
","led");
        return -1;
    }

    dev->async = NULL;
    printk ("<0>success
");
    return 0;
}

static void __exit led_exit (void)
{
    unregister_chrdev(LED_MAJOR,"led");
    printk ("<0>module exit
");
    return ;
}

module_init(led_init);
module_exit(led_exit);

测试程序:

#include "stdio.h"
#include "pthread.h"
#include "stdlib.h"
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
int thread_two_fd;

void sign_handler(int signum);

void thread_one(void)
{
    int fd,count;
    char buf[]={~0x01,~0x02,~0x04,~0x08,0x00,0xff};


    fd = open("/dev/my_led",O_RDWR);
    if (fd < 0)
    {
        printf ("Open /dev/my_led file error
");
        pthread_exit(0);
    }
    for(count=0;count<3;count++)
    {
        write(fd,&buf[0],1);
        sleep(1);
        write(fd,&buf[5],1);
        sleep(1);
    }
    close (fd);
}

void thread_two(void)
{
    int fd,oflags;
    char buf[]={~0x01,~0x02,~0x04,~0x08,0x00,0xff};


    fd = open("/dev/my_led",O_RDWR);
    if (fd < 0)
    {
        printf ("open /dev/my_led file error
");
        pthread_exit(0);
    }
    signal(SIGIO,sign_handler);
    //改变已打开文件属性
    fcntl(fd, F_SETOWN, getpid());
    oflags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, oflags | FASYNC);
    thread_two_fd = fd;
    pause();

    close(fd);
}

main()
{
    pthread_t thread_one_id,thread_two_id;
    int resurt;
    pthread_attr_t thread_one_attr,thread_two_attr;
    int thread_one_priority,thread_two_priority;



    thread_one_priority = 11;
    thread_two_priority = 11;

    pthread_attr_init(&thread_one_attr);
    pthread_attr_init(&thread_two_attr);

    //设置为指定调度算法
    pthread_attr_setinheritsched(&thread_one_attr, PTHREAD_EXPLICIT_SCHED);
    pthread_attr_setinheritsched(&thread_two_attr, PTHREAD_EXPLICIT_SCHED);

    //指定调度算法
    pthread_attr_setschedpolicy(&thread_one_attr, SCHED_RR);
    pthread_attr_setschedpolicy(&thread_two_attr, SCHED_RR);

    pthread_attr_setschedparam(&thread_one_attr, (struct sched_param *)&thread_one_priority);

    pthread_attr_setschedparam(&thread_two_attr, (struct sched_param *)&thread_two_priority);

    pthread_attr_setdetachstate(&thread_one_attr,PTHREAD_CREATE_JOINABLE);
    //sleep(1);
    if((resurt = pthread_create(&thread_one_id, &thread_one_attr, (void *)thread_one, NULL)) == -1)
    {
        printf("thread one creat error
 %d",resurt);
        exit(1);
    }


    if((resurt = pthread_create(&thread_two_id, &thread_two_attr, (void *)thread_two, NULL))== -1)
    {
        printf("thread two creat error
");
        exit(1);
    }



    //sleep(1);
    pthread_join(thread_one_id, NULL);
    pthread_join(thread_two_id, NULL);
}
void sign_handler(int signum)
{
    int count;
    char buf[]={~0x01,~0x02,~0x04,~0x08,0x00,0xff};

    printf("sign_handle
");
    for(count=0;count<3;count++)
    {

        write(thread_two_fd ,&buf[1],1);
        sleep(1);
        write(thread_two_fd ,&buf[5],1);
        sleep(1);
    }
 
}
原文地址:https://www.cnblogs.com/hebaichuanyeah/p/3526415.html