《操作系统课程设计》第6章/proc文件系统——编写内核模块,实现在/proc目录下添加文件

procfs_example

前言

本程序是为了《操作系统课程设计》(浙江大学出版社09年版 李善平著)第6章/proc实验而写,因书上及网络上的现有教程基本上都是10年以前内核2点几的,不再适用,故在3.10.0下写了此内核模块,发布出来。更高版本内核可能会有一些小问题,但大体相似,可自行百度解决。

功能描述

编写一个内核模块,该模块在/proc目录下创建我们自己的目录proc_example 然后在这个目录下创建三个普通文件(foo、bar、jiffies)和一个文件链接(jiffies_too)。foo和bar是两个可读写文件,各自实现读取操作函数与写入操作函数。jiffies是一个只读文件,取得当前系统时间jiffies。jiffies_too为文件jiffies的一个符号链接。

代码

procfs_example.c

/*
 *procfs_example.c
 *
 *test kernel version:3.10.0
 *
 *author:zhangle
 *
 *date:2019/5/5
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/jiffies.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
//如果是高版本内核,copy_to_user可能会报错,原因是该函数定义在include/linux/uaccess.h中,请将上一行改为#include <linux/uaccess.h>

#define MODULE_VERS "1.0"
#define MODULE_NAME "procfs_example"
#define FOOBAR_LEN 8

struct fb_data_t{
	char name[FOOBAR_LEN+1];
	char value[FOOBAR_LEN+1];
};

static struct proc_dir_entry *example_dir, *foo_file, *bar_file, *jiffies_file, *symlink;
struct fb_data_t foo_data, bar_data;
int foo_len,foo_temp,bar_len,bar_temp;
int jiff_temp=-1;
char tempstr[FOOBAR_LEN*2+5];

//===========jiffies文件操作函数=========
static ssize_t read_jiffies_proc(struct file *filp,char __user *buf,size_t count,loff_t *offp ){
	printk(KERN_INFO"count=%d  jiff_temp=%d\n", count, jiff_temp);
	char tempstring[100]="";
	if (jiff_temp!=0)
		jiff_temp=sprintf(tempstring, "jiffies=%ld\n", jiffies);	//jiffies为系统启动后经过的时间戳
	if (count>jiff_temp)
		count=jiff_temp;
	jiff_temp=jiff_temp-count;

	printk(KERN_INFO"count=%d  jiff_temp=%d\n", count, jiff_temp);
	copy_to_user(buf, tempstring, count);
	if (count==0)
		jiff_temp=-1;	//读取结束后temp变回-1
	return count;
}
static const struct file_operations jiffies_proc_fops={
	.read=read_jiffies_proc
};

//=============foo文件操作函数===========
static ssize_t read_foo_proc(struct file *filp,char __user *buf,size_t count,loff_t *offp ) {
	printk(KERN_INFO"count=%d\n", count);
	//调整count与temp的值,具体过程自行查看printk输出的信息来分析
	if (count>foo_temp)
		count=foo_temp;
	foo_temp=foo_temp-count;
	//拼接tempstr字符串
	strcpy(tempstr, foo_data.name);
	strcat(tempstr, "='");
	strcat(tempstr, foo_data.value);
	strcat(tempstr, "'\n");

	printk(KERN_INFO"count=%d  length(tempstr)=%d\n", count, strlen(tempstr));
	//向用户空间写入tempstr
	copy_to_user(buf, tempstr, count);
	//如果count=0,读取结束,temp回归为原来的值
	if (count==0)
		foo_temp=foo_len+4;
	return count;
}
static ssize_t write_foo_proc(struct file *filp,const char __user *buf,size_t count,loff_t *offp ){
	int len;
	if (count>FOOBAR_LEN)
		len=FOOBAR_LEN;
	else
		len=count;
	//将数据写入foo_data的value字段
	if (copy_from_user(foo_data.value, buf, len))
		return -EFAULT;
	foo_data.value[len-1]='\0';	//减1是因为除去输入的回车
	//更新len与temp值
	foo_len=strlen(foo_data.name)+strlen(foo_data.value);
	foo_temp=foo_len+4;
	return len;
}
static const struct file_operations foo_proc_fops={
	.read=read_foo_proc,
	.write=write_foo_proc
};

//===============bar文件操作函数============
static ssize_t read_bar_proc(struct file *filp,char __user *buf,size_t count,loff_t *offp ) {	
	printk(KERN_INFO"count=%d\n", count);
	if (count>bar_temp)
		count=bar_temp;
	bar_temp=bar_temp-count;

	strcpy(tempstr, bar_data.name);
	strcat(tempstr, "='");
	strcat(tempstr, bar_data.value);
	strcat(tempstr, "'\n");
	printk(KERN_INFO"count=%d  length(tempstr)=%d\n", count, strlen(tempstr));
	copy_to_user(buf, tempstr, count);
	if (count==0)
		bar_temp=bar_len+4;
	return count;
}
static ssize_t write_bar_proc(struct file *filp,const char __user *buf,size_t count,loff_t *offp ){
	int len;
	if (count>FOOBAR_LEN)
		len=FOOBAR_LEN;
	else
		len=count;
	if (copy_from_user(bar_data.value, buf, len))
		return -EFAULT;
	bar_data.value[len-1]='\0';
	bar_len=strlen(bar_data.name)+strlen(bar_data.value);
	bar_temp=bar_len+4;
	return len;
}
static const struct file_operations bar_proc_fops={
	.read=read_bar_proc,
	.write=write_bar_proc
};

//===============模块init函数=============
static int __init init_procfs_example(void){
	int rv=0;
	
	//=========create dirctory===========
	example_dir=proc_mkdir(MODULE_NAME, NULL);
	if (example_dir==NULL){
		rv=-ENOMEM;
		goto out;
	}

	//======create jiffies(read only)=====
	jiffies_file=proc_create("jiffies", 0444, example_dir, &jiffies_proc_fops);
	if (jiffies_file==NULL){
		rv=-ENOMEM;
		goto no_jiffies;
	}
	
	//=============create foo=============
	strcpy(foo_data.name, "foo");
	strcpy(foo_data.value, "foo");
	foo_len=strlen(foo_data.name)+strlen(foo_data.value);
	foo_temp=foo_len+4;		//加4是因为拼接tempstr字符串时多了=‘’\n这四个字符
	foo_file=proc_create("foo", 0, example_dir, &foo_proc_fops);
	if (foo_file==NULL){
		rv=-ENOMEM;
		goto no_foo;
	}

	//============create bar===============
	strcpy(bar_data.name, "bar");
	strcpy(bar_data.value, "bar");
	bar_len=strlen(bar_data.name)+strlen(bar_data.value);
	bar_temp=bar_len+4;
	bar_file=proc_create("bar", 0, example_dir, &bar_proc_fops);
	if (bar_file==NULL){
		rv=-ENOMEM;
		goto no_bar;
	}
	
	//===========create symlink==============
	symlink=proc_symlink("jiffies_too", example_dir, "jiffies");
	if (symlink==NULL){
		rv=-ENOMEM;
		goto no_symlink;
	}

	//=============all okay=================
	printk(KERN_INFO"%s%s initialised\n", MODULE_NAME, MODULE_VERS);
	return 0;

no_symlink:
	remove_proc_entry("bar", example_dir);
no_bar:
	remove_proc_entry("foo", example_dir);
no_foo:
	remove_proc_entry("jiffies", example_dir);
no_jiffies:
	remove_proc_entry(MODULE_NAME, NULL);
out:
	return rv;
}

//==============模块cleanup函数==============
static void __exit cleanup_procfs_example(void){
	remove_proc_entry("jiffies_too", example_dir);
	remove_proc_entry("bar", example_dir);
	remove_proc_entry("foo", example_dir);
	remove_proc_entry("jiffies", example_dir);
	remove_proc_entry(MODULE_NAME, NULL);
	printk(KERN_INFO"%s%s removed\n", MODULE_NAME, MODULE_VERS);
}

module_init(init_procfs_example);
module_exit(cleanup_procfs_example);
MODULE_AUTHOR("Zhang Le");
MODULE_DESCRIPTION("proc filesystem example");

Makefile

TARGET=procfs_example
KDIR :=/lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
obj-m :=$(TARGET).o
default:
	make -C $(KDIR) M=$(PWD) modules

clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order

源码地址
我的GitHub

读写示例

write操作必须在root用户下进行,否则权限不够
示例

备注

测试使用的内核版本为3.10.0,其他版本内核可能会在读写操作函数的参数、copy_to_user、copy_from_user处报错,这是内核版本定义不同导致的,请自行百度函数原型

参考网址

https://blog.csdn.net/larry233/article/details/88093780?tdsourcetag=s_pctim_aiomsg
https://blog.csdn.net/caotuo_csdn/article/details/79172663

  • 5
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,这是一个比较复杂的问题,需要较为深入的Linux系统编程知识。我会尽可能详细地回答你的问题。 首先,我们需要了解一下/proc文件系统。在Linux系统中,/proc是一个虚拟文件系统,它不是真正的文件系统,而是内核在运行时动态生成的一个目录树,它提供了一种访问内核数据结构的机制。通过在/proc目录下访问各种文件和子目录,我们可以获取到各种内核参数的信息。 在C程序中,我们可以使用系统调用open()函数和read()函数来访问/proc文件系统。具体的步骤如下: 1. 打开/proc目录,获取其文件描述符。可以使用以下代码实现: ```c int fd = open("/proc", O_RDONLY); if (fd < 0) { printf("Failed to open /proc\n"); return -1; } ``` 2. 遍历/proc目录下的所有子目录文件,找到我们需要修改的内核参数所对应的文件。可以使用以下代码实现: ```c struct dirent *dir; while ((dir = readdir(fd)) != NULL) { if (dir->d_type == DT_DIR && strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0) { // 进入子目录 char path[256]; snprintf(path, sizeof(path), "/proc/%s", dir->d_name); int sub_fd = open(path, O_RDONLY); if (sub_fd < 0) { printf("Failed to open %s\n", path); continue; } // 在子目录中查找指定的文件 struct dirent *sub_dir; while ((sub_dir = readdir(sub_fd)) != NULL) { if (strcmp(sub_dir->d_name, "desired_file") == 0) { // 找到了文件,进行修改 // ... break; } } close(sub_fd); } } ``` 3. 对找到的文件进行修改。具体的操作取决于我们要修改的参数的类型和格式。假设我们要修改的参数是一个整数,那么可以使用以下代码实现: ```c int new_value = 12345; char buf[256]; snprintf(buf, sizeof(buf), "%d", new_value); int len = strlen(buf); lseek(file_fd, 0, SEEK_SET); write(file_fd, buf, len); ``` 这里的file_fd是我们找到的文件文件描述符。 4. 关闭文件描述符,释放资源。可以使用以下代码实现: ```c close(fd); ``` 综上所述,这就是一个简单的通过Linux文件系统调用,用户态下访问/proc文件系统,获得内核参数修改可以修改的参数的C程序的实现方法。当然,实际的程序可能会更加复杂,需要根据具体的需求进行适当的修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值