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 .......这类函数

原文地址:https://www.cnblogs.com/crybaby/p/14319075.html