linux内核驱动入门之阻塞操作实验:glob

首先,先来了解一下设备的阻塞与非阻塞操作以及实现阻塞操作的方法:

1.设备的阻塞与非阻塞操作:

阻塞操作是指,在执行设备操作时,若不能获得资源,则进程被挂起直到满足可操作的条件再进行操作。非阻塞操作是指,当进程不能进行设备操作时,并不挂起,它或者放弃,或者不停地查询,直到可以进行操作为止。

2.实现阻塞操作的方法:

在linux驱动程序中,可以使用等待队列(wait queue)来实现阻塞访问。

一,glob字符设备驱动程序的编写,把文件名命名为glob.c,源代码如下:

#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/sched.h> 
MODULE_LICENSE("GPL");
#define MAJOR_NUM 1400
#define DEVICE_NAME "glob" 
static int glob_var = 0;
static struct semaphore sem; //定义信号量
static wait_queue_head_t outq;  //定义一个等待队列头   
static int flag = 0;

//*******************定义read方法****************************
static ssize_t glob_read(struct file *filp, char *buf, ssize_t len, loff_t *off)
{
      //等待数据可获得
      //wait_event_interruptible的返回一个整数值,非零值表示休眠被某个信号中断
      //wait_event_interruptible中第一个参数是等待队列头,第二个参数是一个布尔表达式,在条件为真之前,进程会保持休眠 
     if (wait_event_interruptible(outq, flag != 0)) 
     {
          return - ERESTARTSYS;
     }
     //down_interruptible 函数返回非零值,表示操作被中断,调用者拥有信号量失败
     if (down_interruptible(&sem))
     {
          return - ERESTARTSYS;
     }
     flag = 0;
     //将内核空间中的数据移动到用户空间
     if (copy_to_user(buf, &glob_var, sizeof(int)))
     {
          up(&sem); //移动数据的操作不完全成功也需要释放信号量
          return - EFAULT;
     }
     up(&sem);//移动数据成功,释放信号量
     return sizeof(int);
}

//************************定义write方法******************************
//glob_write函数中,flip是文件指针,buf是指向用户空间的缓冲区,len表示请求传输数据的长度,
//off指向一个长偏移量类型对象的指针,这个对象指明用户在文件中进行存储操作的位置   
static ssize_t glob_write(struct file *filp, const char *buf, ssize_t len,loff_t *off)
{
     if (down_interruptible(&sem))
     {
          return - ERESTARTSYS;
     }
     //将用户空间的数据移动到内核空间 
     if (copy_from_user(&glob_var, buf, sizeof(int)))
    {
        up(&sem); //移动数据不完全成功也需要释放信号量 
        return - EFAULT;
    }
    up(&sem); //移动数据成功,释放信号量 
    flag = 1;
    //通知数据可获得
    wake_up_interruptible(&outq); //唤醒休眠进程        
    return sizeof(int);
}

//************初始化file_operations结构体*************
struct file_operations glob_fops =
{
    .owner = THIS_MODULE,
    .read = glob_read, 
    .write = glob_write,
};

//*******模块初始化函数*********
static int __init glob_init(void)
{
     int ret;
     ret = register_chrdev(MAJOR_NUM, DEVICE_NAME, &glob_fops); 
     if (ret)
     {
          printk("glob register failure");
     }
     else
    {
         printk("glob register success");
         //init_MUTEX(&sem);
         sema_init(&sem,1); //初始化一个互斥锁,把信号量sem的值设置为1
         init_waitqueue_head(&outq); //初始化等候队列头      
    }
     return ret;
}

//************模块卸载函数**************
static void __exit glob_exit(void)
{
     unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
     printk("glob unregister success!\n");
}

module_init(glob_init);
module_exit(glob_exit);

二,Makefile文件的编写,源代码如下:

obj-m:=glob.o
default:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

三,编译模块:

把上面的glob.c和Makefile两个文件放在同一个文件夹下,我这里的文件夹是“glob阻塞操作实验”,然后进入文件夹,打开终端,登录root,输入指令make,便开始进行模块的编译了,遇到编译错误,多百度,积累经验。

四,加载模块:

在root权限下,在终端输入指令 insmod glob.ko 加载模块,没有任何错误提示的话,加载就成功了,如果遇到设备正忙的错误提示,请更改你的主设备号,然后重新加载便可。输入指令 cat /proc/devices 可以看到里面多了这么一行 :1400 glob  ,这说明我们的模块确实是已经成功加载进了我们的系统内核。

五,编写测试程序测试我们的内核模块:

因为我们需要实现对glob虚拟设备的阻塞访问,所以我们这里编写两个测试程序:glob_read_test.c和glob_write_test.c 。我们最终要实现的效果是,当这两个程序同时访问我们的glob虚拟设备时,只有当成功执行glob_write_test这个进程,往设备里写入了数据,另一个进程glob_read_test,才能读取glob虚拟设备里面的数据。因为最开始glob设备里是不存在数据的,不满足执行进程glob_read_test的条件,这个进程被挂起了直到设备glob里面存在数据,glob_read_test这个进程才会被唤醒,否则一直会被挂起。

glob_read_test.c的源代码如下:

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

glob_write_test.c的源码如下:

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

六,编译我们前面的测试程序:

在终端输入: gcc -o glob_read_test.o glob_read_test.c

gcc -o glob_write_test.o glob_write_test.c

编译我们的测试程序。

七,创建设备文件:

在终端输入命令:mknod /dev/glob c 1400 0 ,前面第一个参数是创建的目录;第二个参数是指设备的类型,c代表字符设备;第三个参数是主设备号;第四个参数是次设备号。

