zoukankan      html  css  js  c++  java
  • Linux内核源码分析 -- /dev/mem

    源码版本:Linux kernel 1.0

    分析字符设备 /dev/mem 的驱动程序

    既然是字符设备,万物皆文件,先找 file_operations 结构

    struct file_operations mem_fops

    static struct file_operations mem_fops = {
    	memory_lseek,
    	read_mem,
    	write_mem,
    	NULL,		/* mem_readdir */
    	NULL,		/* mem_select */
    	NULL,		/* mem_ioctl */
    	mmap_mem,
    	NULL,		/* no special open code */
    	NULL,		/* no special release code */
    	NULL		/* fsync */
    };
    

    支持 read write lseek mmap

    一个一个分析

    read_mem

    参数就是直接对 inode 进行操作

    static int read_mem(struct inode * inode, struct file * file,char * buf, int count)
    {
    	unsigned long p = file->f_pos; // 获取 mem 文件的 pos 指针(其实就是我们指定的读取的地址)
    	int read; // 计数器,用来记录拷贝了多少数据
    
    	
    	if (count < 0) // 检查读取 size 的合法性,不能读取一个负数大小
    		return -EINVAL;
    
    	if (p >= high_memory) // 不能读取 high memory
    		return 0;
    	if (count > high_memory - p) // 如果读取的大小比可读内存区域还大的话
    		count = high_memory - p; // 只允许读除了 high memory 以外的内存
    	read = 0;
    
    	// 不能读取 0 号页,所以逐字节把 buf 前 4096 字节置 0(因为现在是在内核态,buf 位于用户态,需要用 put_fs_byte 来操作)
    	while (p < PAGE_SIZE && count > 0) {
    		put_fs_byte(0,buf); 
    		buf++;
    		p++;
    		count--;
    		read++;
    	}
    	// 把 p 为起始地址,大小为 count 的内存数据拷贝到用户态的 buf
    	memcpy_tofs(buf,(void *) p,count);
    	read += count; // 增加计数器的值
    	file->f_pos += read; // 移动文件 pos 指针
    	return read;
    }
    

    write_mem

    static int write_mem(struct inode * inode, struct file * file,char * buf, int count)
    {
    	unsigned long p = file->f_pos; // 获取 mem 文件的 pos 指针(我们要写入数据的地址)
    	int written; // 计数器
    
    	if (count < 0) // 检查写入数据的大小的合法性
    		return -EINVAL;
    	if (p >= high_memory) // 不能写 high memory
    		return 0;
    	if (count > high_memory - p)
    		count = high_memory - p;
    	written = 0;
    	// 不能写 0 号页,直接跳过
    	while (p < PAGE_SIZE && count > 0) {
    		/* Hmm. Do something? */
    		buf++;
    		p++;
    		count--;
    		written++;
    	}
    	memcpy_fromfs((void *) p,buf,count); // 把数据从用户态 buf 拷贝到现在 p 指向的地址(数据块大小为 count)
    	written += count; // 增加计数器的值
    	file->f_pos += written; // 移动指针
    	return count;
    }
    

    memory_lseek

    /*
     * The memory devices use the full 32 bits of the offset, and so we cannot
     * check against negative addresses: they are ok. The return value is weird,
     * though, in that case (0).
     *
     * also note that seeking relative to the "end of file" isn't supported:
     * it has no meaning, so it returns -EINVAL.
     */
    static int memory_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
    {
    	switch (orig) {
    		case 0:
    			file->f_pos = offset;
    			return file->f_pos;
    		case 1:
    			file->f_pos += offset;
    			return file->f_pos;
    		default:
    			return -EINVAL;
    	}
    	if (file->f_pos < 0)
    		return 0;
    	return file->f_pos;
    }
    

    结语

    在对 /dev/mem 进行读写时,file->f_pos 其实就是我们读写的地址,buf 因为是位于用户态,在内核态不能直接读写用户态的数据,所以需要特定的函数去执行,就像高版本的 kernel 的 copy_from_user copy_to_user .......这类函数

  • 相关阅读:
    树莓派Raspberry命令行配置无线网络连接
    Gradient Descent
    下载知乎指定问题的答案并保存图片
    获取JQuery UI tabs中被选中的tabs的方法
    VM603:1 Uncaught SyntaxError: Unexpected token o in JSON at position 1
    PHPstorm配置远程及本地服务器
    ubuntu系统搭建samba服务
    centos6.4升级openssh7.4p1
    nginx服务学习第二章
    centos7.2升级openssh7.9p1
  • 原文地址:https://www.cnblogs.com/crybaby/p/14319075.html
Copyright © 2011-2022 走看看