内核proc文件系统与seq接口(5)---通用proc接口与seq_file接口实验

 前面几篇已经学习了procfs和seq_file接口的相关知识,最后当然要动手实验。如果没有实验,调试一下简单的程序,学的知识很快就会忘记。

一、使用默认的proc访问接口
这里我使用默认的proc访问函数并实现需要的read_proc和write_proc函数。

Makefile

MODULE_NAME = proc_test
MODULE_CONFIG = CONFIG_PROC_TEST
CROSS_CONFIG = y
# Comment/uncomment the following line to disable/enable debugging
DEBUG = y



ifneq ($(KERNELRELEASE),)
# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
  DEBFLAGS = -O -g -DDEBUG # "-O" is needed to expand inlines
else
  DEBFLAGS = -O2
endif
ccflags-y += $(DEBFLAGS)


obj-$($(MODULE_CONFIG)) := $(MODULE_NAME).o
#for Multi-files module
#$(MODULE_NAME)-objs := hello_linux_simple_dep.o ex_output.o

else

ifeq ($(CROSS_CONFIG), y)
#for Cross-compile
KERNELDIR = /home/tekkaman/development/linux-omap3
ARCH = arm
#FIXME:maybe we need absolute path for different user. eg root
#CROSS_COMPILE = arm-none-linux-gnueabi-
CROSS_COMPILE = /home/tekkaman/development/toolchain/arm-2009q1/bin/arm-none-linux-gnueabi-
INSTALLDIR := /home/tekkaman/development/targetfs/

else
#for Local compile
KERNELDIR = /lib/modules/$(shell uname -r)/build
ARCH = x86
CROSS_COMPILE = 
INSTALLDIR := /

endif


################################
PWD := $(shell pwd)

.PHONY: modules modules_install clean

modules:
	$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) $(MODULE_CONFIG)=m -C $(KERNELDIR) M=$(PWD) modules

modules_install: modules
	$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) $(MODULE_CONFIG)=m -C $(KERNELDIR) INSTALL_MOD_PATH=$(INSTALLDIR) M=$(PWD) modules_install

clean:
	@rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers *.order .*.o.d modules.builtin
endif

proc_test.c

/*
 * Module for Tekkaman LDD
 *
 * Copyright (C) 2012 Tekkaman Ninja.
 *
 * Licensed under the GPL-2 or later.
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>

#define USE_CAT_TO_TEST
#define MAX_BUFFER_LENGTH       (PAGE_SIZE*2)

static struct proc_dir_entry *proc_test_entry;
static char *proc_test;		// Space for proc_test_buffer strings
static int string_index;	// Index to write next string
static int string_next;		// Index to read next string

static int proc_test_write(struct file *filp, const char __user * buff,
			   unsigned long len, void *data)
{
	int space_available = MAX_BUFFER_LENGTH - string_index;

	if (len >= MAX_BUFFER_LENGTH)
	{
		pr_warning("%s: Input is too big ! MAX_BUFFER_LENGTH = %lu\n", __func__,
		       MAX_BUFFER_LENGTH);
		return -ENOSPC;
	}

	if (len >= space_available) {
		pr_warning("%s: buffer is not space left! Begin to clean buffer and loop!\n", __func__);
		memset(proc_test, 0, MAX_BUFFER_LENGTH);
		string_index = 0;
		string_next = 0;
//		return -ENOSPC;
	}
	if (copy_from_user(&proc_test[string_index], buff, len)) {
		pr_err("%s:copy_from_user error!\n", __func__);
		return -EFAULT;
	}
	string_index += len;

#if defined(USE_CAT_TO_TEST)
	//FIXME:some user space utils(like cat) will cut the input to PAGE_SIZE/time.
	if (len != PAGE_SIZE)
#endif
		proc_test[string_index++] = '\0';

	pr_debug("%s:len:%lu\n", __func__, len);
	pr_debug("%s:string_index:%d\n", __func__, string_index);
	pr_debug("%s:string_next:%d\n", __func__, string_next);
	return len;
}

static int proc_test_read(char *page, char **start, off_t off,
			  int count, int *eof, void *data)
{
	int len = 0;
//	void *ptr;

	len = strlen(&proc_test[string_next + off]);

	if (len == 0) {
		*eof = 1;
		string_next += (off + 1);

		/* Wrap-around */
	if (string_next >= string_index)
		string_next = 0;

		pr_debug
			("%s:string_index:%d, len:%d, off:%ld,*eof:%d, count:%d,start:%p\n",
			 __func__, string_index, len, off, *eof, count, *start);
		pr_debug("%s:string_next:%d\n", __func__, string_next);
		return 0;
	}

	if (len < (count)) {
		*eof = 1;
		if (off != 0) {
			*start = page;
		}
	} else {
		len = count;
		*start = (char *)len;
	}

	(void *)memcpy(page, &proc_test[string_next + off], len);

	pr_debug
	    ("%s:string_index:%d, len:%d, off:%ld,*eof:%d, count:%d,start:%p\n",
	     __func__, string_index, len, off, *eof, count, *start);
	pr_debug("%s:string_next:%d\n", __func__, string_next);

	return len;
}

