在老版本的Linux内核中,proc文件系统有一个缺陷:如果输出内容大于1个内存页,需要多次读,因此处理起来很难。另外,如果输出内容太大,速度会比较慢。在2.6内核中,由于大量使用了seq_file功能,使得内核输出大文件信息更容易。使用seq_file需要包含头文件linux/seq_file.h,并定义一个seq_operations结构:
- struct seq_operations {
- void * (*start) (struct seq_file *m, loff_t *pos);// 指定seq_file文件的读开始位置
- void (*stop) (struct seq_file *m, void *v);//关闭seq_file
- // 把seq_file文件的当前读位置移动到下一个读位置
- void * (*next) (struct seq_file *m, void *v, loff_t *pos);
- int (*show) (struct seq_file *m, void *v);//格式化输出
- };
seq_file的常用操作接口如下:
- int seq_open(struct file *, const struct seq_operations *);//打开
- ssize_t seq_read(struct file *, char __user *, size_t, loff_t *);//读
- loff_t seq_lseek(struct file *, loff_t, int);//定位
- int seq_release(struct inode *, struct file *);//释放
- int seq_escape(struct seq_file *, const char *, const char *);//写缓冲,忽略某些字符
- int seq_putc(struct seq_file *m, char c);// 把一个字符输出到seq_file文件
- int seq_puts(struct seq_file *m, const char *s);// 把一个字符串输出到seq_file文件
下面以cpuinfo为例说明seq_file在proc中的使用。
- void create_seq_entry(char *name, mode_t mode, const struct file_operations *f)
- {
- struct proc_dir_entry *entry;
- entry = create_proc_entry(name, mode, NULL);
- if (entry)
- entry->proc_fops = f;
- }
- void __init proc_misc_init(void)
- {
- …
- create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations); //创建/proc目录
- }
定义proc_cpuinfo_operations:
- static const struct file_operations proc_cpuinfo_operations = {
- .open = cpuinfo_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
- };
接下来看cpuinfo_open:
- static int cpuinfo_open(struct inode *inode, struct file *file)
- {
- return seq_open(file, &cpuinfo_op);
- }
cpuinfo_op是与硬件平台相关的。ARM的cpuinfo_op在/arc/arm/kernel/setup.c中:
- struct seq_operations cpuinfo_op = {
- .start = c_start,
- .next = c_next,
- .stop = c_stop,
- .show= c_show
- };
cpuinfo_op就是实际上对/proc/ cpuinfo进行修改的操作接口,其中最重要的是c_show:
- static int c_show(struct seq_file *m, void *v)
- {
- int i;
- seq_printf(m, "Processor\t: %s rev %d (%s)\n",
- cpu_name, (int)processor_id & 15, elf_platform);
- #if defined(CONFIG_SMP)//针对多处理器
- for_each_online_cpu(i) {
- seq_printf(m, "processor\t: %d\n", i);
- seq_printf(m, "BogoMIPS\t: %lu.%02lu\n\n",
- per_cpu(cpu_data, i).loops_per_jiffy / (500000UL/HZ),
- (per_cpu(cpu_data, i).loops_per_jiffy / (5000UL/HZ)) % 100);
- }
- #else /* CONFIG_SMP */
- seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",
- loops_per_jiffy / (500000/HZ),
- (loops_per_jiffy / (5000/HZ)) % 100);
- #endif
- seq_puts(m, "Features\t: ");
- for (i = 0; hwcap_str[i]; i++)
- if (elf_hwcap & (1 << i))
- seq_printf(m, "%s ", hwcap_str[i]);
- seq_printf(m, "\nCPU implementer\t: 0x%02x\n", processor_id >> 24);
- seq_printf(m, "CPU architecture: %s\n", proc_arch[cpu_architecture()]);
- if ((processor_id & 0x0008f000) == 0x00000000) {/* pre-ARM7 */
- seq_printf(m, "CPU part\t: %07x\n", processor_id >> 4);
- } else {
- if ((processor_id & 0x0008f000) == 0x00007000) {/* ARM7 */
- seq_printf(m, "CPU variant\t: 0x%02x\n",
- (processor_id >> 16) & 127);
- } else {/* ARM7以上的CPU */
- seq_printf(m, "CPU variant\t: 0x%x\n",
- (processor_id >> 20) & 15);
- }
- seq_printf(m, "CPU part\t: 0x%03x\n",(processor_id >> 4) & 0xfff);
- }
- seq_printf(m, "CPU revision\t: %d\n", processor_id & 15);
- {
- unsigned int cache_info = read_cpuid(CPUID_CACHETYPE);
- if (cache_info != processor_id) {
- seq_printf(m, "Cache type\t: %s\n"
- "Cache clean\t: %s\n"
- "Cache lockdown\t: %s\n"
- "Cache format\t: %s\n",
- cache_types[CACHE_TYPE(cache_info)],
- cache_clean[CACHE_TYPE(cache_info)],
- cache_lockdown[CACHE_TYPE(cache_info)],
- CACHE_S(cache_info) ? "Harvard" : "Unified");
- if (CACHE_S(cache_info)) {
- c_show_cache(m, "I", CACHE_ISIZE(cache_info));
- c_show_cache(m, "D", CACHE_DSIZE(cache_info));
- } else {
- c_show_cache(m, "Cache", CACHE_ISIZE(cache_info));
- }
- }
- }
- seq_puts(m, "\n");
- seq_printf(m, "Hardware\t: %s\n", machine_name);
- seq_printf(m, "Revision\t: %04x\n", system_rev);
- seq_printf(m, "Serial\t\t: %08x%08x\n",system_serial_high, system_serial_low);
- return 0;
- }