ARM-驱动

一、Linux内核中并发和竞态的解决方法

 1.竞态产生的原因:

 当多个应用程序访问同一个驱动的临界资源的时候,竞态就会产生了。

产生竞态的根本原因?

1.对于单核处理器,如果内核支持抢占就会产生竞态

2.多核处理器,核心与核心之间本身就会产生竞态。

3.中断和进程间也会产生竞态

2. 解决竞态的方法

 中断屏蔽

中断屏蔽:中断屏蔽只针对单核处理器有效,中断屏蔽顾名思义就是将中断关闭掉,

当中断关闭掉之后就不会产生竞态了。中断屏蔽的时间要尽可能的短,否则会出现

用户数据的丢失或者内核的崩溃。

函数接口API:

        local_irq_disable();  //屏蔽中断
        //临界资源
        local_irq_enable();   //开启中断

 自旋锁(重点掌握)

 自旋锁:针对多核设计的,当一个进程获取到自旋锁之后,另外一个进程也想获取

这把锁,此时后一个进程处于自旋状态(原地打转)(自旋锁又叫盲等锁)

特点:

1.自旋状态是需要消耗cpu资源的

2.自旋锁适合用在临界区比较小地方,临界区不能有延时,耗时,甚至休眠的操作,

临界区也不能够有copy_to_user/copy_from_user的函数。

3.自旋锁会导致死锁(在同一个进程中多次获取同一把未解锁的锁)

4.自旋锁可以在进程上下文中使用,也可以在中断上下文中使用

5.自旋锁在上锁前会关闭抢占

自旋锁的API:

spinlock_t lock;        //定义自旋锁
spin_lock_init(&lock); //初始化自旋锁
spin_lock(&lock);      //上锁
spin_unlock(&lock);    //解锁

自旋锁的实例 

mycdev.c 

#include 
#include 
#include 
#include 
#include 
#include 

#define CNAME "mycdev"

//定义cdev的结构体指针变量
struct cdev *cdev;
#if 1
unsigned int major = 0; //动态申请
#else
unsigned int major = 500; //静态指定
#endif
int minor = 0;
const int count = 3;
struct class *cls;
struct device *dev;

char kbuf[128] = {0};
spinlock_t lock;  //定义自旋锁
int flags=0;
int mycdev_open(struct inode *inode, struct file *file)
{
    spin_lock(&lock); //上锁
    if(flags !=0 ){
         spin_unlock(&lock);
        return -EBUSY;
    }
    flags=1;
    spin_unlock(&lock);
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

ssize_t mycdev_read(struct file *file, char __user *ubuf,
 size_t size, loff_t *offs)
{
    int ret;

    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

    if (size > sizeof(kbuf))
        size = sizeof(kbuf);

    ret = copy_to_user(ubuf, kbuf, size);
    if (ret)
    {
        printk("copy data to user error\n");
        return -EIO;
    } // shift+alt+f 代码格式化
    return size;
}
ssize_t mycdev_write(struct file *file,
     const char __user *ubuf, size_t size, loff_t *off)
{
    int ret;
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

    if (size > sizeof(kbuf))
        size = sizeof(kbuf);
    ret = copy_from_user(kbuf, ubuf, size);
    if (ret)
    {
        printk("copy data from user error\n");
        return -EIO;
    }
    return size;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    spin_lock(&lock);
    flags=0;
    spin_unlock(&lock); //解锁
    return 0;
}

const struct file_operations fops = {
    .open = mycdev_open,
    .read = mycdev_read,
    .write = mycdev_write,
    .release = mycdev_close,
};

static int __init mycdev_init(void)
{
    int i, ret;
    dev_t devno;
    // 1.分配对象
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk("cdev alloc memory error\n");
        ret = -ENOMEM;
        goto ERR1;
    }
    // 2.对象的初始化
    cdev_init(cdev, &fops);
    // 3.申请设备号
    if (major == 0)
    {
        //动态申请
        ret = alloc_chrdev_region(&devno, minor, count, CNAME);
        if (ret)
        {
            printk("dynamic:alloc device number error\n");
            goto ERR2;
        }
        major = MAJOR(devno);
        minor = MINOR(devno);
    }
    else if (major > 0)
    {
        //静态指定
        ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
        if (ret)
        {
            printk("static:alloc device number error\n");
            goto ERR2;
        }
    }
    // 4.字符设备驱动的注册
    ret = cdev_add(cdev, MKDEV(major, minor), count);
    if (ret)
    {
        printk("cdev register error\n");
        goto ERR3;
    }
    // 5.自动创建设备节点
    cls = class_create(THIS_MODULE, CNAME);
    if (IS_ERR(cls))
    {
        printk("class create error\n");
        ret = PTR_ERR(cls);
        goto ERR4;
    }
    for (i = 0; i < count; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
        if (IS_ERR(dev))
        {
            printk("device create error\n");
            ret = PTR_ERR(dev);
            goto ERR5;
        }
    }
    //初始化自旋锁
    spin_lock_init(&lock);

    return 0; //!!!!!!!这里的return 0千万不要忘记写!!!!!!!!!!!!!!
ERR5:
    for (--i; i >= 0; i--)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls);