static int __init proc_test_init(void)
{
	int ret = 0;

	pr_debug("proc_test, online !\n");
	proc_test = (char *)vmalloc(MAX_BUFFER_LENGTH);
	if (!proc_test) {
		ret = -ENOMEM;
	} else {
		pr_debug("proc_test:%p~%p\n", proc_test, (void *)proc_test + MAX_BUFFER_LENGTH -1);

		memset(proc_test, 0, MAX_BUFFER_LENGTH);
		proc_test_entry = create_proc_entry("proc_test", 0644, NULL);
		if (proc_test_entry == NULL) {
			ret = -ENOMEM;
			vfree(proc_test);
			pr_err("proc_test: Couldn't create proc entry\n");
		} else {
			string_index = 0;
			string_next = 0;

			proc_test_entry->read_proc = proc_test_read;
			proc_test_entry->write_proc = proc_test_write;

			pr_info("proc_test: Module loaded.\n");
		}
	}
	return ret;
}

static void __exit proc_test_exit(void)
{
	remove_proc_entry("proc_test", NULL);
	vfree(proc_test);
	pr_info("proc_test, exit !\n");
}

module_init(proc_test_init);
module_exit(proc_test_exit);

MODULE_DESCRIPTION("Procfs test module");
MODULE_VERSION("v1.0");
MODULE_AUTHOR("Tekkaman Ninja <tekkamanninja@gmail.com>");
MODULE_LICENSE("Dual BSD/GPL");

test_6KB

123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789

这个测试程序的功能是实现简单的/proc/proc_test文件,这个文件在内核中有一个MAX_BUFFER_LENGTH大小的缓存,用户空间可以将字符串写入缓存(字符串长度可以大于1页,每个都以'\0'结尾),并可以分次去取出各段字符串。但是,的确,由于默认的proc访问接口的缓存只有一页内核,这种proc接口在输出大于一页的数据时,对于read_proc的调试的确是让人郁闷的事情(本人水平有限)。幸运的是最后通过了在shell命令行下的测试。以下是测试记录:

# insmod proc_test.ko
proc_test: Module loaded.
# echo tekkaman_0 > /proc/proc_test
# echo tekkaman_1 > /proc/proc_test
# echo tekkaman_2 > /proc/proc_test
# echo tekkaman_3 > /proc/proc_test
# cat test_6KB > /proc/proc_test
# echo tekkaman_4 > /proc/proc_test
# echo tekkaman_5 > /proc/proc_test
# echo tekkaman_6 > /proc/proc_test
# echo tekkaman_7 > /proc/proc_test
# cat /proc/proc_test > ./result.log
# cat /proc/proc_test >> ./result.log
# cat /proc/proc_test >> ./result.log
# cat /proc/proc_test >> ./result.log
# cat /proc/proc_test >> ./result.log
# cat /proc/proc_test >> ./result.log
# cat /proc/proc_test >> ./result.log
# cat /proc/proc_test >> ./result.log
# cat /proc/proc_test >> ./result.log
# rmmod proc_test.ko
proc_test, exit !

