精华内容
下载资源
问答
  • Linux驱动 读写文件

    千次阅读 2012-05-08 16:01:49
    在VFS的支持下,用户态进程读写任何类型的文件系统都可以使用read和write着两个系统调用,但是在linux内核中没有这样的系统调用我们如何操作文件呢?我们知道read和write在进入内核态之后,实际执行的是sys_read和...

    在VFS的支持下,用户态进程读写任何类型的文件系统都可以使用read和write着两个系统调用,但是在linux内核中没有这样的系统调用我们如何操作文件呢?我们知道read和write在进入内核态之后,实际执行的是sys_read和sys_write,但是查看内核源代码,发现这些操作文件的函数都没有导出(使用EXPORT_SYMBOL导出),也就是说在内核模块中是不能使用的,那如何是好?

    通过查看sys_open的源码我们发现,其主要使用了do_filp_open()函数,该函数在fs/namei.c中,而在改文件中,filp_open函数也是调用了do_filp_open函数,并且接口和sys_open函数极为相似,调用参数也和sys_open一样,并且使用EXPORT_SYMBOL导出了,所以我们猜想该函数可以打开文件,功能和open一样。使用同样的查找方法,我们找出了一组在内核中操作文件的函数,如下:

    功能函数原型
    打开文件struct file *filp_open(const char *filename, int flags, int mode)
    读取文件ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
    写文件ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
    关闭文件int filp_close(struct file *filp, fl_owner_t id)

     

    我们注意到在vfs_read和vfs_write函数中,其参数buf指向的用户空间的内存地址,如果我们直接使用内核空间的指针,则会返回-EFALUT。所以我们需要使用
    set_fs()和get_fs()宏来改变内核对内存地址检查的处理方式,所以在内核空间对文件的读写流程为:

    1. mm_segment_t fs = get_fs();
    2. set_fs(KERNEL_FS);
    3. //vfs_write();
    4. vfs_read();
    5. set_fs(fs);

    下面为一个在内核中对文件操作的例子:

    1. #include <linux/module.h>
    2. #include <linux/init.h>
    3. #include <linux/fs.h>
    4. #include <linux/uaccess.h>
    5. static char buf[] = "你好";
    6. static char buf1[10];
    7.  
    8. int __init hello_init(void)
    9. {
    10.     struct file *fp;
    11.     mm_segment_t fs;
    12.     loff_t pos;
    13.     printk("hello enter\n");
    14.     fp = filp_open("/home/niutao/kernel_file"O_RDWR | O_CREAT0644);
    15.     if (IS_ERR(fp)) {
    16.         printk("create file error\n");
    17.         return -1;
    18.     }
    19.     fs = get_fs();
    20.     set_fs(KERNEL_DS);
    21.     pos = 0;
    22.     vfs_write(fpbufsizeof(buf), &pos);
    23.     pos = 0;
    24.     vfs_read(fpbuf1sizeof(buf), &pos);
    25.     printk("read: %s\n"buf1);
    26.     filp_close(fpNULL);
    27.     set_fs(fs);
    28.     return 0;
    29. }
    30. void __exit hello_exit(void)
    31. {
    32.     printk("hello exit\n");
    33. }
    34.  
    35. module_init(hello_init);
    36. module_exit(hello_exit);
    37.  
    38. MODULE_LICENSE("GPL");
    展开全文
  • Linux驱动:阻塞式读写测试

    千次阅读 多人点赞 2012-01-17 16:28:10
    Linux驱动:阻塞式读写测试 本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明. 环境: 主机:Fedora12 目标板:MINI6410 目标板LINUX内核版本:2.6.38 实现功能: 开辟一个256字节...

    Linux驱动:阻塞式读写测试


    本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明.

    环境:

    主机:Fedora12

    目标板:MINI6410

    目标板LINUX内核版本:2.6.38


    实现功能:

    开辟一个256字节的循环缓冲,可以同时读写,如果数据量不足,则读进程会被阻塞,直到有数据写入


    驱动源代码:

    test_driver.c:

    #include <linux/miscdevice.h>
    #include <linux/delay.h>
    #include <asm/irq.h>
    //#include <mach/regs-gpio.h>
    #include <mach/hardware.h>
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/mm.h>
    #include <linux/fs.h>
    #include <linux/types.h>
    #include <linux/delay.h>
    #include <linux/moduleparam.h>
    #include <linux/slab.h>
    #include <linux/errno.h>
    #include <linux/ioctl.h>
    #include <linux/cdev.h>
    #include <linux/string.h>
    #include <linux/list.h>
    #include <linux/pci.h>
    #include <asm/uaccess.h>
    #include <asm/atomic.h>
    #include <asm/unistd.h>
    #include <linux/major.h>
    
    #include <mach/map.h>
    #include <mach/regs-clock.h>
    #include <mach/regs-gpio.h>
    
    #include <plat/gpio-cfg.h>
    #include <mach/gpio-bank-e.h>
    #include <mach/gpio-bank-k.h>
    #include <mach/gpio-bank-h.h>
    #include <mach/gpio-bank-n.h>
    #include <mach/gpio-bank-l.h>
    #include <mach/gpio-bank-p.h>
    
    #include <linux/device.h>
    
    #include <linux/jiffies.h>
    #include <linux/string.h>
    
    #include <linux/semaphore.h>
    #include <linux/sched.h> 
    #include <linux/wait.h>
    
    #define DEVICE_NAME "test_driver"
    #define T_MAJORS	800
    
    //设备结构
    static struct _Test_Driver_Device
    {
    	struct cdev fun_cdev;
    	//定义缓冲
    	unsigned char *buffer,*end;
    	//读写指针
    	unsigned char *rp,*wp;
    	//读信号量
    	struct semaphore sem_r;
    	//写信号量
    	struct semaphore sem_w;
    	//等待队列头
    	struct wait_queue_head_t *wq;
    };
    struct _Test_Driver_Device *Test_Driver_Device;
    
    static dev_t dev;
    static struct class    *test_class;
    
    //开辟缓存,用来读写
    #define LEN_BUF	256
    static unsigned char Buffer[LEN_BUF];
    
    //功能:初始化缓存
    static void init_buf(void)
    {
    	memset(Buffer,0,LEN_BUF);
    }
    
    //功能:读取缓存
    //返回:读取的字节数
    ssize_t test_driver_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos)
    {
    	int temp1 = 0,temp2 = 0,temp3 = 0;
    
    	//获取信号量
    	if (down_interruptible(&Test_Driver_Device->sem_r))
    	{
    		return -ERESTARTSYS;
    	}
    	
    	//循环防止解除阻塞时的竞争
    	while (Test_Driver_Device->wp == Test_Driver_Device->rp)
    	{
    		//读不到数据
    		//释放信号量
    		up(&Test_Driver_Device->sem_r);
    
    		//判断是否是非阻塞读
    		if (filp->f_flags & O_NONBLOCK)
    		{
    			return -EAGAIN;
    		}
    		
    		//如果是阻塞式读,则阻塞
    		if (wait_event_interruptible(Test_Driver_Device->wq,Test_Driver_Device->wp != Test_Driver_Device->rp))
    		{
    			return -ERESTARTSYS;
    		}
    
    		//获取信号量
    		if (down_interruptible(&Test_Driver_Device->sem_r))
    		{
    			return -ERESTARTSYS;
    		}
    	}
    
    	if (Test_Driver_Device->wp > Test_Driver_Device->rp)
    	{
    		count = min(count,(size_t)(Test_Driver_Device->wp - Test_Driver_Device->rp));
    		//拷贝数据到用户空间
    		if (copy_to_user(buf,Test_Driver_Device->rp,count))
    		{
    			return -EFAULT;
    		}
    		Test_Driver_Device->rp += count;
    	}
    	else
    	{
    		temp1 = Test_Driver_Device->end - Test_Driver_Device->rp + 1;
    		temp2 = Test_Driver_Device->wp - Test_Driver_Device->buffer;
    		if (count <= temp1)
    		{
    			//拷贝数据到用户空间
    			if (copy_to_user(buf,Test_Driver_Device->rp,count))
    			{
    				return -EFAULT;
    			}
    			Test_Driver_Device->rp += count;
    		}
    		else
    		{
    			//拷贝数据到用户空间
    			if (copy_to_user(buf,Test_Driver_Device->rp,temp1))
    			{
    				return -EFAULT;
    			}
    			Test_Driver_Device->rp = Test_Driver_Device->buffer;
    			temp3 = min(count - temp1,temp2);
    			count = temp1 + temp3;
    			if (copy_to_user(buf + temp1,Test_Driver_Device->rp,temp3))
    			{
    				return -EFAULT;
    			}
    			Test_Driver_Device->rp += temp3;
    		}
    	}
    
    	if (Test_Driver_Device->rp == Test_Driver_Device->end + 1)
    	{
    		Test_Driver_Device->rp = Test_Driver_Device->buffer;
    	}
    	//释放信号量
    	up(&Test_Driver_Device->sem_r);
    	printk (DEVICE_NAME"\tjdh:rp zhi zhen = %d\n",Test_Driver_Device->rp - Test_Driver_Device->buffer);
    
    	return count;
    }
    
    //功能:写入缓存
    //返回:写入的字节数
    ssize_t test_driver_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_pos)
    {
    	int temp1 = 0,temp2 = 0;;
    
    	//判断需要写入的字节数是否大于缓存
    	if (count > LEN_BUF)
    	{
    		return -ENOMEM;
    	}
    
    	//获取信号量
    	if (down_interruptible(&Test_Driver_Device->sem_w))
    	{
    		return -ERESTARTSYS;
    	}
    
    	//写入缓存
    	if (count <= (Test_Driver_Device->end - Test_Driver_Device->wp + 1))
    	{
    		//从用户空间拷贝数据
    		if (copy_from_user(Test_Driver_Device->wp,buf,count))
    		{
    			return -EFAULT;
    		}
    		Test_Driver_Device->wp += count;
    	}
    	else
    	{
    		temp1 = Test_Driver_Device->end - Test_Driver_Device->wp + 1;
    		temp2 = count - temp1;
    		//从用户空间拷贝数据
    		if (copy_from_user(Test_Driver_Device->wp,buf,temp1))
    		{
    			return -EFAULT;
    		}
    		Test_Driver_Device->wp = Test_Driver_Device->buffer;
    		//从用户空间拷贝数据
    		if (copy_from_user(Test_Driver_Device->wp,buf + temp1,temp2))
    		{
    			return -EFAULT;
    		}
    		Test_Driver_Device->wp += temp2;
    	}
    
    	if (Test_Driver_Device->wp == Test_Driver_Device->end + 1)
    	{
    		Test_Driver_Device->wp = Test_Driver_Device->buffer;
    	}
    	//唤醒阻塞进程
    	wake_up_interruptible(&Test_Driver_Device->wq);
    	//释放信号量
    	up(&Test_Driver_Device->sem_w);
    	printk (DEVICE_NAME"\tjdh:wp zhi zhen = %d\n",Test_Driver_Device->wp - Test_Driver_Device->buffer);
    
    	return count;
    }
    
    static struct file_operations io_dev_fops = {
    	.owner = THIS_MODULE,
    	.write = test_driver_write,
    	.read = test_driver_read,
    };
    
    static int __init dev_init(void)
    {
    	int ret;
    	unsigned temp;
    
    	init_buf();
    
    	//分配结构体
    	Test_Driver_Device = kmalloc(sizeof(struct _Test_Driver_Device),GFP_KERNEL);
    	if (!Test_Driver_Device)
    	{
    		unregister_chrdev_region(dev,1);
        		device_destroy(test_class, dev); 
        		class_destroy(test_class);
    
    		return -ENOMEM;
    	}
    	
    	//定义缓冲的开始和结束的指针
    	Test_Driver_Device->buffer = Buffer;
    	Test_Driver_Device->end = Buffer + LEN_BUF - 1;
    	Test_Driver_Device->rp = Test_Driver_Device->buffer;
    	Test_Driver_Device->wp = Test_Driver_Device->buffer;
    	//初始化读信号量
    	sema_init(&Test_Driver_Device->sem_r,1);
    	//初始化写信号量
    	sema_init(&Test_Driver_Device->sem_w,1);
    	//初始化等待队列头
    	init_waitqueue_head(&Test_Driver_Device->wq);
    
    	dev = MKDEV(T_MAJORS,0);
    	cdev_init(&Test_Driver_Device->fun_cdev,&io_dev_fops);
    	ret = register_chrdev_region(dev,1,DEVICE_NAME);
    	if (ret < 0) return 0;
    	ret = cdev_add(&Test_Driver_Device->fun_cdev,dev,1);
    	if (ret < 0) return 0;
    
    	printk (DEVICE_NAME"\tjdh:test_driver initialized!!\n");
    
    	test_class = class_create(THIS_MODULE, "test_class1"); 
    	if (IS_ERR(test_class)) 
    	{ 
    		printk(KERN_INFO "create class error\n"); 
    		return -1; 
    	} 
    	device_create(test_class, NULL, dev, NULL, "test_driver"); 
    
    	return ret;
    }
    
    static void __exit dev_exit(void)
    {
    	unregister_chrdev_region(dev,1);
    
        	device_destroy(test_class, dev); 
        	class_destroy(test_class);
    }
    
    module_init(dev_init);
    module_exit(dev_exit);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("JDH");


    用户程序:

    分为写程序,和读程序。写程序每次写入100字节,读每次读取50字节。

    test_driver_write.c:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include "stdio.h"
    #include "sys/types.h"
    #include "sys/ioctl.h"
    #include "stdlib.h"
    #include "termios.h"
    #include "sys/stat.h"
    #include "fcntl.h"
    #include "sys/time.h"
    
    #include <string.h>  
    #include <stdlib.h> 
    
    int main(int argc, char** argv)
    {
    	int fd;
    	unsigned char buf[256];
    	int i = 0;
    	int err = 0;
    
    	fd = open("/dev/test_driver",O_RDWR);
    	if (fd < 0)
    	{
    		perror("open test_driver error");
    		exit(1);
    	}
    	
    	//写入数据
    	for (i = 0;i < 256;i++)
    	{
    		buf[i] = i;
    	}
    	err = write(fd,buf,100);
    	printf("write %d\n",err);
    	perror("open 22 error");
    
    	close(fd);
    	
    	return 0;
    }
    
    

    test_driver_read.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include "stdio.h"
    #include "sys/types.h"
    #include "sys/ioctl.h"
    #include "stdlib.h"
    #include "termios.h"
    #include "sys/stat.h"
    #include "fcntl.h"
    #include "sys/time.h"
    
    #include <string.h>  
    #include <stdlib.h> 
    
    int main(int argc, char** argv)
    {
    	int fd;
    	unsigned char temp[256];
    	int i = 0;
    	int err = 0;
    
    	fd = open("/dev/test_driver",O_RDWR);
    	if (fd < 0)
    	{
    		perror("open test_driver error");
    		exit(1);
    	}
    
    	//读取数据
    	err = read(fd,temp,50);
    	printf("read %d\n",err);
    	perror("open 33error");
    
    	//输出
    	for (i = 0;i < 50;i++)
    	{
    		printf("%d\t",(int)temp[i]);
    	}
    
    	close(fd);
    	
    	return 0;
    }
    


    测试说明:

    将编译所得的可执行文件test_driver_write和test_driver_read上传到开发板

    同时运行,测试读写





    展开全文
  • linux驱动-映射进程空间

    千次阅读 2017-02-12 10:11:44
    简述:内核映射进程空,就是由进程分配好空间(属于进程独占资源)后,将用户空间虚拟地址,传递到内核,然后内核映射成内核虚拟地址直接访问,此时内核访问的物理空间是位于用户空间。这样的好处是,不再是内核将...

    简述:

    内核映射进程空间,就是由进程分配好空间(属于进程独占资源)后,将用户空间虚拟地址,传递到内核,然后内核映射成内核虚拟地址直接访问,此时内核访问的物理空间是位于用户空间。这样的好处是,内核直接访问进程空间,减少copy动作。

    接口:

    • 接口要包含的头文件:
    #include <linux/mm.h>
    • 函数接口:
    long get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
                unsigned long start, unsigned long nr_pages,
                int write, int force, struct page **pages,
                struct vm_area_struct **vmas);

    功能:内核用来映射进程内存空间
    第一个参数: tsk是进程控制块指针,这个参数几乎一直作为 current 传递。
    第二个参数: 一个内存管理结构的指针, 描述被映射的地址空间. mm_struct 结构是捆绑一个进程的虚拟地址空间所有部分. 对于驱动的使用, 这个参数应当一直是current->mm。
    第三个参数: start 是(页对齐的)用户空间缓冲的地址。要映射进程空间虚拟地址的起始地址。
    第四个参数: nr_pages是映射进程空间的大小,单位页。
    第五个参数: write是内核映射这段进程空间来读还是写,非0(一般就是1)表示用来写,当然,对于用户空间就只能是读了;为0表示用来读,此时用户空间就只能是写了。
    第六个参数: force 标志告知 get_user_pages 来覆盖在给定页上的保护, 来提供要求的
    权限; 驱动应当一直传递 0 在这里。
    第七个参数: pages是这个函数的输出参数,映射完成后,pages是指向进程空间的页指针数组,如pages[1],下标最大是nr_pages-1。注意,内核要用pages,还需要映射成内核虚拟地址,一般用kmap(),kmap_atomic()
    第八个参数: vmas也是个输出参数,vm_area_struct结构体,是linux用来管理虚拟内存的,映射完成后关于虚拟内存的信息就在这结构体里面。如果驱动不用,vmas可以是NULL。
    返回值: 返回实际映射的页数,只会小于等于nr_pages。

    映射:

    • 映射的时候要上进程读者锁:
      进程旗标(锁/信号量),通过current->mm->mmap_sem来获得。
      如:
    down_read(&current->mm->mmap_sem);
    result = get_user_pages(current, current->mm, ...);
    up_read(&current->mm->mmap_sem);

    如果内核映射的空间,用来写,写的时候要上进程写锁,在写的时候去读,就会被阻塞:

    down_write(current->mm->mmap_sem);
    ...
    //向映射的空间写数据
    //up_write(current->mm->mmap_sem);

    current->mm->page_table_lock.rlock也可以用这个自旋锁实现的读者写者,具体情况考虑。

    释放映射:

    if (! PageReserved(page))
        SetPageDirty(page);
    page_cache_release(struct page *page);

    PageReserved(): 判断是否为保留页,是保留页返回非0,不是返回0。在我们一般映射的页,没经过处理,都不是保留页,返回0。
    SetPageDirty(): 简单来说,就是告诉系统这页被修改。

    PageReserved(),SetPageDirty()定义在include/linux/page-flags.h,对他们的作用理解,兵不是很深,在一般的驱动中可有可无,安全起见,就按照上面形式放在哪里。

    page_cache_release()定义在include/linux/pagemap.h,只能是一次释放一个page,多个page用循环多次调用。

    思考:

    这种映射,主要是用在进程直接I/O,进程直接读写用户空间内存,就可以达到读写I/O。但是也要具体情况看,相对这种映射对系统开销,还是比较大的。
    get_user_pages的用法例子,可以看drivers/scsi/st.c

    用这种映射来实现读者写者,进程是读者,驱动是写者。
    在“linux驱动—file_operations异步读写aio_read、aio_write”这章,描述的异步例子基础上做,

    原始例子如下:

    struct kiocb *aki;
    struct iovec *aiov;
    loff_t aio_off = 0;
    struct workqueue *aiowq;
    
    void data_a(struct work_struct *work);
    DECLARE_DELAYED_WORK(aio_delaywork,data_a);
    
    ssize_t d_read(struct file *f, char __user *buf, size_t n, loff_t *off)
    {
    }
    void data_a(struct work_struct *work)
    {
        int ret = 0;
        ret = d_read(aki->ki_filp,aiov->iov->iov_base,n,&off);
        aio_complete(aki,ret ,0);
    }
    ssize_t d_aio_read(struct kiocb *ki, const struct iovec *iovs, unsigned long n, loff_t off)
    {
        if(is_sync_kiocb(ki))
            return d_read(ki->ki_filp,iovs->iov->iov_base,n,&off);
        else
        {
            aki = ki;
            aiov = iovs;
            aio_off = off;
            queue_delayed_work(aiowq,aio_delaywork,100);
            return -EIOCBQUEUED;//一般都返回这个,
        }
    }
    void init_aio()
    {
        aiowq= create_workqueue("aiowq");
    }

    用上get_user_pages()后,将变成:

    #include <linux/list.h>
    #include <linux/aio.h>
    #include <linux/workqueue.h>
    #include <linux/mm.h>
    
    struct kiocb *aki;
    //struct iovec *aiov;
    //loff_t aio_off = 0;
    struct workqueue *aiowq;
    struct page **aiopages;
    
    
    
    LIST_HEAD(custom_aa);
    
    struct custom_async{
        list_head list;
        task_struct *tsk;
        struct page **pg;
        long page_num;
        ssize_t size;   
    };
    
    void data_a(struct work_struct *work);
    DECLARE_DELAYED_WORK(aio_delaywork,data_a);
    DECLARE_DELAYED_WORK(aio_delaywork_c,async_d_writedata);
    
    struct file_operations {
        .owner = THIS_MODE,
        .read = d_read,
        .aio_read = d_aio_read,
        ...
        ..
    };
    
    
    ssize_t d_read(struct file *f, char __user *buf, size_t n, loff_t *off)
    {
    }
    ssize_t async_d_writedata(struct work_struct *work)
    {
        int i,n;
        void *p = NULL;
        struct page **temp = NULL;
        struct custom_async *t = NULL;
        struct list_head *tmp = NULL;
        list_for_each(custom_aa,tmp){
            t = container_of(tmp,custom_async,list);
            for(i=0;i<t->page_num;i++)
            {
                temp = t->pg;
                p = kmap(temp[i]);
                down_write(&t->tsk->mm->mmap_sem);
                ...
                //向p指定的空间写数据 ,   n为写了多少个字节数据
                ...
                t->size = n;
                up_write(&t->tsk->mm->mmap_sem);
                kunmap(aio_pages[i]);
            }
        }
        queue_delayed_work(aiowq,aio_delaywork_c,100);  
    }
    void data_a(struct work_struct *work)
    {
        int ret = 0;
        struct custom_async *t;
        //ret = d_read(aki->ki_filp,aiov->iov->iov_base,n,&off);
    
        t = container_of(custom_aa.next,custom_async,list);
        async_d_writedata();     
        ret = t->size;
        aio_complete(aki,ret ,0);
    }
    ssize_t d_aio_read(struct kiocb *ki, const struct iovec *iovs, unsigned long n, loff_t off)
    {   
        if(is_sync_kiocb(ki))
            return d_read(ki->ki_filp,iovs->iov->iov_base,n,&off);
        else
        {
            aki = ki;
            //aiov = iovs;
            //aio_off = off;
            struct custom_async *p;
    
            p = kmalloc(sizeof(struct custom_async));
            memset(p,0,sizeof(struct custom_async));
            INIT_LIST_HEAD(&p->list);
            list_add(&p->list,&custom_aa); 
            p->tsk = current;       
            down_read(&current->mm->mmap_sem);
            pagenum = get_user_pages(current,current->mm,iovs->iov->iov_base,
                            n/PAGE_SIZE+(n%PAGE_SIZE)?1:0,
                            1,0,p->pg,NULL);
            up_read(&current->mm->mmap_sem);       
            queue_delayed_work(aiowq,aio_delaywork,100);
            return -EIOCBQUEUED;//一般都返回这个,
        }
    }
    void init_aio()
    {
        aiowq= create_workqueue("aiowq");
        INIT_LIST_HEAD(custom_aa);
    }
    void exit()
    {
        int i;
        ...
        ..
        for(i=0;i<pagenum;i++)
        {
            if (! PageReserved(aiopages[i]))
                SetPageDirty(aiopages[i]);
            page_cache_release(aiopages[i]);
        }
        ...
        ..
    }

    上面的例子,进程都可以通过异步调用来申请映射,当进程收到一次异步调用完成后,不用再次异步调用,只需要读就可以,驱动会自动的向里面写数据。只要来异步调用申请过一次的进程,当有数据时,都会向每个进程空间写数据。典型的读者写者,进程是读者,驱动是写者。

    上面例子,只是表达了处理逻辑,没有编译过,模块退出时,释放不完全。

    对比一般的异步,内核是直接向进程空间写数据,不用在数据完成后,还需要一次copy,理论上会快些。

    展开全文
  • 所谓的内存映射就是把物理内存映射到进程的地址空间之内,这些应用程序就可以直接使用输入输出的地址空间,从而提高读写的效率。Linux提供了mmap()函数,用来映射物理内存。  在驱动程序中,应用程序以设备文件为...
    Linux设备驱动之内存映射 
    
    [日期:2011-04-11] 
    来源:Linux社区  作者:chenjin_zhong 
    1. 内存映射 
    所谓的内存映射就是把物理内存映射到进程的地址空间之内,这些应用程序就可以直接使用输入输出的地址空间,从而提高读写的效率。Linux提供了mmap()函数,用来映射物理内存。 
    在驱动程序中,应用程序以设备文件为对象,调用mmap()函数,内核进行内存映射的准备工作,生成vm_area_struct结构体,然后调用设备驱动程序中定义的mmap函数。 
     
    2. 映射的种类 
    把同一个物理地址映射为虚拟地址有两种方法,第一种是mmap()函数将物理地址映射到进程的虚拟地址空间中去,第二种方法为ioremap()函数映射到内核虚拟地址上的方法。 
    mmap映射的方法: 
    有两种方法建立页表,一次性建立页表,可以调用函数remap_pfn_range和每次建立一个页的页表,调用函数nopage。 
    remap_pfn_range: 
    这个函数的功能是一次性建立新的页表去映射物理地址。 
    int remap_pfn_range(struct vma_area_struct* vma,unsigned long virt_addr,unsigned long pfn,unsigned long size,pgprot_t prot); 
    返回值:映射成功时返回0,否则返回一个错误的负数代码。 
    vma 物理地址被映射到的虚拟内存区域 
    virt_addr 被映射到用户空间的起始虚拟地址。页表建立的范围在virt_addr到virt_addr+size 
    pfn 对应物理地址的页框号,一般是vma->vm_pgoff域。 
    size 被映射区域的字节大小 
    prot  vma->vm_page_pro

    nopage: 
    struct page *(*nopage) (struct vm_area_struct *vma,unsigned long address,int *type); 


    vm_area_struct:虚拟内存区域 
    address:发生page fault的进程空间的虚拟地址 
    type 返回page fault可处理类型的地址 
    get_page(struct page* pageptr); 
    增加被映射页的引用次数。
    展开全文
  • Linux下直接读写物理地址内存

    千次阅读 2018-07-21 17:52:14
    虚拟 转 物理地址virt_to_phys( *addr );...Linux内核里提供的/dev/mem驱动,为我们读写内存物理地址,提供了一个渠道。下面讲述2种利用mem设备文件进行物理地址读写的方法,一种是设备驱动的方法,另一...
  • linux驱动设备在多进程打开问题

    千次阅读 2015-08-07 17:51:24
    一、问题 一个spi设备,单独一个进程打开就行读写没有问题,但是多进程打开却死锁了 当然该设备是修改过的,并不是原生态的spi驱动
  • Linux驱动中如何对文件进行读写

    千次阅读 2018-04-11 14:39:19
    使用filp_open()和struct file可以在驱动模块中访问其他文件。注意: 在调用pfilp-&gt;f_op-&gt;read和pfilp-&gt;f_op-&gt;write等对文件的操作之前,应该先备份pold_fs,然后设置set_fs(KERNEL_DS)...
  • 但是,由于应用程序不能直接操作设备硬件地址,所以操作系统提供了这样的一种机制——内存映射,把设备地址映射到进程虚拟地址,mmap就是实现内存映射的接口。 操作设备还有很多方法,如ioctl...
  • linux 内核驱动中对文件的读写

    千次阅读 2017-08-20 14:45:15
    有时候需要在Linux kernel--大多是在需要调试的驱动程序--中读写文件数据。在kernel中操作文件没有标准库可用,需要利用kernel的一些函数,这些函数主 要有: filp_open() filp_close(), vfs_read() v
  • 从单片机到ARM Linux驱动——Linux驱动入门篇

    万次阅读 多人点赞 2020-10-24 10:58:43
    我们需要熟悉Linux操作系统,知道Linux的常用命令、文件系统、Linux网络、多线程/多进程,同时要会用vi编辑器、gcc编译器、shell脚本和一些简单的makefile的编写,在这些的基础之上进行Linux驱动开发的学习就会如步...
  • Linux驱动学习(4-字符设备-自动创建字符设备并读写
  • Linux设备驱动内存映射--mmap--转

    千次阅读 2011-12-16 16:28:47
    所谓的内存映射就是把物理内存映射到进程的地址空间之内,这些应用程序就可以直接使用输入输出的地址空间,从而提高读写的效率。Linux提供了mmap()函数,用来映射物理内存。 在驱动程序中,应用程序以设备文件为...
  •  I/O端口是驱动程序与许多设备之间的通信方式,Linux的内核为我们提供了I/O端口分配的操作接口,但对PCI设备来讲,它的配置地址空间已经为其指定了I/O端口范围,不需要额外的分配操作。 下列代码通过访问I/O内存...
  • linux驱动

    千次阅读 2013-04-18 09:04:05
    1. Linux驱动程序设计模式(40%)2.内核相关知识(30%)3. 硬件相关知识(30%)z 驱动分类:字符,网络,块 字符设备:以字节为最小单位,不可以乱序读写。 块设备: 一次传送一个整体数据(512字节),...
  • linux文件读写浅析

    千次阅读 2013-05-11 19:51:19
    在《linux内核虚拟文件系统浅析》这篇文章中,我们看到文件是如何被打开、文件的读写是如何被触发的。 对一个已打开的文件fd进行read/write系统调用时,内核中该文件所对应的file结构的f_op->read/f_op->write被...
  • (a)Linux驱动程序学习 知识结构: 1. Linux驱动程序设计模式(40%) 2. 内核相关知识(30%) 3. 硬件相关知识(30%) (b)驱动分类: ①字符设备:  字符设备是一种按字节来访问的设备,字符驱动则负责驱动字符设备,...
  • file_operations中read,write是同步读写,异步读写用接口是aio_read、aio_wirte(在4.5版本中发现已经把名字改成read_iter、write_iter)。异步读写对应的系统调用API:int aio_read(struct aiocb *__aiocbp); int ...
  • Linux驱动编程】mmap方法

    千次阅读 2020-04-03 00:10:11
    一个设备,一般涉及到帧缓存会考虑实现映射接口,常见的LCD<font color=cloor> framebuffer设备的显存空间,经过映射后,用户进程可以直接操作进程内存空间将LCD显示数据写入,提高刷新效率,节省CPU拷贝内存开销。
  • linux驱动开发流程

    万次阅读 2012-01-31 10:09:58
    嵌入式linux驱动开发流程 嵌入式系统中,操作系统是通过各种驱动程序来驾驭硬件设备的。设备驱动程序是操作系统内核和硬件设备之间的接口,它为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个...
  • linux驱动开发--I/O内存的访问流程

    千次阅读 2015-02-01 17:28:17
    设备通常会提供一组寄存器来用于控制设备、读写设备和获取设备状态,既控制寄存器、数据寄存器和状态寄存器、这些寄存器可能位于I/O空间,也可能位于内存空间。当位于I/O空间时,通常被称为I/O端口,位于内存空间时...
  • linux文件读写流程

    千次阅读 2017-03-29 17:26:56
    在《linux内核虚拟文件系统浅析》这篇文章中,我们看到文件是如何被打开、文件的读写是如何被触发的。 对一个已打开的文件fd进行read/write系统调用时,内核中该文件所对应的file结构的f_op->read/f_op->write被调用...
  • 每一个进程都有着自己独立的地址空间,比如程序之前申请了一块内存,当调用fork函数之后,父进程和子进程所使用的是不同的内存。因此进程间的通信,不像线程间通信那么简单。但是共享内存编程接口可以让一个进程使用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 58,081
精华内容 23,232
关键字:

linux驱动读写进程内存

linux 订阅