ERR4:
    cdev_del(cdev);
ERR3:
    unregister_chrdev_region(MKDEV(major, minor), count);
ERR2:
    kfree(cdev);
ERR1:
    return ret;
}
static void __exit mycdev_exit(void)
{
    int i;
    // 1.销毁设备节点
    for (i = 0; i < count; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls);
    // 2.销毁字符设备驱动
    cdev_del(cdev);
    // 3.销毁设备号
    unregister_chrdev_region(MKDEV(major, minor), count);
    // 4.释放动态申请的cdev内存
    kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

test.c

#include 
int main(int argc,const char * argv[])
{
    int fd;
    char buf[128] = "i am fine..........\n";

    if((fd = open("/dev/mycdev0",O_RDWR))==-1){
        perror("open error");
        exit(EXIT_FAILURE);
    }
    write(fd,buf,sizeof(buf));

    memset(buf,0,sizeof(buf));
    sleep(7);
    read(fd,buf,sizeof(buf));
    printf("user read buf = %s\n",buf);
    close(fd);
    return 0;
}

信号量(重点掌握)

信号量:当一个进程获取到信号量之后,另外一个进程也想获取信号量,此时后一个进程处于休眠状态。

特点:

1.休眠的进程是不消耗cpu资源的

2.信号量不会导致死锁

3.信号量保护的临界区可以很大,里面可以有延时,耗时,甚至休眠的操作。

4.信号量只能工作在进程上下文。不能工作于中断上下文。

5.信号量也不会关闭抢占

信号量的API:

struct semaphore sem; //定义信号量
void sema_init(struct semaphore *sem, int val) //初始化信号量
//注意,这里的val只有初始化为1的时候才有能够解决竞态。当val初始化为0的时候代表同步机制
void down(struct semaphore *sem); //上锁
int  down_trylock(struct semaphore *sem); //尝试获取锁 ,成功返回0,失败返回1,不会阻塞
void up(struct semaphore *sem);//解锁

 使用信号量解决竞态问题的实例

#include 
#include 
#include 
#include 
#include 
#include 

#define CNAME "mycdev"

//定义cdev的结构体指针变量
struct cdev *cdev;
#if 1
unsigned int major = 0; //动态申请
#else
unsigned int major = 500; //静态指定
#endif
int minor = 0;
const int count = 3;
struct class *cls;
struct device *dev;
char kbuf[128] = {0};
struct semaphore sem; //定义信号量

int mycdev_open(struct inode *inode, struct file *file)
{
    //down(&sem);
    if(down_trylock(&sem)){ //尝试获取锁
        return -EBUSY;
    }
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

ssize_t mycdev_read(struct file *file, char __user *ubuf,
 size_t size, loff_t *offs)
{
    int ret;

    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

    if (size > sizeof(kbuf))
        size = sizeof(kbuf);

    ret = copy_to_user(ubuf, kbuf, size);
    if (ret)
    {
        printk("copy data to user error\n");
        return -EIO;
    } // shift+alt+f 代码格式化
    return size;
}
ssize_t mycdev_write(struct file *file,
     const char __user *ubuf, size_t size, loff_t *off)
{
    int ret;
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

    if (size > sizeof(kbuf))
        size = sizeof(kbuf);
    ret = copy_from_user(kbuf, ubuf, size);
    if (ret)
    {
        printk("copy data from user error\n");
        return -EIO;
    }
    return size;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    up(&sem); //解锁
    return 0;
}

const struct file_operations fops = {
    .open = mycdev_open,
    .read = mycdev_read,
    .write = mycdev_write,
    .release = mycdev_close,
};

static int __init mycdev_init(void)
{
    int i, ret;
    dev_t devno;
    // 1.分配对象
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk("cdev alloc memory error\n");
        ret = -ENOMEM;
        goto ERR1;
    }
    // 2.对象的初始化
    cdev_init(cdev, &fops);
    // 3.申请设备号
    if (major == 0)
    {
        //动态申请
        ret = alloc_chrdev_region(&devno, minor, count, CNAME);
        if (ret)
        {
            printk("dynamic:alloc device number error\n");
            goto ERR2;
        }
        major = MAJOR(devno);
        minor = MINOR(devno);
    }
    else if (major > 0)
    {
        //静态指定
        ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
        if (ret)
        {
            printk("static:alloc device number error\n");
            goto ERR2;
        }
    }
    // 4.字符设备驱动的注册
    ret = cdev_add(cdev, MKDEV(major, minor), count);
    if (ret)
    {
        printk("cdev register error\n");
        goto ERR3;
    }
    // 5.自动创建设备节点
    cls = class_create(THIS_MODULE, CNAME);
    if (IS_ERR(cls))
    {
        printk("class create error\n");
        ret = PTR_ERR(cls);
        goto ERR4;
    }
    for (i = 0; i < count; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
        if (IS_ERR(dev))
        {
            printk("device create error\n");
            ret = PTR_ERR(dev);
            goto ERR5;
        }
    }

    //初始化信号量
    sema_init(&sem,1);

    return 0; //!!!!!!!这里的return 0千万不要忘记写!!!!!!!!!!!!!!
ERR5:
    for (--i; i >= 0; i--)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls);
ERR4:
    cdev_del(cdev);
ERR3:
    unregister_chrdev_region(MKDEV(major, minor), count);
ERR2:
    kfree(cdev);
ERR1:
    return ret;
}
static void __exit mycdev_exit(void)
{
    int i;
    // 1.销毁设备节点
    for (i = 0; i < count; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls);
    // 2.销毁字符设备驱动
    cdev_del(cdev);
    // 3.销毁设备号
    unregister_chrdev_region(MKDEV(major, minor), count);
    // 4.释放动态申请的cdev内存
    kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

test.c

#include 

int main(int argc,const char * argv[])
{
    int fd;
    char buf[128] = "i am fine..........\n";

    if((fd = open("/dev/mycdev0",O_RDWR))==-1){
        perror("open error");
        exit(EXIT_FAILURE);
    }
    write(fd,buf,sizeof(buf));

    memset(buf,0,sizeof(buf));
    sleep(7);
    read(fd,buf,sizeof(buf));
    printf("user read buf = %s\n",buf);

    close(fd);
    return 0;
}

 互斥体(掌握)

 互斥体:当一个进程获取到互斥体之后,另外一个进程也想获取互斥体,此时后一个进程处于休眠状态。

特点:

1.休眠的进程是不消耗cpu资源的

2.互斥体不会导致死锁

3.互斥体保护的临界区可以很大,里面可以有延时,耗时,甚至休眠的操作。

4.互斥体只能工作在进程上下文。不能工作于中断上下文。

5.互斥体也不会关闭抢占

6.互斥体在进入休眠前会稍微等一会儿在进入休眠状态,但是对于临界区比较小

​ 的时候使用互斥体的效率是高于信号量的,优先使用互斥体

互斥体的API

struct mutex mutex; //定义互斥体
mutex_init(&mutex); //初始化互斥体
void  mutex_lock(struct mutex *lock) //上锁
int  mutex_trylock(struct mutex *lock) //尝试获取锁,成功返回1,失败返回0
static int mutex_unlock(unsigned long *lock) //解锁

 使用互斥体解决竞态问题的实例

#include 
#include 
#include 
#include 
#include 
#include 

#define CNAME "mycdev"

//定义cdev的结构体指针变量
struct cdev *cdev;
#if 1
unsigned int major = 0; //动态申请
#else
unsigned int major = 500; //静态指定
#endif
int minor = 0;
const int count = 3;
struct class *cls;
struct device *dev;
char kbuf[128] = {0};
struct mutex mutex;//定义互斥体
int mycdev_open(struct inode *inode, struct file *file)
{
    if(!mutex_trylock(&mutex)){
        return -EBUSY;
    }
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

ssize_t mycdev_read(struct file *file, char __user *ubuf,
 size_t size, loff_t *offs)
{
    int ret;

    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

    if (size > sizeof(kbuf))
        size = sizeof(kbuf);

    ret = copy_to_user(ubuf, kbuf, size);
    if (ret)
    {
        printk("copy data to user error\n");
        return -EIO;
    } // shift+alt+f 代码格式化
    return size;
}
ssize_t mycdev_write(struct file *file,
     const char __user *ubuf, size_t size, loff_t *off)
{
    int ret;
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

    if (size > sizeof(kbuf))
        size = sizeof(kbuf);
    ret = copy_from_user(kbuf, ubuf, size);
    if (ret)
    {
        printk("copy data from user error\n");
        return -EIO;
    }
    return size;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

    mutex_unlock(&mutex);
    return 0;
}

const struct file_operations fops = {
    .open = mycdev_open,
    .read = mycdev_read,
    .write = mycdev_write,
    .release = mycdev_close,
};

static int __init mycdev_init(void)
{
    int i, ret;
    dev_t devno;
    // 1.分配对象
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk("cdev alloc memory error\n");
        ret = -ENOMEM;
        goto ERR1;
    }
    // 2.对象的初始化
    cdev_init(cdev, &fops);
    // 3.申请设备号
    if (major == 0)
    {
        //动态申请
        ret = alloc_chrdev_region(&devno, minor, count, CNAME);
        if (ret)
        {
            printk("dynamic:alloc device number error\n");
            goto ERR2;
        }
        major = MAJOR(devno);
        minor = MINOR(devno);
    }
    else if (major > 0)
    {
        //静态指定
        ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
        if (ret)
        {
            printk("static:alloc device number error\n");
            goto ERR2;
        }
    }
    // 4.字符设备驱动的注册
    ret = cdev_add(cdev, MKDEV(major, minor), count);
    if (ret)
    {
        printk("cdev register error\n");
        goto ERR3;
    }
    // 5.自动创建设备节点
    cls = class_create(THIS_MODULE, CNAME);
    if (IS_ERR(cls))
    {
        printk("class create error\n");
        ret = PTR_ERR(cls);
        goto ERR4;
    }
    for (i = 0; i < count; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
        if (IS_ERR(dev))
        {
            printk("device create error\n");
            ret = PTR_ERR(dev);
            goto ERR5;
        }
    }

    mutex_init(&mutex); //初始化互斥体
    return 0; //!!!!!!!这里的return 0千万不要忘记写!!!!!!!!!!!!!!
ERR5:
    for (--i; i >= 0; i--)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls);
ERR4:
    cdev_del(cdev);
ERR3:
    unregister_chrdev_region(MKDEV(major, minor), count);
ERR2:
    kfree(cdev);
ERR1:
    return ret;
}
static void __exit mycdev_exit(void)
{
    int i;
    // 1.销毁设备节点
    for (i = 0; i < count; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls);
    // 2.销毁字符设备驱动
    cdev_del(cdev);
    // 3.销毁设备号
    unregister_chrdev_region(MKDEV(major, minor), count);
    // 4.释放动态申请的cdev内存
    kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

 test.c

#include 

int main(int argc,const char * argv[])
{
    int fd;
    char buf[128] = "i am fine..........\n";

    if((fd = open("/dev/mycdev0",O_RDWR))==-1){
        perror("open error");
        exit(EXIT_FAILURE);
    }
    write(fd,buf,sizeof(buf));

    memset(buf,0,sizeof(buf));
    sleep(7);
    read(fd,buf,sizeof(buf));
    printf("user read buf = %s\n",buf);

    close(fd);
    return 0;
}

 原子操作(掌握)

原子操作:将整个过程看做是一个不可被分割的整体。原子操作结构体内部本身就是一个变量,通过对这个变量的值的修改来实现防止竞态的效果,对这个变量的值的修改是通过内联汇编完成的。

原子操作的API接口

atomic_t atm = ATOMIC_INIT(1); //定义并初始化原子操作
int atomic_dec_and_test(atomic_t *v) //上锁
功能:让原子变量的值减去1,如果结果为0 表示获取锁成功了
参数:
    @v:原子变量的地址
返回值:如果获取成功返回真,失败返回假
void atomic_inc(atomic_t *v)   //将原子变量的值加1,解锁

atomic_t atm = ATOMIC_INIT(-1); //定义并初始化原子操作
int atomic_inc_and_test(atomic_t *v) //上锁

功能:让原子变量的值加1,如果结果为0 表示获取锁成功了
参数:
    @v:原子变量的地址
返回值:如果获取成功返回真,失败返回假
void atomic_dec(atomic_t *v)   //将原子变量的值减去1,解锁

 使用原子变量实现防竞态的实例

#include 
#include 
#include 
#include 
#include 
#include 

#define CNAME "mycdev"

//定义cdev的结构体指针变量
struct cdev *cdev;
#if 1
unsigned int major = 0; //动态申请
#else
unsigned int major = 500; //静态指定
#endif
int minor = 0;
const int count = 3;
struct class *cls;
struct device *dev;
char kbuf[128] = {0};

atomic_t atm = ATOMIC_INIT(1);

int mycdev_open(struct inode *inode, struct file *file)
{
    if(!atomic_dec_and_test(&atm)){
        atomic_inc(&atm);
        return -EBUSY;
    }
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}

ssize_t mycdev_read(struct file *file, char __user *ubuf,
 size_t size, loff_t *offs)
{
    int ret;

    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

    if (size > sizeof(kbuf))
        size = sizeof(kbuf);

    ret = copy_to_user(ubuf, kbuf, size);
    if (ret)
    {
        printk("copy data to user error\n");
        return -EIO;
    } // shift+alt+f 代码格式化
    return size;
}
ssize_t mycdev_write(struct file *file,
     const char __user *ubuf, size_t size, loff_t *off)
{
    int ret;
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);

    if (size > sizeof(kbuf))
        size = sizeof(kbuf);
    ret = copy_from_user(kbuf, ubuf, size);
    if (ret)
    {
        printk("copy data from user error\n");
        return -EIO;
    }
    return size;
}
int mycdev_close(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    atomic_inc(&atm);
    return 0;
}

