文章目录
源代码hwinfo分析与学习
源代码hwinfo分析与学习–1入口入门与目录解析
前序
一直以来都悄悄的收藏着大佬们的佳作,今天终于提笔撒网乱涂,源代码hwinfo分析与学习。
转载请记上出处 --jeffshuai 20230913
阅读hwinfo基本技能需求
- c语言指针与链表:功力深厚
- shell +makefile : 能看懂
- linux 系统编程 :会勤劳查资料
- perf :更佳
代码介绍
代码目录
一些重要的目录 和文件
hwinfo-21.63$ tree -L 2
.
├── doc
│ ├── ......
├── hwinfo #make 后生成的bin文件
├── hwinfo.c # main 入口
├── specifications.md # 随便看看
├── Makefile # 根Makefile
├── Makefile.common # 每个目录Makefile都会引用
├── src
│ ├── hd
│ │ ├── Makefile
│ │ ├── ......
│ ├── ids
│ │ ├── Makefile
│ │ ├── ......
│ ├── isdn
│ │ ├── Makefile
│ │ ├── ......
│ ├── libhd.a
│ ├── libhd.so -> libhd.so.21
│ ├── libhd.so.21 -> libhd.so.21.63.2
│ ├── libhd.so.21.63.2
│ ├── Makefile
│ ...... 省略些不重要的
代码初理解从 入口 makefile走起
LIBS = -lhd
SLIBS = -lhd
TLIBS = -lhd_tiny
include Makefile.common
......
hwinfo: hwinfo.o $(LIBHD)
$(CC) hwinfo.o $(LDFLAGS) $(CFLAGS) $(LIBS) -o $@
从上可看出hwinfo 是由hwinfo.o + libhd构成
文件 src/Makefile 解析
CFILES = $(sort $(wildcard *.c))
OBJS = $(CFILES:.c=.o)
LIBHD_BASE = libhd
LIBHD = $(TOPDIR)/src/$(LIBHD_BASE).a
LIBHD_SONAME = $(LIBHD_BASE).so.$(LIBHD_MAJOR_VERSION)
LIBHD_NAME = $(LIBHD_BASE).so.$(LIBHD_VERSION)
LIBHD_SO = $(TOPDIR)/src/$(LIBHD_NAME)
$(LIBHD): $(OBJS)
ar r $@ $?
@rm -f $(LIBHD_D)
$(CC) -shared $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -Wl,--whole-archive $(LIBHD) -Wl,--no-whole-archive \
-Wl,-soname=$(LIBHD_SONAME) \
-o $(LIBHD_SO) $(SO_LIBS)
生成 libhd.a 和libhd.so*;
目录 src/isdn 解析
makefile:
isdn_cdb.h: isdn_cdb ISDN.CDB.txt
rm -f isdn_cdb.h
./isdn_cdb ISDN.CDB.txt isdn_cdb.h
ISDN.CDB.hwdb: mk_isdnhwdb ISDN.CDB.txt
rm -f ISDN.CDB.hwdb
./mk_isdnhwdb ISDN.CDB.txt ISDN.CDB.hwdb
从上可看出 代码主要的功能: 生成工具 isdn_cdb和 mk_isdnhwdb,再用工具生成文件 ISDN.CDB.hwdb 和isdn_cdb.h;由 src/isdn/cdbisdn.c 》src/hd/isdn.c 调用生成API 在libhd中;详见hd_cdbisdn_**开头的API。
大约关系如下:
isdn_cdb.c+cdb_read.c 》isdn_cdb
ISDN.$(ARCH).txt 》 ISDN.CDB.txt 》isdn_cdb.h
ISDN.CDB.txt 》 ISDN.CDB.hwdb
其中 src/isdn/cdb 下的文件生成工具和 一堆数据文件,关乎vendor、device、vendor_id、device_id等
install -m 644 src/isdn/cdb/ISDN.CDB.txt $(DESTDIR)/usr/share/hwinfo
install -m 644 src/isdn/cdb/ISDN.CDB.hwdb $(DESTDIR)/usr/share/hwinfo
会将文件 ISDN.CDB.txt 和ISDN.CDB.txt 安装到系统中。
而 src/isdn/isa_probe.c 函数isa_isdn_t *isdn_detect()在libhd中调用
isa_isdn_t *isdn_detect()
{ //ISDN(综合业务数字网)是传统电话服务的替换产品,“ ISDN(Integrated Services Digital Network),即综合业务数字网。是一个数字电话网络国际标准,是一种典型的电路交换网络系统。
isa_isdn_t *ii = NULL;
if (iopl(3) < 0) return ii;
avm_a1_detect(&ii); //vendor=AVM Computersysteme AVM是一家德国柏林成立消费电子公司,生产通讯,网络设备,例如DSL,ISDN,无线和VoIP产品。Fritz!Box
probe_elsa(&ii); //vendor=Elsa
telesdetect(&ii); //vendor=Teles
iopl(0);
return ii;
}
从上可看出:isdn_detect 就是侦测 这三家厂商的ISDN设备的,ISDN 对于DOS代的 j e f f 现实中没有见过这种设备,感觉原代码大佬上石器时期就很牛了。
目录 src/smp 解析
其中的总入口函数int detectSMP(void)末被 其它调用过;末知其用, 故ffej认为此目录内与其它无关,暂可不阅 仅作知识扩展。
cpu三大架构 numa smp mpp。
- SMP (Symmetric Multiprocessing) , 对称多处理器. 顾名思义, 在SMP中所有的处理器都是对等的, 它们通过总线连接共享同一块物理内存,从linux 上也能看到:
ls /sys/devices/system/node/# 如果只看到一个node0 那就是smp架构
可以看到只有仅仅一个node,经过大神们的测试发现,2至4个CPU比较适合smp架构。 - NUMA ( Non-Uniform Memory Access),非均匀访问存储模型。
- MPP (Massive Parallel Processing) ,这个其实可以理解为刀片服务器,每个刀扇里的都是一台独立的smp架构服务器,且每个刀扇之间均有高性能的网络设备进行交互,保证了smp服务器之间的数据传输性能。相比numa 来说更适合大规模的计算。
目录 src/ids 解析
IDFILES += src/bus src/class src/extra src/special src/scanner src/network \
src/usb src/usb2 src/isapnp src/monitor src/camera src/tv2 src/tv src/dvb2 src/dvb \
src/chipcard src/modem src/pcmcia src/s390 src/sdio
IDFILES += src/pci src/storage src/sound src/mouse src/braille
$(LIBHD_D): hd_ids.o
ar r $(LIBHD) $?
check_hd: check_hd.c
$(CC) $(CFLAGS) $< -o $@
hd_ids.c: hd_ids.h hd_ids_tiny.h
hd_ids.h hd.ids: check_hd $(IDFILES)
./check_hd --check --sort --cfile hd_ids.h $(IDFILES)
hd_ids_tiny.h: check_hd hd.ids
./check_hd --mini --cfile hd_ids_tiny.h --log=hd_tiny.log --out=hd_tiny.ids hd.ids
上面就是将src/ids/src下的所有数据ids数据文件通过工具命令check_hd生成hd_tiny.ids hd.ids;存放在/var/lib/hardware/hd.ids中,可用 "hwinfo --dump-db 1"查看。
convert_hd
src/ids/convert_hd 这个文件的第一行是 #! /usr/bin/perl,可看出是个perl代码。
更新同步上游ids数据库
在src/ids/update_pci_usb 中
curl --fail -o pci.ids http://pci-ids.ucw.cz/v2.2/pci.ids //下载 各种pci.ids
curl --fail -o usb.ids http://www.linux-usb.org/usb.ids //下载 各种pci.ids
./convert_hd pci.ids && mv hd.ids src/pci //转化成hwinfo格式的文件
./convert_hd usb.ids && mv hd.ids src/usb //转化成hwinfo格式的文件
src/ids/update_x11
system "../check_hd --sort --check --join-keys-first --split --log=$out.log --out=$out hd.ids";
Note: Use the update_pci_usb script to update pci and usb ids.
总结: 上述操作是从上游网站上更新IDS文件,并转化为hwinfo格式的文件,可用命令"hwinfo --dump-db 1"查看;注意: 更新同步上游ids数据库,需要手动去运行 update_pci_usb 脚本。
目录 src/hd 解析
src/hd 是libhd库的核心代码,是hwinfo.c的依赖;需要作重点分析的
那就先从hwinfo.c 的main入口开始吧,比如执行hwinfo --mouse
main
do_hw(hd_data, f, hw_item[i]); //如果是多个项则 do_hw_multi
default: hd0 = hd_list(hd_data, hw_item, 1, NULL);
hd_set_probe_feature_hw(hd_data, item);
hd_scan(hd_data);
hd_scan_no_hal
hd_scan_input
get_input_devices
input = read_file("/proc/bus/input/devices", 0, 0);
ADD2LOG
//接下来就是针对内核接口内容一一解析如"/dev/input/event%u" "/dev/input/mouse%u"
上面展示了一个mouse 被扫描到的详细函数调用;针对其它类型的设备也是一样的流程展开。
src/hd/hd.h 这个文件里的数据结构 会被 其它文件引用
关键数据结构:
void hd_set_probe_feature_hw(hd_data_t *hd_data, hd_hw_item_t item)
{
switch (item) {
case hw_cdrom:
hd_set_probe_feature(hd_data, pr_pci);
hd_set_probe_feature(hd_data, pr_usb);
hd_set_probe_feature(hd_data, pr_block_mods);
hd_set_probe_feature(hd_data, pr_scsi);
。。。。。。
case hw_mouse:
hd_set_probe_feature(hd_data, pr_misc);
if (!hd_data->flags.fast) {
hd_set_probe_feature(hd_data, pr_serial);
}
hd_set_probe_feature(hd_data, pr_usb);
hd_set_probe_feature(hd_data, pr_kbd);
hd_set_probe_feature(hd_data, pr_sys);
hd_set_probe_feature(hd_data, pr_bios);
hd_set_probe_feature(hd_data, pr_mouse);
hd_set_probe_feature(hd_data, pr_input);
hd_set_probe_feature(hd_data, pr_pci);
break;
。。。。。。
case hw_camera:
hd_set_probe_feature(hd_data, pr_usb);
break;
}
hd_probe_feature_t; //flags to control the probing.
void hd_set_probe_feature(hd_data_t *hd_data, enum probe_feature feature)
//如上代码 当指定一类设备扫描时,函数hd_set_probe_feature_hw调用hd_set_probe_feature 设置只扫加入的地方。
* Note: hw_tv _must_ be < hw_display!
* Sync with check_hd and convert_hd!
*/
typedef enum hw_item {
hw_none = 0, hw_sys, hw_cpu, hw_keyboard, hw_braille, hw_mouse,
hw_joystick, hw_printer, hw_scanner, hw_chipcard, hw_monitor, hw_tv,
hw_display, hw_framebuffer, hw_camera, hw_sound, hw_storage_ctrl,
hw_network_ctrl, hw_isdn, hw_modem, hw_network, hw_disk, hw_partition,
hw_cdrom, hw_floppy, hw_manual, hw_usb_ctrl, hw_usb, hw_bios, hw_pci,
hw_isapnp, hw_bridge, hw_hub, hw_scsi, hw_ide, hw_memory, hw_dvb,
hw_pcmcia, hw_pcmcia_ctrl, hw_ieee1394, hw_ieee1394_ctrl, hw_hotplug,
hw_hotplug_ctrl, hw_zip, hw_pppoe, hw_wlan, hw_redasd, hw_dsl, hw_block,
hw_tape, hw_vbe, hw_bluetooth, hw_fingerprint, hw_mmc_ctrl,
/** append new entries here */
hw_unknown, hw_all /**< hw_all must be last */
} hd_hw_item_t;
//针对设备作粗略的分类
/**
* Individual hardware item.
* Every hardware component gets an \ref hd_t entry. A list of all hardware
* items is in \ref hd_data_t::hd.
*/
typedef struct s_hd_t {
struct s_hd_t *next; /**< Link to next hardware item. */
/**
* Unique index, starting at 1.
* Use \ref hd_get_device_by_idx() to look up an hardware entry by index. And don't
* free the result!
*/
unsigned idx;
。。。。。。。。。。。
} hd_t;
/**
* Holds all data accumulated during hardware probing.
*/
typedef struct {
/**
* @brief Current hardware list.
* The list of all currently probed hardware. This is not identical with
* the result of \ref hd_list(). (But a superset of it.)
*/
hd_t *hd;
。。。。。。。。。。。。
str_list_t *only;
/*
* The following entries should *not* be accessed outside of libhd!
*/
unsigned char probe[(pr_all + 7) / 8]; /**< (Internal) bitmask of probing features. */
unsigned char probe_set[(pr_all + 7) / 8]; /**< (Iternal) bitmask of probing features that will always be set. */
unsigned char probe_clr[(pr_all + 7) / 8]; /**< (Internal) bitmask of probing features that will always be reset. */
hal_prop_t *probe_val; /**< (Internal) probing features with arbitrary values */
unsigned last_idx; /**< (Internal) index of the last hd entry generated */
unsigned module; /**< (Internal) the current probing module we are in */
enum boot_arch boot; /**< (Internal) boot method */
hd_t *old_hd; /**< (Internal) old (outdated) entries (if you scan more than once) */
pci_t *pci; /**< (Internal) raw PCI data */
isapnp_t *isapnp; /**< (Internal) raw ISA-PnP data */
cdrom_info_t *cdrom; /**< (Internal) CDROM devs from PROC_CDROM_INFO */
str_list_t *net; /**< (Internal) list of network interfaces */
str_list_t *floppy; /**< (Internal) contents of PROC_NVRAM, used by the floppy module */
misc_t *misc; /**< (Internal) data gathered in the misc module */
serial_t *serial; /**< (Internal) /proc's serial info */
scsi_t *scsi; /**< (Internal) raw SCSI data */
ser_device_t *ser_mouse; /**< (Internal) info about serial mice */
ser_device_t *ser_modem; /**< (Internal) info about serial modems */
str_list_t *cpu; /**< (Internal) /proc/cpuinfo */
str_list_t *klog; /**< (Internal) kernel log */
str_list_t *proc_usb; /**< (Internal) /proc/bus/usb info */
usb_t *usb; /**< (Internal) usb info */
modinfo_t *modinfo_ext; /**< (Internal) external module info */
modinfo_t *modinfo; /**< (Internal) module info */
hddb2_data_t *hddb2[2]; /**< (Internal) hardware database */
str_list_t *kmods; /**< (Internal) list of active kernel modules */
uint64_t used_irqs; /**< (Internal) irq usage */
uint64_t assigned_irqs; /**< (Internal) irqs automatically assigned by libhd (for driver info) */
memory_range_t bios_rom; /**< (Internal) BIOS 0xc0000 - 0xfffff */
memory_range_t bios_ram; /**< (Internal) BIOS 0x00400 - 0x004ff */
memory_range_t bios_ebda; /**< (Internal) EBDA */
} hd_data_t;
hd_data_t 里的数据反复被每个环节的作操作,往里按逻辑写入各种设备的信息数据。
hal.c hal.h
只有3个接口,扫描设备:
void hd_scan_hal(hd_data_t *hd_data);
void hd_scan_hal_basic(hd_data_t *hd_data);
void hd_scan_hal_assign_udi(hd_data_t *hd_data);
hd_int.h
这里定义着各种linux操作系统的接口文件或命令宏。
#define PROC_CMDLINE "/proc/cmdline"
#define PROC_PCI_DEVICES "/proc/bus/pci/devices"
#define PROC_PCI_BUS "/proc/bus/pci"
#define PROC_CPUINFO "/proc/cpuinfo"
#define PROC_IOPORTS "/proc/ioports"
#define DEV_MICE "/dev/input/mice"
#define DEV_FB "/dev/fb"
#define DEV_FB0 "/dev/fb0"
。。。。。。
#define PROG_MODPROBE "/sbin/modprobe"
#define PROG_RMMOD "/sbin/rmmod"
#define PROG_CARDCTL "/sbin/cardctl"
#define PROGRESS(a, b, c) progress(hd_data, a, b, c)
#define ADD2LOG(a...) hd_log_printf(hd_data, a)
src/hd/hd.c 是所有设备数据操作的实现。
ADD2LOG("----- /proc/bus/input/devices end -----\n");
ADD2LOG(" abs = %s\n", abso);
void hd_log_printf(hd_data_t *hd_data, char *format, ...)
{
ssize_t l;
char *s = NULL;
va_list args;
va_start(args, format);
l = vasprintf(&s, format, args);
va_end(args);
hd_log(hd_data, s, l); //jeff 认为这里将 获取内容或日志写入了hd_data,供后续解析
free(s);
}
hdp.h hdp.c
格式输出 显示
hddb.h hddb.c
src/hd/hddb.c db数据库的查询解析处理。
再看看其它相关文件:
src/hd$ ls
bios.c cpu.c edd.c hal.c hd.h input.c isapnp.c klog.c memory.c monitor.c parallel.c pppoe.c sbus.c sys.c
bios.h cpu.h edd.h hal.h hd_int.h input.h isapnp.h klog.h memory.h monitor.h parallel.h pppoe.h sbus.h sys.h
block.c drm.c fb.c hd.c int.c isdn.c Makefile misc.c mouse.c pci.c prom.c serial.c usb.c
block.h drm.h fb.h hddb.c int.h isdn.h manual.c misc.h mouse.h pci.h prom.h serial.h usb.h
braille.c dvd.c floppy.c hddb.h hwclass_names.h isa.c kbd.c manual.h modem.c net.c pcmcia.c s390.c smbios.c wlan.c
braille.h dvd.h floppy.h hddb_int.h ibm-notebooks.h isa.h kbd.h mdt.c modem.h net.h pcmcia.h s390.h smbios.h wlan.h
从上可看出一个 xx.c+xx.h代表一种类的设备;在xx.h中有入口函数。
因涉太多的设备,且每一样都 不一样,故在后续开章节再来一一道 来。
代码编译运行测试
make 前记得加上如下环境变量:
export LIBDIR=/usr/lib/$(shell uname -m)-linux-gnu
export HWINFO_VERSION=21.63.10 #定义编译的版本
需要sudo make install 才能调试,否则需要自己指定hwinfo 编译链接运行的路径。
小结
其中太多的用c语言用来解析字符串文件感觉 ,感觉就是用手术刀 切牛,从编程效率和维护角度讲,太难了。
但从代码上可看出:原作者对C语言指针与链表,linux系统接口编程及硬件这块掌握的太深,很多逻辑都 需要与每块linux子框架系统相结合作了解 。
后续针对每类设备,第个系统文件作一一分析, je f f得花上好多时间与精力,写到哪算哪吧。