你可以在./result.log 中看到读写的数据。

二、proc文件的访问使用seq_file接口
       前面有讲到,不仅对于大文件,对于一系列的内核结构体的访问(例如用链表串起的数据结构),如果使用procfs默认的访问函数,必须自己在read_proc中实现访问迭代,让人调试到吐血不说,还容易出bug。本身那种接口就不适合大于一页的数据访问。而解决这个问题正是seq_file存在的意义。在这个实验中,我在模块的初始化函数中创建了一系列用于读取的数据结构,并用内核双向链表将其串起。通过procfs输出这一系列的结构体中的数据。数据结构如下图所示:

 

源码:

Makefile

MODULE_NAME = proc_seq
MODULE_CONFIG = CONFIG_PROC_SEQ
CROSS_CONFIG = y
# Comment/uncomment the following line to disable/enable debugging
DEBUG = y



ifneq ($(KERNELRELEASE),)
# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
  DEBFLAGS = -O -g -DDEBUG # "-O" is needed to expand inlines
else
  DEBFLAGS = -O2
endif
ccflags-y += $(DEBFLAGS)


obj-$($(MODULE_CONFIG)) := $(MODULE_NAME).o
#for Multi-files module
#$(MODULE_NAME)-objs := hello_linux_simple_dep.o ex_output.o

else

ifeq ($(CROSS_CONFIG), y)
#for Cross-compile
KERNELDIR = /home/tekkaman/development/linux-omap3
ARCH = arm
#FIXME:maybe we need absolute path for different user. eg root
#CROSS_COMPILE = arm-none-linux-gnueabi-
CROSS_COMPILE = /home/tekkaman/development/toolchain/arm-2009q1/bin/arm-none-linux-gnueabi-
INSTALLDIR := /home/tekkaman/development/targetfs/

else
#for Local compile
KERNELDIR = /lib/modules/$(shell uname -r)/build
ARCH = x86
CROSS_COMPILE = 
INSTALLDIR := /

endif


################################
PWD := $(shell pwd)

.PHONY: modules modules_install clean

modules:
	$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) $(MODULE_CONFIG)=m -C $(KERNELDIR) M=$(PWD) modules

modules_install: modules
	$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) $(MODULE_CONFIG)=m -C $(KERNELDIR) INSTALL_MOD_PATH=$(INSTALLDIR) M=$(PWD) modules_install

clean:
	@rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers *.order .*.o.d modules.builtin
endif

Makefile~

MODULE_NAME = proc_seq
MODULE_CONFIG = CONFIG_PROC_SEQ
CROSS_CONFIG = y
# Comment/uncomment the following line to disable/enable debugging
DEBUG = y



ifneq ($(KERNELRELEASE),)
# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
  DEBFLAGS = -O -g -DDEBUG # "-O" is needed to expand inlines
else
  DEBFLAGS = -O2
endif
ccflags-y += $(DEBFLAGS)


obj-$($(MODULE_CONFIG)) := $(MODULE_NAME).o
#for Multi-files module
#$(MODULE_NAME)-objs := hello_linux_simple_dep.o ex_output.o

else

ifeq ($(CROSS_CONFIG), y)
#for Cross-compile
KERNELDIR = /media/6a55c5a3-f467-4b31-a56a-73b57c5cd2a2/C6A816x/development/linux-omap3
ARCH = arm
#FIXME:maybe we need absolute path for different user. eg root
#CROSS_COMPILE = arm-none-linux-gnueabi-
CROSS_COMPILE = /media/6a55c5a3-f467-4b31-a56a-73b57c5cd2a2/C6A816x/development/toolchain/arm-2009q1/bin/arm-none-linux-gnueabi-
INSTALLDIR := /media/6a55c5a3-f467-4b31-a56a-73b57c5cd2a2/C6A816x/development/targetfs/