八,测试设备glob:

打开一个终端 输入 ./glob_write_test.o

打开另一个终端 输入 ./glob_read_test.o

这时候你会发现,只有当执行线程glob_write_test,往glob写入数据后,另一个线程glob_read_test才能读取数据,实现了glob_read_test对字符设备glob的阻塞访问,这和我们第五步中的设想完全一致! 效果如下图:

时间: 2024-10-10 02:40:18

linux内核驱动入门之阻塞操作实验:glob的相关文章

linux内核驱动入门的第一个实验:globalvar驱动

一,驱动文件的编写,将文件的名字命名为globalvar.c ,源码如下: #include <linux/module.h> //支持动态加载和卸载模块的头文件 #include <linux/init.h> //最基本的头文件,内核初始化 #include <linux/fs.h>  //文件系统头文件,包括设备注册函数和注销函数等 #include <asm/uaccess.h> //声明了在内核代码和用户空间之间移动数据的函数的头文件 MODULE_

Unix/Linux环境C编程入门教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

1. openSUSE是一款优秀的linux. 2.选择默认虚拟机 3.选择稍后安装操作系统 4.选择linux  opensuse 5. 选择默认虚拟机名称 6.设置处理器为双核. 7.内存设置为2G 8. 选择网络地址转换 9.设置IO控制器 10. 选择默认磁盘类型 11.创建一个新的虚拟磁盘 12.设置磁盘大小 13.选择路径保存虚拟磁盘 14. 完成虚拟机创建 15.设置虚拟机 16.选择opensuse镜像 17.开启虚拟机 18.虚拟机启动 19.安装opensuse 20.安装程

深入浅出~Linux设备驱动中的阻塞和非阻塞I/O

今天意外收到一个消息,真是惊呆我了,博客轩给我发了信息,说是俺的博客文章有特色可以出本书,,这简直让我受宠若惊,俺只是个大三的技术宅,写的博客也是自己所学的一些见解和在网上看到我一些博文以及帖子里综合起来写的,,总之这又给了额外的动力,让自己继续前进,,希望和大家能够分享一些自己的经验,,在最需要奋斗的年级以及在技术的领域踽踽独行的过程中有共同的伙伴继续前进~ 今天写的是Linux设备驱动中的阻塞和非阻塞I/0,何谓阻塞与非阻塞I/O?简单来说就是对I/O操作的两种不同的方式,驱动程序可以灵活的

Linux内核驱动编程

Linux内核驱动编程 2015-02-12 驱动程序基础的东西这儿就不罗嗦了,百度上有更好的资料,此处我们只是注重实际用处. 下面我们开始写程序: 一.初步helloword程序 首先是来一个简单的hello. hello.c代码: 1 /****************************** 2 3 the first program 4 5 Hello World! 6 7 ******************************/ 8 9 #include <linux/mod

(笔记)Linux内核中内存相关的操作函数

linux内核中内存相关的操作函数 1.kmalloc()/kfree() static __always_inline void *kmalloc(size_t size, gfp_t flags) 内核空间申请指定大小的内存区域,返回内核空间虚拟地址.在函数实现中,如果申请的内存空间较大的话,会从buddy系统申请若干内存页面,如果申请的内存空间大小较小的话,会从slab系统中申请内存空间.有关buddy和slab,请参见<linux内核之内存管理.doc> gfp_t flags 的选项

Linux设备驱动入门

设备驱动的作用 任何一个计算机系统的运转都是系统中软硬件共同作用的结果,计算机系统的软硬件互相成就了对方. 应用软件工程师需要看到一个没有硬件的纯粹的软件世界,硬件必须被透明地呈现.驱动工程师来实现硬件对软件工程师的隐形. 设备驱动充当了硬件和应用软件之间的纽带.在系统中没有操作系统的情况下,工程师可以根据硬件设备的特点自行定义接口.在有操作系统的情况下,驱动的架构由相应的操作系统定义,驱动工程师必须按照相应的架构设计驱动,驱动才能良好的整合进操作系统的内核. 无操作系统时的设备驱动 功能单一.

Unix/Linux环境C编程新手教程(12) openSUSECCPP以及Linux内核驱动开发环境搭建

1. openSUSE是一款优秀的linux. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaXRjYXN0Y3Bw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" /> 2.选择默认虚拟机 3.选择稍后安装操作系统 4.选择linux  opensuse 5. 选择默认虚拟机名称 6.设置处理器为双核. watermark/2/text/a

linux内核驱动模型

linux内核驱动模型,以2.6.32内核为例.(一边写一边看的,有点乱.) 1.以内核对象为基础.用kobject表示,相当于其它对象的基类,是构建linux驱动模型的关键.具有相同类型的内核对象构成内核对象集,用kset表示,内核对象集也包含自己的内核对象,从而组成层次化的结构.2.用sysfs文件系统导出到用户空间.内核中的所有内核对象组织成树状,以对象属性为叶子.通过sysfs文件系统,将用户空间对文件的读写操作转化为对内核对象属性的显示和保存方法.从而导出内核对象信息,并提供配置接口.

Linux设备驱动中的阻塞与非阻塞IO与并发控制

Linux设备驱动中的阻塞与非阻塞IO: 1.Linux设备驱动中的阻塞与非阻塞总结:http://m.blog.csdn.net/blog/dongteen/17264501 2.Linux设备驱动中的阻塞与非阻塞IO:http://m.blog.csdn.net/blog/dongteen/17264501 3.Linux设备驱动中的阻塞与非阻塞I/O实例:http://blog.csdn.net/wenhui_/article/details/6817659 linux内核中等待队列: 1