• 驱动开发之阻塞与按键驱动


    驱动开发之阻塞与按键驱动:

    内核源码分析:
    container_of((drv), struct platform_driver,driver)        ptr       type    member #define container_of(ptr, type, member) ({     const typeof( ((type *)0)->member ) *__mptr = (ptr);     const typeof( ((struct platform_driver *)0)->driver) *__mptr =drv;// driver类型是struct device_driver    typeof( ((struct platform_driver *)0)->driver)//获取了struct device_driver类型   struct device_driver *__mptr = drv;     (type *)( (char *)__mptr - offsetof(type,member) );

    })
    #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) TYPE:struct platform_driver MEMBER:driver (TYPE *)0 <==> (struct platform_driver *)0 假设platform_driver首地址为0 (TYPE *)0)->MEMBER 以0地址为标准引用了driver成员。 &((TYPE *)0)->MEMBER) 取出driver成员的地址(相对于0的地址) (size_t) &((TYPE *)0)->MEMBER) 将driver成员的地址转成了一个整数 (char *)__mptr 是一个已知的成员地址 offsetof(type,member) 是一个偏移量 成员地址 - 偏移量 = 整个结构体的首地

    同步和互斥:

    当前的os是运行在多核cpu上。 宏观和微观都可以同时执行多个任务。

     如何避免资源竞争?

    1.中断屏蔽:只适用于单核cpu 

    1 关闭中断:local_irq_disable();
    2 临界区
    3 开启中断:local_irq_enable();


    2.原子操作:不可拆分操作

    1 atomic_set();//初始化原子变量
    2 atomic_dec_and_test();//对原子变量-1并且和-进行比较
    3 临界区
    4 atomic_inc();//对原子变量+1


    3.互斥锁

    1 mutex_init();//初始化互斥锁
    2 mutex_lock();//上锁,如果申请不到锁会阻塞
    3 临界区
    4 mutex_unlock();//解锁


    4.信号量

    1 sema_init();//初始化信号量
    2 down();//申请资源,如果申请不到资源会阻塞
    3 临界区
    4 up();//释放资源


    5.自旋锁

    1 spin_lock_init();//初始化自旋锁
    2 spin_lock();//上锁,如果申请不到锁会自旋(循环)
    3 spin_unlock();//解锁

    列如

      while(1);//一直执行,一直在占用cpu(此时cpu不可以分配时间片给其他任务)
      printf("hello ");

      pthread_join();//等待,已经释放了cpu资源(此时cpu可以分配时间片给其他任务)
      printf("hello ");

    自旋锁和互斥锁:

    自旋锁和互斥锁都可以实现互斥,但是自旋锁不会阻塞,互斥锁会阻塞,自旋锁占用资源,互斥锁阻塞时不占用资源。互斥锁的临界区中可以有延时函数,自旋锁的临界区中不能有延时函数。

      自旋锁:临界区中不能有睡眠,而且它的上锁函数本身可能会自旋不会阻塞。
      互斥锁:临界区中能有睡眠,而且申请不到锁会阻塞。

    注:中断处理函数:保证尽快完成,内部不能阻塞。

     ------->如果中断处理函数中需要使用同步互斥机制,应该选择自旋锁。


    IO模型:

      阻塞

      非阻塞

      多路复用

      异步通知

    阻塞:

      内核实现阻塞使用了等待队列机制(双向循环链表)

    等待队列头 
    struct __wait_queue_head {
    spinlock_t lock;//自旋锁变量
    struct list_head task_list;//构建双向循环链表
    };
     typedef struct __wait_queue_head wait_queue_head_t;
    等待队列项 
    typedef struct __wait_queue wait_queue_t;
    typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flag
    
    struct __wait_queue {
    unsigned int flags;
    void *private;//存放的是当前进程结构体的指针
    wait_queue_func_t func;//函数指针
    struct list_head task_list;//构建双向循环链表 
    };

    等待队列的函数接口:

     #define init_waitqueue_head(q) 
    do { 
    static struct lock_class_key __key; 
    
     __init_waitqueue_head((q), #q, &__key); 
     } while (0)    
    
    ----->void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lo
     {
    spin_lock_init(&q->lock);//初始化自旋锁 
     INIT_LIST_HEAD(&q->task_list);//为等待队列头创建空的双向循环链表
     }

    阻塞接口:

    wait_event(wait_queue_head_t,条件);//实现阻塞,不可被信号中断
    wait_event_timeout();
    
    wait_event_interruptible();//实现阻塞,可被信号中断
    wait_event_interruptible_timeout();
    
    sleep_on();//实现阻塞

    唤醒接口:

    wake_up(wait_queue_head_t *);
    wake_up_interruptible();
     1 #include <linux/init.h>
     2 #include <linux/module.h>
     3 #include <linux/fs.h>
     4 #include <linux/device.h>
     5 #include <asm/uaccess.h>
     6 #include <linux/sched.h>
     7 
     8 MODULE_LICENSE("GPL");
     9 
    10 int major;
    11 struct class *cls;
    12 struct device *devs;
    13 
    14 char kbuf[1024];
    15 wait_queue_head_t rq;
    16 wait_queue_head_t wq;
    17 int flag = 0;
    18 
    19 int demo_open(struct inode *inode,struct file *filp)
    20 {
    21     return 0;
    22 }
    23 
    24 int demo_close(struct inode *inode,struct file *filp)
    25 {
    26     return 0;
    27 }
    28 
    29 ssize_t demo_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off)
    30 {
    31     int ret;
    32     ssize_t n;
    33     
    34     wait_event(rq,flag != 0);
    35     if(strlen(kbuf) + 1 > size)
    36         n = size;
    37     else
    38         n = strlen(kbuf) + 1;
    39 
    40     ret = copy_to_user(ubuf,kbuf,n);
    41     if(ret != 0)
    42     {
    43         return -EFAULT;
    44     }
    45     wake_up(&wq);
    46     flag = 0;
    47 
    48     return n;
    49 }
    50 
    51 ssize_t demo_write(struct file *filp,const char __user *ubuf,size_t size,loff_t *off)
    52 {
    53     ssize_t n;
    54     int ret;
    55     
    56     wait_event(wq,flag == 0);
    57     if(size > sizeof(kbuf))
    58         n = sizeof(kbuf);
    59     else 
    60         n = size;
    61     ret = copy_from_user(kbuf,ubuf,n);
    62     if(ret != 0)
    63         return -EFAULT;
    64     printk("%s
    ",kbuf);
    65     wake_up(&rq);
    66     flag = 1;
    67     return n;
    68 }
    69 struct file_operations fops = {
    70     .owner = THIS_MODULE,
    71     .open = demo_open,
    72     .read = demo_read,
    73     .write = demo_write,
    74     .release = demo_close,
    75 };
    76 
    77 int demo_init(void)
    78 {
    79     major = register_chrdev(0,"demo",&fops);
    80 
    81     cls = class_create(THIS_MODULE,"demo");
    82 
    83     devs = device_create(cls,NULL,MKDEV(major,0),NULL,"demo");
    84 
    85     init_waitqueue_head(&rq);
    86     init_waitqueue_head(&wq);
    87     return 0;
    88 }
    89 module_init(demo_init);
    90 
    91 void demo_exit(void)
    92 {
    93     device_destroy(cls,MKDEV(major,0));
    94     class_destroy(cls);
    95     unregister_chrdev(major,"demo");
    96     return;
    97 }
    98 module_exit(demo_exit);
    wait_demo
     1 #include <stdio.h>
     2 #include <sys/types.h>
     3 #include <sys/stat.h>
     4 #include <fcntl.h>
     5 
     6 int main(int argc, const char *argv[])
     7 {
     8     int fd;
     9 
    10     fd = open("/dev/demo",O_RDWR);
    11     if(fd == -1)
    12     {
    13         perror("open");
    14         return -1;
    15     }
    16     
    17     pid_t pid;
    18     
    19     char buf[64];
    20 
    21     pid = fork();
    22     if(pid == -1)
    23     {
    24     
    25     }
    26     else if(pid == 0)
    27     {
    28         read(fd,buf,sizeof(buf));
    29         printf("user read:%s
    ",buf);
    30     }
    31     else 
    32     {
    33         sleep(5);
    34         write(fd,"hello",6);
    35     }
    36     close(fd);
    37     return 0;
    38 }
    wait_app.c

    分析接口:

    vi -t wait_event 
    
    -->#define wait_event(wq, condition)
    do {
             if (condition) //一定要保证最初这个条件为假,而且还建议使用变量表达式。
            break; 
            __wait_event(wq, condition); 
    } while (0)    
    
    -->#define __wait_event(wq, condition) (void)___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, 0, schedule())
    
    -->#define ___wait_event(wq, condition, state, exclusive, ret, cmd) 
    ({ 
        __label__ __out; 
         wait_queue_t __wait;//定义了一个等待队列项
        long __ret = ret; 
    
     INIT_LIST_HEAD(&__wait.task_list);给等待队列项创建双向循环链表
     if (exclusive) 条件为假
    __wait.flags = WQ_FLAG_EXCLUSIVE; 
    else 
     __wait.flags = 0; //被执行
    
    for (;;) { 无限循环
    long __int = prepare_to_wait_event(&wq, &__wait, state);
    if (condition)
        break;
        cmd;
    } 
        finish_wait(&wq, &__wait); 
         __out: __ret; 
    })
    
    进入prepare_to_wait_event(&wq, &__wait, state);
    wait->private = current;//存放了当前进程指针
    wait->func = autoremove_wake_function; //存放了一个函数接口,用来唤醒。
    给等待队列项的成员初始化
     if (list_empty(&wait->task_list)) {判断等待队列项是否为空链表
    if (wait->flags & WQ_FLAG_EXCLUSIVE)//条件为假,因为上面代码将flags = 0
        __add_wait_queue_tail(q, wait);
    else
        __add_wait_queue(q, wait);//将等待队列项加入到等待队列中
    }
    set_current_state(state);//将当前进程的属性设置为TASK_UNINTERRUPTIBLE
    
    回到___wait_event(wq, condition, state, exclusive, ret, cmd)
    继续执行
    for(::)
    {
         if (condition)暂时还是假
        break;
        cmd;//进程调度器,调度器执行前进程已经存放在了等待队列中。
    }
    进程调度器被执行时会判断进程的属性,如果进程的属性为TASK_UNINTERRUPTIBLE,进程调度器会将运行队列中的进程删除。
    
    cpu给进程分配时间片前,会遍历运行队列,只有这里的进程才具备拥有时间片的资格。
    
    所以说:一旦进程从运行队列中删除后可能出现阻塞。
    
    唤醒接口:
    vi -t wake_up 
    #define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
    
    --> __wake_up_common(q, mode, nr_exclusive, 0, key);
    
    --> static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
    int nr_exclusive, int wake_flags, void *key)
    {
        wait_queue_t *curr, *next;
    
         list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
         unsigned flags = curr->flags;
     
         if (curr->func(curr, mode, wake_flags, key) &&
         (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
         break;
    }
    }
    执行curr->func(curr, mode, wake_flags, key)时,本质上在调用prepare_to_wait_event(&wq, &__wait, state);中的autoremove_wake_function,这个函数将进程的状态设置为TASK_WAKING。
    
    时刻记住wait_event内部的for循环是一直在执行的,一直在执行进程调度器。进程调度器会识别到
    TASK_WAKING,将进程信息拷贝到运行队列中,但是此时等待队列中还包含进程信息。一旦重新将进程拿到运行队列后,那么需要指定条件为真。跳出循环后执行finish_wait(&wq, &__wait); 将进程从等待队列中删除

    死锁:

    线程1:
    pthread_mutex_lock(&a);
    pthread_mutex_lock(&b);
    临界区
    pthread_mutex_unlock(&a);
    pthread_mutex_unlock(&b);

    线程2:
    pthread_mutex_lock(&b);
    pthread_mutex_lock(&a);
    临界区
    pthread_mutex_unlock(&a);
    pthread_mutex_unlock(&b);
    出现死锁
    避免的方式:保证多个线程申请锁的顺序一致。

    按键驱动(使用中断的方式来实现按键驱动):

    按键的硬件特性:
      核心板:
        XEINT9/KP_COL1/ALV_DBG5/GPX1_1 ---> UART_RING
        说明UART_RING引脚使用外部中断9,控制引脚是GPX1_1
        在厂家封装的设备树中对应的gpio控制器的引脚名称叫做gpx1

    进入三星手册中:
      第九章
        25 57 – EINT[9] External Interrupt
        说明外部中断9使用的共享中断的中断号为25

    vi exynos4x12-pinctl.dtsi
    gpx1: gpx1 { 
    gpio-controller;//说明这个节点是一个gpio控制器
    #gpio-cells = <2>;
    
    interrupt-controller;//同时也是一个中断控制器
    interrupt-parent = <&gic>;//gpx1中断控制器的中断父节点是gic
    interrupts = <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>,
    索引值             0                 1             2             3 
    <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>;
        4                 5             6             7
    #interrupt-cells = <2>;//当前节点的中断子节点中的interrupts属性有2个值
    };            

    UART_RING按键连接到了gpx1中断控制器上

    interrupts = <val1 val2 val3>;//一般出现在控制器节点中
      val1:中断类型 0代表共享中断、1代表私有中断
      val2:硬件中断号
      val3:中断触发方式
        1 上升沿触发
        2 下降沿触发
        4 高电平触发
        8 低电平触发

    interrupts = <val1 val2>;//用于一下普通节点
      val1:硬件中断号的索引值
      val2:代表中断触发方式

    自己的设备树如何实现?

    fs4412-key{
    compatible = "fs4412,key";
    interrupt-parent = <&gpx1>;
    interrupts = <1 2>,<2 2>;
    };
    make dtbs 
    cp exynos4412-fs4412.dtb /tftpboot

    注册中断接口:
     1 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
     2 功能:给内核注册中断(给struct irqaction结构体初始化)
     3 参数1:虚拟中断号
     4 参数2:中断处理函数 :irqreturn_t (*)(int ,void *);
     5                                 参数1:虚拟中断号
     6                                 参数2:只有共享中断可以使用
     7                                 返回值:如果中断被当前设备操作直接返回IRQ_HANDLED
     8 参数3:指定中断触发方式,也可以指定中断类型
     9 中断触发方式:
    10             上升沿触发:IRQF_TRIGGER_RISING
    11             下降沿触发:IRQF_TRIGGER_FALLING
    12             高电平触发:IRQF_TRIGGER_HIGH
    13             低电平触发:IRQF_TRIGGER_LOW
    14 参数4:出现在/proc/interrupts文件中的一个名称,代表中断名称     
    15 参数5:共享中断使用,其他中断传递NULL        


    注销中断接口:

    1 void free_irq(unsigned int irq,void *);

    按键驱动实现过程:
      1、模块三要素
      2、注册platform驱动,注销platform驱动
      3、定义并且初始化platform_driver结构体
      4、如果和设备树匹配成功执行probe函数
          在probe中需要搭建字符设备框架、创建设备类、创建设备文件、注册中断
      5、如果有按键按下则执行中断处理函数

      1 #include <linux/module.h>
      2 #include <linux/fs.h>
      3 #include <linux/platform_device.h>
      4 #include <linux/device.h>
      5 #include <asm/uaccess.h>
      6 #include <linux/irqreturn.h>
      7 #include <linux/interrupt.h>
      8 #include <linux/sched.h>
      9 
     10 int major;
     11 
     12 struct class *cls;
     13 struct device *devs;
     14 struct resource *res_key2;
     15 struct resource *res_key3;
     16 
     17 int key;
     18 wait_queue_head_t keyq;
     19 int flag = 0;
     20 
     21 irqreturn_t fs4412_key_handler(int irqno,void *id)
     22 {
     23     if(irqno == res_key2->start)
     24         key = 2;
     25     if(irqno == res_key3->start)
     26         key = 3;
     27 //    wake_up(&keyq);
     28     wake_up_interruptible(&keyq);
     29     flag = 1;
     30     return IRQ_HANDLED;
     31 }
     32 
     33 int fs4412_key_open(struct inode *inode ,struct file *filp)
     34 {
     35     return 0;
     36 }
     37 
     38 ssize_t fs4412_key_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off)
     39 {
     40     int ret;
     41     
     42 //    wait_event(keyq,flag != 0);
     43     wait_event_interruptible(keyq,flag != 0);
     44     ret = copy_to_user(ubuf,&key,sizeof(key));
     45 
     46     flag = 0;
     47     return sizeof(key);
     48 }
     49 
     50 struct file_operations fops = {
     51     .owner = THIS_MODULE,
     52     .open = fs4412_key_open,
     53     .read = fs4412_key_read,
     54 };
     55 
     56 int fs4412_key_probe(struct platform_device *pdev)
     57 {
     58     int ret;
     59     printk("match ok
    ");
     60 
     61     major = register_chrdev(0,"key",&fops);
     62     cls = class_create(THIS_MODULE,"key");
     63     devs = device_create(cls,NULL,MKDEV(major,0),NULL,"key");
     64     
     65     res_key2 = platform_get_resource(pdev,IORESOURCE_IRQ,0);//索引interrupts = <>,<>第一个<>
     66     res_key3 = platform_get_resource(pdev,IORESOURCE_IRQ,1);
     67     ret = request_irq(res_key2->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key2",NULL);
     68     ret = request_irq(res_key3->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key3",NULL);
     69 
     70     init_waitqueue_head(&keyq);
     71     return 0;
     72 }
     73 
     74 int fs4412_key_remove(struct platform_device *pdev)
     75 {
     76     free_irq(res_key3->start,NULL);
     77     free_irq(res_key2->start,NULL);
     78     device_destroy(cls,MKDEV(major,0));
     79     class_destroy(cls);
     80     unregister_chrdev(major,"key");
     81     return 0;
     82 }
     83 
     84 struct of_device_id fs4412_dt_tbl[] = {
     85     {
     86         .compatible = "fs4412,key",
     87     },
     88     {
     89    
     90          },//必须要有个空大括号如果没有可能会发生段错误
     91 };
     92 
     93 
     94 struct platform_driver pdrv = {
     95     .driver = {
     96         .name = "fs4412-key",
     97         .of_match_table = fs4412_dt_tbl,
     98     },
     99     .probe = fs4412_key_probe,
    100     .remove = fs4412_key_remove,
    101 };
    102 
    103 module_platform_driver(pdrv);
    104 MODULE_LICENSE("GPL");
    105     
    key_int.c
     1 #include <stdio.h>
     2 #include <sys/types.h>
     3 #include <sys/stat.h>
     4 #include <fcntl.h>
     5 
     6 int main(int argc, const char *argv[])
     7 {
     8     int fd;
     9 
    10     fd = open("/dev/key",O_RDWR);
    11     if(fd == -1)
    12     {
    13         perror("open");
    14         return -1;
    15     }
    16     
    17     int key;
    18     while(1)
    19     {
    20         read(fd,&key,sizeof(key));
    21         printf("key = %d
    ",key);
    22     }
    23     return 0;
    24 }
    app.c

    定时器接口:

    1 struct timer_list
    2 {
    3 void (*function)(unsigned long);//定时器中断处理函数
    4 };
    1 init_timer(struct timer_list *);//初始化定时器
    1 add_timer(struct timer_list *);//给内核注册定时器
    1 int mod_timer(struct timer_list *timer, unsigned long expires)
    2 参数1:
    3 参数2:定时的时间值
    4     绝对时间
    5     相对时间:jiffies代表从系统开机到mod_timer执行完成的时间差
    6         jiffies + 30;定时30ms
    7         jiffies + 3 * HZ / 100;定时30ms 
    1 void del_timer(struct timer_list *);
      1 #include <linux/module.h>
      2 #include <linux/fs.h>
      3 #include <linux/platform_device.h>
      4 #include <linux/device.h>
      5 #include <asm/uaccess.h>
      6 #include <linux/irqreturn.h>
      7 #include <linux/interrupt.h>
      8 #include <linux/sched.h>
      9 
     10 int major;
     11 
     12 struct class *cls;
     13 struct device *devs;
     14 struct resource *res_key2;
     15 struct resource *res_key3;
     16 
     17 int key;
     18 wait_queue_head_t keyq;
     19 int flag = 0;
     20 struct timer_list t;
     21 int glo_irqno;
     22 
     23 irqreturn_t fs4412_key_handler(int irqno,void *id)
     24 {
     25     glo_irqno = irqno;
     26     mod_timer(&t,jiffies + 30);
     27     return IRQ_HANDLED;
     28 }
     29 
     30 void fs4412_key_timer_handler(unsigned long data)
     31 {
     32     if(glo_irqno == res_key2->start)
     33         key = 2;
     34     if(glo_irqno == res_key3->start)
     35         key = 3;
     36 //    wake_up(&keyq);
     37     wake_up_interruptible(&keyq);
     38     flag = 1;
     39 }
     40 
     41 int fs4412_key_open(struct inode *inode ,struct file *filp)
     42 {
     43     return 0;
     44 }
     45 
     46 ssize_t fs4412_key_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off)
     47 {
     48     int ret;
     49     
     50 //    wait_event(keyq,flag != 0);
     51     wait_event_interruptible(keyq,flag != 0);
     52     ret = copy_to_user(ubuf,&key,sizeof(key));
     53 
     54     flag = 0;
     55     return sizeof(key);
     56 }
     57 
     58 struct file_operations fops = {
     59     .owner = THIS_MODULE,
     60     .open = fs4412_key_open,
     61     .read = fs4412_key_read,
     62 };
     63 
     64 int fs4412_key_probe(struct platform_device *pdev)
     65 {
     66     int ret;
     67     printk("match ok
    ");
     68 
     69     major = register_chrdev(0,"key",&fops);
     70     cls = class_create(THIS_MODULE,"key");
     71     devs = device_create(cls,NULL,MKDEV(major,0),NULL,"key");
     72     
     73     res_key2 = platform_get_resource(pdev,IORESOURCE_IRQ,0);//索引interrupts = <>,<>第一个<>
     74     res_key3 = platform_get_resource(pdev,IORESOURCE_IRQ,1);
     75     ret = request_irq(res_key2->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key2",NULL);
     76     ret = request_irq(res_key3->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key3",NULL);
     77 
     78     init_waitqueue_head(&keyq);
     79 
     80     init_timer(&t);
     81     t.function = fs4412_key_timer_handler;//定时器中断处理函数
     82     add_timer(&t);//让内核认识定时器中断处理函数
     83     return 0;
     84 }
     85 
     86 int fs4412_key_remove(struct platform_device *pdev)
     87 {
     88     del_timer(&t);
     89     free_irq(res_key3->start,NULL);
     90     free_irq(res_key2->start,NULL);
     91     device_destroy(cls,MKDEV(major,0));
     92     class_destroy(cls);
     93     unregister_chrdev(major,"key");
     94     return 0;
     95 }
     96 
     97 struct of_device_id fs4412_dt_tbl[] = {
     98     {
     99         .compatible = "fs4412,key",
    100     },
    101     {},
    102 };
    103 
    104 
    105 struct platform_driver pdrv = {
    106     .driver = {
    107         .name = "fs4412-key",
    108         .of_match_table = fs4412_dt_tbl,
    109     },
    110     .probe = fs4412_key_probe,
    111     .remove = fs4412_key_remove,
    112 };
    113 
    114 module_platform_driver(pdrv);
    115 MODULE_LICENSE("GPL");
    key_timer.c
     1 #include <stdio.h>
     2 #include <sys/types.h>
     3 #include <sys/stat.h>
     4 #include <fcntl.h>
     5 
     6 int main(int argc, const char *argv[])
     7 {
     8     int fd;
     9 
    10     fd = open("/dev/key",O_RDWR);
    11     if(fd == -1)
    12     {
    13         perror("open");
    14         return -1;
    15     }
    16     
    17     int key;
    18     while(1)
    19     {
    20         read(fd,&key,sizeof(key));
    21         printf("key = %d
    ",key);
    22     }
    23     return 0;
    24 }
    app.c



  • 相关阅读:
    PhpStorm一次性折叠所有函数或者方法
    安装IntelliJ IDEA热部署tomcat插件JreBel
    mysql-master-ha
    mysql sys table
    Innodb 表修复(转)
    MySQL Binlog 【ROW】和【STATEMENT】选择(转)
    Innodb 存储引擎(转)
    MySQL 利用SQL线程对Binlog操作(转)
    针对跑MySQL的Linux优化【转】
    MySQL explain key_len 大小的计算
  • 原文地址:https://www.cnblogs.com/hslixiqian/p/9664059.html
Copyright © 2020-2023  润新知