else
#for Local compile
KERNELDIR = /lib/modules/$(shell uname -r)/build
ARCH = x86
CROSS_COMPILE = 
INSTALLDIR := /

endif


################################
PWD := $(shell pwd)

.PHONY: modules modules_install clean

modules:
	$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) $(MODULE_CONFIG)=m -C $(KERNELDIR) M=$(PWD) modules

modules_install: modules
	$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) $(MODULE_CONFIG)=m -C $(KERNELDIR) INSTALL_MOD_PATH=$(INSTALLDIR) M=$(PWD) modules_install

clean:
	@rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.symvers *.order .*.o.d modules.builtin
endif

proc_seq.c

/*
 * Module for Tekkaman LDD
 *
 * Copyright (C) 2012 Tekkaman Ninja.
 *
 * Licensed under the GPL-2 or later.
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include <linux/seq_file.h>
#include <linux/list.h>
#include "proc_seq.h"

LIST_HEAD(test_data_head);

static struct proc_dir_entry *proc_test_entry;

static void *proc_seq_start(struct seq_file *s, loff_t * pos)
{
	int i;
	struct proc_seq_data *tmp_v =
	    list_entry(test_data_head.next, struct proc_seq_data, list);

	pr_debug("%s: *pos = %lld\n", __func__, *pos);

	if (*pos <= 0) {
		if (*pos < 0) {
			pr_err("%s: *pos = %lld !?? make *pos = 0\n", __func__,
			       *pos);
		}
		return tmp_v;
	} else {
		if (*pos >= MAX_DATA_NODE) {
//			pr_err("%s: *pos = %lld ~~too big!\n", __func__, *pos);
			return NULL;
		}
		for (i = 0; i < *pos; i += 1) {
			tmp_v = list_entry(tmp_v->list.next, struct proc_seq_data, list);
		}
		BUG_ON(i != *pos);
		return tmp_v;
	}
}

static void *proc_seq_next(struct seq_file *s, void *v, loff_t * pos)
{
	struct proc_seq_data *tmp_v = (struct proc_seq_data *)v;
	if (*pos < (MAX_DATA_NODE - 1)) {
		tmp_v = list_entry(tmp_v->list.next, struct proc_seq_data,
				   list);
		(*pos)++;
		return tmp_v;
	} else {
		*pos = 0;
		return NULL;
	}
}

static void proc_seq_stop(struct seq_file *s, void *v)
{
	pr_debug("%s\n", __func__);
}

static int proc_seq_show(struct seq_file *s, void *v)
{
	int i;
	int *data = ((struct proc_seq_data *)v)->data;
	
	seq_printf(s, "node-%d	", ((struct proc_seq_data *)v)->key);
	
	for (i = 0; i < MAX_DATA_NUM; i++) {
		seq_printf(s, "data[%d] = %02d;	", i, data[i]);
	}
	seq_putc(s, '\n');
	return 0;
}

static struct seq_operations proc_seq_ops = {
	.start = proc_seq_start,
	.next = proc_seq_next,
	.stop = proc_seq_stop,
	.show = proc_seq_show
};

static int proc_seq_open(struct inode *inode, struct file *file)
{
	return seq_open(file, &proc_seq_ops);
};

static struct file_operations proc_ops = {
	.owner = THIS_MODULE,
	.open = proc_seq_open,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = seq_release,
};

static void __exit free_test_data(int num)
{
	int i;
	struct proc_seq_data *temp;
	for (i = 0; i < num; i += 1) {
		if (list_empty(&test_data_head)) {
			break;
		}
		temp =
		    list_first_entry(&test_data_head, struct proc_seq_data,
				     list);
		list_del(test_data_head.next);
		kfree(temp);
	}
	BUG_ON(!list_empty(&test_data_head));
	return;
}

static int __init init_test_data(void)
{
	int i, j;
	struct proc_seq_data *temp;
	for (i = 0; i < MAX_DATA_NODE; i += 1) {
		temp = kmalloc(sizeof(struct proc_seq_data), GFP_KERNEL);
		if (temp == NULL) {
			pr_err("%s:%d:can not get mem at %d", __FILE__,
			       __LINE__, i);
			break;
		}
		temp->key = i;
		for (j = 0; j < MAX_DATA_NUM; j += 1) {
			temp->data[j] = i * MAX_DATA_NUM + j;
		}
		list_add_tail(&(temp->list), &(test_data_head));
	}
	if (i != MAX_DATA_NODE) {
		BUG_ON(i > MAX_DATA_NODE);
		free_test_data(i);
		return -1;
	}
	return 0;
}

static void __exit cleanup_test_data(void)
{
	free_test_data(MAX_DATA_NODE);
}

static int __init proc_seq_init(void)
{
	int ret = 0;

	pr_info("proc_seq, online !\n");
	ret = init_test_data();
	if (ret != 0) {
		pr_err("%s: init test data error!\n", KBUILD_MODNAME);
		return ret;
	} else {
		proc_test_entry = create_proc_entry("proc_seq", 0644, NULL);
		if (proc_test_entry == NULL) {
			ret = -ENOMEM;
			cleanup_test_data();
			pr_err("proc_test: Couldn't create proc entry\n");
		} else {
			proc_test_entry->proc_fops = &proc_ops;
			pr_info("proc_test: Module loaded.\n");
		}
	}
	return ret;
}

static void __exit proc_seq_exit(void)
{
	remove_proc_entry("proc_seq", NULL);
	cleanup_test_data();
	pr_info("proc_seq, exit !\n");
}

module_init(proc_seq_init);
module_exit(proc_seq_exit);

MODULE_DESCRIPTION("Procfs seq test module");
MODULE_VERSION("v1.0");
MODULE_AUTHOR("Tekkaman Ninja <tekkamanninja@gmail.com>");
MODULE_LICENSE("Dual BSD/GPL");

proc_seq.h

/*
 * Module for Tekkaman LDD
 *
 * Copyright (C) 2012 Tekkaman Ninja.
 *
 * Licensed under the GPL-2 or later.
 */