const struct file_operations fops = {
    .open = mycdev_open,
    .read = mycdev_read,
    .write = mycdev_write,
    .release = mycdev_close,
};

static int __init mycdev_init(void)
{
    int i, ret;
    dev_t devno;
    // 1.分配对象
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk("cdev alloc memory error\n");
        ret = -ENOMEM;
        goto ERR1;
    }
    // 2.对象的初始化
    cdev_init(cdev, &fops);
    // 3.申请设备号
    if (major == 0)
    {
        //动态申请
        ret = alloc_chrdev_region(&devno, minor, count, CNAME);
        if (ret)
        {
            printk("dynamic:alloc device number error\n");
            goto ERR2;
        }
        major = MAJOR(devno);
        minor = MINOR(devno);
    }
    else if (major > 0)
    {
        //静态指定
        ret = register_chrdev_region(MKDEV(major, minor), count, CNAME);
        if (ret)
        {
            printk("static:alloc device number error\n");
            goto ERR2;
        }
    }
    // 4.字符设备驱动的注册
    ret = cdev_add(cdev, MKDEV(major, minor), count);
    if (ret)
    {
        printk("cdev register error\n");
        goto ERR3;
    }
    // 5.自动创建设备节点
    cls = class_create(THIS_MODULE, CNAME);
    if (IS_ERR(cls))
    {
        printk("class create error\n");
        ret = PTR_ERR(cls);
        goto ERR4;
    }
    for (i = 0; i < count; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
        if (IS_ERR(dev))
        {
            printk("device create error\n");
            ret = PTR_ERR(dev);
            goto ERR5;
        }
    }
    return 0; //!!!!!!!这里的return 0千万不要忘记写!!!!!!!!!!!!!!
ERR5:
    for (--i; i >= 0; i--)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls);
ERR4:
    cdev_del(cdev);
ERR3:
    unregister_chrdev_region(MKDEV(major, minor), count);
ERR2:
    kfree(cdev);
ERR1:
    return ret;
}
static void __exit mycdev_exit(void)
{
    int i;
    // 1.销毁设备节点
    for (i = 0; i < count; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls);
    // 2.销毁字符设备驱动
    cdev_del(cdev);
    // 3.销毁设备号
    unregister_chrdev_region(MKDEV(major, minor), count);
    // 4.释放动态申请的cdev内存
    kfree(cdev);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

 test.c

#include 

int main(int argc,const char * argv[])
{
    int fd;
    char buf[128] = "i am fine..........\n";

    if((fd = open("/dev/mycdev0",O_RDWR))==-1){
        perror("open error");
        exit(EXIT_FAILURE);
    }
    write(fd,buf,sizeof(buf));

    memset(buf,0,sizeof(buf));
    sleep(7);
    read(fd,buf,sizeof(buf));
    printf("user read buf = %s\n",buf);

    close(fd);
    return 0;
}

你可能感兴趣的:(linux,单片机)