linux设备驱动学习笔记--内核调试方法之proc(补充seq_file)
2015-02-01 23:11
459 查看
上一节中的proc实现对于开关文件,控制文件,以及显示很少信息的文件来说还是比较简单的,但是对于需要输出大量信息像meminfo,或者结构化的信息像cpuinfo等时就会显得很笨拙,并且代码也很不好理解与维护。内核为了简化这种proc文件的实现提供了另外一种方案----seq_file接口。
set_file 接口假定你在创建一个虚拟文件, 它涉及一系列的必须返回给用户空间的项. 为使用 seq_file, 你必须创建一个简单的 "iterator" 对象, 它能在序列里建立一个位置(start), 向前进(next), 并且输出序列里的一个项(show). 它可能听起来复杂,但实际上过程非常简单.
seq_file接口即可以实现以前的非结构化的信息显示,也可以实现结构化的信息显示,并且两种实现的代码都比较简单清晰。使用seq_file接口需要包含头文件<linux/seq_file.h>。
seq_file的使用比较规范,与其他文件操作步骤比较一致,所以看起来也比较容易,我们可以通过分析内核中对meminfo和cpuinfo来学习非结构化与结构化信息显示的实现步骤。
next迭代器:
stop迭代器,一般实现为空
show迭代器,最主要的信息显示
set_file 接口假定你在创建一个虚拟文件, 它涉及一系列的必须返回给用户空间的项. 为使用 seq_file, 你必须创建一个简单的 "iterator" 对象, 它能在序列里建立一个位置(start), 向前进(next), 并且输出序列里的一个项(show). 它可能听起来复杂,但实际上过程非常简单.
seq_file接口即可以实现以前的非结构化的信息显示,也可以实现结构化的信息显示,并且两种实现的代码都比较简单清晰。使用seq_file接口需要包含头文件<linux/seq_file.h>。
seq_file的使用比较规范,与其他文件操作步骤比较一致,所以看起来也比较容易,我们可以通过分析内核中对meminfo和cpuinfo来学习非结构化与结构化信息显示的实现步骤。
1,结构化信息显示方式
第一步:在模块初始化时调用proc_create来创建相应proc文件
static int __init proc_cpuinfo_init(void) { proc_create("cpuinfo", 0, NULL, &proc_cpuinfo_operations); return 0; }
第二步:在模块卸载时调用remove_proc_entry函数删除相应的proc文件,当然因为cpuinfo的实现在内核代码中,不涉及到卸载,所以cpuinfo没有删除相应proc文件的操作,但是我们自己编写的内核模块涉及此场景,所以需要实现此步骤。
第三步:定义file_operations结构体(第一步中创建时会使用)
static const struct file_operations proc_cpuinfo_operations = { .open = cpuinfo_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, };
第四步,实现file_operations的open函数,在open函数中建议proc文件与seq_file机制的四个迭代器的联系。而read、llseek、release等都是seq_file实现好的框架,不需要另外实现。
extern const struct seq_operations cpuinfo_op; static int cpuinfo_open(struct inode *inode, struct file *file) { return seq_open(file, &cpuinfo_op); }
第五步,定义seq_operations结构体
const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, .show = show_cpuinfo, };
第六步,实现迭代器(start/next/stop/show),按自己需要来实现,这里只是cpuinfo的实现
start迭代器:static void *c_start(struct seq_file *m, loff_t *pos) { *pos = cpumask_next(*pos - 1, cpu_online_mask); if ((*pos) < nr_cpu_ids) return &cpu_data(*pos); return NULL; }
next迭代器:
static void *c_next(struct seq_file *m, void *v, loff_t *pos) { (*pos)++; return c_start(m, pos); }
stop迭代器,一般实现为空
static void c_stop(struct seq_file *m, void *v) { }
show迭代器,最主要的信息显示
static int show_cpuinfo(struct seq_file *m, void *v) { struct cpuinfo_x86 *c = v; unsigned int cpu; int i; cpu = c->cpu_index; seq_printf(m, "processor\t: %u\n" "vendor_id\t: %s\n" "cpu family\t: %d\n" "model\t\t: %u\n" "model name\t: %s\n", cpu, c->x86_vendor_id[0] ? c->x86_vendor_id : "unknown", c->x86, c->x86_model, c->x86_model_id[0] ? c->x86_model_id : "unknown"); if (c->x86_mask || c->cpuid_level >= 0) seq_printf(m, "stepping\t: %d\n", c->x86_mask); else seq_printf(m, "stepping\t: unknown\n"); if (c->microcode) seq_printf(m, "microcode\t: 0x%x\n", c->microcode); if (cpu_has(c, X86_FEATURE_TSC)) { unsigned int freq = cpufreq_quick_get(cpu); if (!freq) freq = cpu_khz; seq_printf(m, "cpu MHz\t\t: %u.%03u\n", freq / 1000, (freq % 1000)); } /* Cache size */ if (c->x86_cache_size >= 0) seq_printf(m, "cache size\t: %d KB\n", c->x86_cache_size); show_cpuinfo_core(m, c, cpu); show_cpuinfo_misc(m, c); seq_printf(m, "flags\t\t:"); for (i = 0; i < 32*NCAPINTS; i++) if (cpu_has(c, i) && x86_cap_flags[i] != NULL) seq_printf(m, " %s", x86_cap_flags[i]); seq_printf(m, "\nbogomips\t: %lu.%02lu\n", c->loops_per_jiffy/(500000/HZ), (c->loops_per_jiffy/(5000/HZ)) % 100); #ifdef CONFIG_X86_64 if (c->x86_tlbsize > 0) seq_printf(m, "TLB size\t: %d 4K pages\n", c->x86_tlbsize); #endif seq_printf(m, "clflush size\t: %u\n", c->x86_clflush_size); seq_printf(m, "cache_alignment\t: %d\n", c->x86_cache_alignment); seq_printf(m, "address sizes\t: %u bits physical, %u bits virtual\n", c->x86_phys_bits, c->x86_virt_bits); seq_printf(m, "power management:"); for (i = 0; i < 32; i++) { if (c->x86_power & (1 << i)) { if (i < ARRAY_SIZE(x86_power_flags) && x86_power_flags[i]) seq_printf(m, "%s%s", x86_power_flags[i][0] ? " " : "", x86_power_flags[i]); else seq_printf(m, " [%d]", i); } } seq_printf(m, "\n\n"); return 0; }
2,非结构化信息显示方式
第一步:在模块初始化时调用proc_create来创建相应proc文件
static int __init proc_meminfo_init(void) { proc_create("meminfo", 0, NULL, &meminfo_proc_fops); return 0; }
第二步:在模块卸载时调用remove_proc_entry函数删除相应的proc文件,当然因为meminfo的实现在内核代码中,不涉及到卸载,所以meminfo没有删除相应proc文件的操作,但是我们自己编写的内核模块涉及此场景,所以需要实现此步骤。
第三步:定义file_operations结构体(第一步中创建时会使用)
static const struct file_operations meminfo_proc_fops = { .open = meminfo_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, };
第四步,实现file_operations的open函数,因为meminfo是非结构化信息显示,所以我们不需要实现start/next/stop等迭代器,只需要实现show来显示信息即可,所以不需要像cpuinfo一样需要定义一个seq_operations结构体并且调用seq_open来建立proc文件与seq_file迭代器之间的联系。seq_file接口对于非结构化的信息显示情况提供了一个single_open接口,所以在open中调用single_open。
static int meminfo_proc_open(struct inode *inode, struct file *file) { return single_open(file, meminfo_proc_show, NULL); }
第五步,只需要实现show迭代器meminfo_proc_show即可
static int meminfo_proc_show(struct seq_file *m, void *v) { struct sysinfo i; unsigned long committed; unsigned long allowed; struct vmalloc_info vmi; long cached; unsigned long pages[NR_LRU_LISTS]; int lru; /* * display in kilobytes. */ #define K(x) ((x) << (PAGE_SHIFT - 10)) si_meminfo(&i); si_swapinfo(&i); committed = percpu_counter_read_positive(&vm_committed_as); allowed = ((totalram_pages - hugetlb_total_pages()) * sysctl_overcommit_ratio / 100) + total_swap_pages; cached = global_page_state(NR_FILE_PAGES) - total_swapcache_pages - i.bufferram; if (cached < 0) cached = 0; get_vmalloc_info(&vmi); for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++) pages[lru] = global_page_state(NR_LRU_BASE + lru); /* * Tagged format, for easy grepping and expansion. */ seq_printf(m, "MemTotal: %8lu kB\n" "MemFree: %8lu kB\n" "Buffers: %8lu kB\n" "Cached: %8lu kB\n" "SwapCached: %8lu kB\n" "Active: %8lu kB\n" "Inactive: %8lu kB\n" "Active(anon): %8lu kB\n" "Inactive(anon): %8lu kB\n" "Active(file): %8lu kB\n" "Inactive(file): %8lu kB\n" "Unevictable: %8lu kB\n" "Mlocked: %8lu kB\n" #ifdef CONFIG_HIGHMEM "HighTotal: %8lu kB\n" "HighFree: %8lu kB\n" "LowTotal: %8lu kB\n" "LowFree: %8lu kB\n" #endif #ifndef CONFIG_MMU "MmapCopy: %8lu kB\n" #endif "SwapTotal: %8lu kB\n" "SwapFree: %8lu kB\n" "Dirty: %8lu kB\n" "Writeback: %8lu kB\n" "AnonPages: %8lu kB\n" "Mapped: %8lu kB\n" "Shmem: %8lu kB\n" "Slab: %8lu kB\n" "SReclaimable: %8lu kB\n" "SUnreclaim: %8lu kB\n" "KernelStack: %8lu kB\n" "PageTables: %8lu kB\n" #ifdef CONFIG_QUICKLIST "Quicklists: %8lu kB\n" #endif "NFS_Unstable: %8lu kB\n" "Bounce: %8lu kB\n" "WritebackTmp: %8lu kB\n" "CommitLimit: %8lu kB\n" "Committed_AS: %8lu kB\n" "VmallocTotal: %8lu kB\n" "VmallocUsed: %8lu kB\n" "VmallocChunk: %8lu kB\n" #ifdef CONFIG_MEMORY_FAILURE "HardwareCorrupted: %5lu kB\n" #endif , K(i.totalram), K(i.freeram), K(i.bufferram), K(cached), K(total_swapcache_pages), K(pages[LRU_ACTIVE_ANON] + pages[LRU_ACTIVE_FILE]), K(pages[LRU_INACTIVE_ANON] + pages[LRU_INACTIVE_FILE]), K(pages[LRU_ACTIVE_ANON]), K(pages[LRU_INACTIVE_ANON]), K(pages[LRU_ACTIVE_FILE]), K(pages[LRU_INACTIVE_FILE]), K(pages[LRU_UNEVICTABLE]), K(global_page_state(NR_MLOCK)), #ifdef CONFIG_HIGHMEM K(i.totalhigh), K(i.freehigh), K(i.totalram-i.totalhigh), K(i.freeram-i.freehigh), #endif #ifndef CONFIG_MMU K((unsigned long) atomic_long_read(&mmap_pages_allocated)), #endif K(i.totalswap), K(i.freeswap), K(global_page_state(NR_FILE_DIRTY)), K(global_page_state(NR_WRITEBACK)), K(global_page_state(NR_ANON_PAGES)), K(global_page_state(NR_FILE_MAPPED)), K(global_page_state(NR_SHMEM)), K(global_page_state(NR_SLAB_RECLAIMABLE) + global_page_state(NR_SLAB_UNRECLAIMABLE)), K(global_page_state(NR_SLAB_RECLAIMABLE)), K(global_page_state(NR_SLAB_UNRECLAIMABLE)), global_page_state(NR_KERNEL_STACK) * THREAD_SIZE / 1024, K(global_page_state(NR_PAGETABLE)), #ifdef CONFIG_QUICKLIST K(quicklist_total_size()), #endif K(global_page_state(NR_UNSTABLE_NFS)), K(global_page_state(NR_BOUNCE)), K(global_page_state(NR_WRITEBACK_TEMP)), K(allowed), K(committed), (unsigned long)VMALLOC_TOTAL >> 10, vmi.used >> 10, vmi.largest_chunk >> 10 #ifdef CONFIG_MEMORY_FAILURE ,atomic_long_read(&mce_bad_pages) << (PAGE_SHIFT - 10) #endif ); hugetlb_report_meminfo(m); arch_report_meminfo(m); return 0; #undef K }
相关文章推荐
- linux设备驱动学习笔记--内核调试方法之proc
- Linux内核学习笔记之seq_file接口创建可读写proc文件
- linux设备驱动学习笔记--内核调试方法之printk
- linux设备驱动学习笔记--内核调试方法之printk
- Ldd3 学习笔记2 — simple 2.6.10以上内核版本编译错误解决方法!!!
- windbg学习笔记 FOR 内核调试(三) --进程句柄表HANDLE_TABLE
- IOS学习笔记:NSFileManager常用方法
- linux0.11学习笔记-技术铺垫-简单AB任务切换程序(3)-调试手段和方法
- iOS学习笔记-- 加载图片选择imageNamed 方法还是 imageWithContentsOfFile
- PHP学习笔记,curl,file_get_content,include和fopen四种方法获取远程文件速度测试.
- windbg 学习笔记 FOR 内核调试(三) --进程句柄表HANDLE_TABLE
- 用户空间内核空间ipc总结(sockopt,ioctl,mmap,netlink,proc,seq,file,copy_user)
- windbg学习笔记 FOR 内核调试
- 学习笔记 --- LINUX应用调试之修改内核输出应用程序的段错误信息
- 【Java学习笔记】JFrame类的pack()与validate()方法的区别(待补充)
- 黑马程序员——学习笔记补充-RandomAccessFile
- 转载只为记录经典之<用户空间内核空间ipc总结(sockopt,ioctl,mmap,netlink,proc,seq,file,copy_user)>
- 【内核】:一个使用 seq_file 接口的 proc_fs 例子
- android 学习笔记(五)调试相关 5.6 AndroidApp定位和规避内存泄露方法研究
- linux0.11学习笔记-技术铺垫-简单AB任务切换程序(3)-调试手段和方法