#ifndef _PROC_SEQ_H_
#define _PROC_SEQ_H_

#define MAX_DATA_NODE	(10)
#define MAX_DATA_NUM	(4)

struct proc_seq_data {
	int key;
	int data[MAX_DATA_NUM];
	struct list_head list;
};

#endif				/* _PROC_SEQ_H_ */

 

测试结果如下:

# insmod proc_seq.ko
proc_seq, online !
proc_test: Module loaded.
# cat /proc/proc_seq
node-0 data[0] = 00; data[1] = 01; data[2] = 02; data[3] = 03;
node-1 data[0] = 04; data[1] = 05; data[2] = 06; data[3] = 07;
node-2 data[0] = 08; data[1] = 09; data[2] = 10; data[3] = 11;
node-3 data[0] = 12; data[1] = 13; data[2] = 14; data[3] = 15;
node-4 data[0] = 16; data[1] = 17; data[2] = 18; data[3] = 19;
node-5 data[0] = 20; data[1] = 21; data[2] = 22; data[3] = 23;
node-6 data[0] = 24; data[1] = 25; data[2] = 26; data[3] = 27;
node-7 data[0] = 28; data[1] = 29; data[2] = 30; data[3] = 31;
node-8 data[0] = 32; data[1] = 33; data[2] = 34; data[3] = 35;
node-9 data[0] = 36; data[1] = 37; data[2] = 38; data[3] = 39;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值