打造嵌入式c调试系统2--状态信息实现

本文介绍用户态状态信息的设计方法,用户态不像驱动,驱动加状态信息,可以用proc系统,可以用debugfs,反正就是现成的,用户态目前没有统一的解决方案,要自己设计,本文介绍了一些常用的设计思路,以供参考。

状态信息

什么是状态信息,直接看两个例子:

典型的状态系统是linux的proc(proc是内核态的,本文主要讲的是用户态应用状态信息设计,举这个例子只是外在表现的借鉴),如下:

~ # cat /proc/297/task/300/stat
300 (timer) S 1 47 47 0 -1 1077936192 43 2351 0 0 4422 22030 20 18 20 0 107 0 947 1230798848 57047 4294967295 65536 14535940 2124209840 1993334272 1994546904 0 0 4096 1073759470 2147897988 0 0 -1 1 0 0 0 0 0 14602240 15256008 22667264 2124210065 2124210071 2124210071 2124210166 0

这是一个线程的stat信息,可以看到全是数字,proc里面的信息有一个特点,信息很全,至于干啥用的,proc信息本身不体现出来,都是等着第三方工具去解析使用。
下面是某大公司(HIS)的状态信息,如下:
在这里插入图片描述

用过的同学肯定都很熟悉了(图片有处理,原信息太多,截取部分),最早接触这玩意的时候,他们技术支持工程师最常说的一句话就是,把umap信息拷3遍给我发过来,都很少问我们问题(这点很重要,我们也碰到有的公司,用他们的产品,出问题后,让他们解问题,搞的我们花的时间比他们都多,整天瞎猜,各种验证,最后整得我们是不严重的问题都不上报),很有可能第二天就有结果了,当时我就被这种查问题的方式刺激到了,那时候我们查问题基本靠重现、打印,方法很少,查问题如看天书,能不能懂要看运气,后来,我们慢慢的完美的copy(程序员不说山寨的事)了这么一套系统(用户态),再后来各个方面慢慢完善起来了,终于改蒙问题为查问题了。

有依据的查问题叫调试问题,没有依据的只能叫蒙,虽然调试也有时候会做一些假设猜测,但只是依据不足时的辅助手段。状态信息就是一种很重要的依据,看上面的两个例子,还是有一些明显的差异,如下:

proc信息HIS状态信息
适合第三方工具查看适合人工看
信息简明意义清晰
无格式,排布无规律有严格的格式,看起来很工整

建议大家采用HIS这种形式的状态信息,像proc这种,不借助工具看太难看了,linux系统这么设计proc,可能是因为内核东西太多,我们的一般没这么大,可以把信息写的更明了一些(除非是对代码大小非常严格,省点flash空间,这个会省的非常少)。

状态信息设计及使用

再看一下以前提到的状态信息内容:

  • 状态信息是一些全局量、过程量、统计信息的外在体现。
    重要的全局量需要纳入到状态信息中去,一个问题的表现,往往牵涉了很多变量,调试问题的时候,你肯定希望知道某些全局量的值,这些值都是需要加入到状态信息中。
    过程量指的是有一定生命周期的变量,如一个对象,创建的时候可以同时创建状态信息(如用户登录时的信息),销毁的时候销毁状态信息。
    统计信息有两种类型的,一种对外提供接口的,直接加到状态信息中去,另一种外面不需要,完全是自己用的,则需要额外的加。
  • 系统当前的运行状态,对监控者来说,系统是透明的,运行过程是可量化的。
    在增加状态信息之前,需增加打印确定的值,都可以加到状态信息中,但注意与日志间的区别。
  • 包含必要的统计信息,系统功能可能用不到这些信息,但对系统监控是不可缺少的。
    如某函数执行最长消耗的时间,不加这个信息,只能看到函数当前执行时间,这是对信息的一种补充。
  • 信息需成体系,如相关模块A与B,模块A的输出能与模块B的输入对接,对接的不止是功能,状态信息也能匹配。
    一个应用系统里面一般包含多个模块,对同一信息,不同模块都会作一些处理,传给下一级模块,出了问题,是哪一层的问题呢?每个模块通过自己的状态信息来说明自己没有问题,可以快速定位到出问题的模块。

状态信息实现方式

上面举的两个例子,只能参考,因为proc信息都是在内核态下面,应用想要获取程序的状态信息,需要一些技巧来把全局量等信息展示出来,简单的说,就是在终端上通过命令能获取应用程序内部信息的方式,获取应用程序内部信息的方式太多了,这个就是ipc(进程通信)的方式,ipc方式包括:

  • socket通信:可以用,非常灵活。
  • 管道:不推荐,不如用socket。
  • 消息队列:不推荐,同上。
  • 共享内存:有特点,某些情况下可用。
    除了这几种方式,ipc方式都是双工的,收发可以走同一条路,其实收发可以分开,比如用socket传递要获取状态信息项,输出在终端直接打印,也是一种方法。状态信息还可以用vfs来做,搞得跟proc一样的,方法是很多的。

需要注意的是,不论何种方法,都不能采用定时把变量信息写到某个地方,然后用其他程序去读的方式,一定是需查看时,实时读变量的值后再输出,否则效率太低,对系统影响太大。
下面介绍几个状态信息的设计方法,并列出一些优缺点作对比,下面说的:

  • 不支持vfs:指的是不能像proc那样,直接copy,或者grep等,获取全部状态信息或者查找指定的信息就会比较麻烦。
  • 需工具支持:用vfs,直接cat查看就可以了,但用socket这种方式,得用工具(很简单的程序)才能查看。

共享内存

共享内存实现状态信息方式如上图:

  • 应用程序把对象或者变量放到共享内存中(可以设计成共享内存池),变量的读写都在共享内存中完成。
  • 当需要获取的时候,使用客户端实时读共享内存的内容
优点缺点
应用程序异常退出后,状态信息还在内存管理麻烦
数据结构和输出容易出现不匹配问题
不支持vfs
需要专门的工具支持

可以看到,共享内存式的状态信息唯一的优点是应用程序退出后,还可以看到状态信息,这也是其他几种方式不具备的优点,可以辅助着用。

socket通信

这种方式非常灵活,实现过程:

  • 制定通讯协议,定好协议头、输入(要获取的状态项)数据、输出(状态信息)数据
  • 客户端和应用程序按协议实现好。
  • 需要获取状态信息时,通过客户端从应用程序获取到相关信息,并输出。
优点缺点
灵活性强,在pc上都可以用不支持vfs
可以传递参数需要专门的工具支持
设计协议和实现稍微麻烦

这种方式推荐使用,最大的特点是灵活性非常强,可以把传递参数给应用程序,这是其它方式比不了的。

proc实现

这种方式是在应用层使用proc,看起来和用起来和proc都没什么区别。实现过程:

  • 需要专门写驱动支持,驱动启动之前要加载好,应用程序和驱动通讯。
  • 根据应用需要,驱动程序在proc里面注册目录和文件,应用程序负责提供信息,驱动程序负责输出。
  • 用户查看状态信息时,如cat /proc/my_test/stat,信息流是这样的:
    • 收到命令后,proc虚拟文件系统调用自定义驱动的注册函数。
    • 驱动注册函数向应用程序获取状态信息数据(可以使用netlink通信)
    • 驱动获取到信息后可以显示出来了
优点缺点
支持vfs,可以cat、grep、cp等需要自己写驱动支持
不需要开发客户端自己实现通讯协议,熟悉netlink等通信方式
完全的用户态proc,逼格高实现难度稍高

这种方式有兴趣可以用,高度可控,代码量也不多(比fuse方式少,fuse库会大一些),做好了用起来比较舒服。

fuse方式

在这里插入图片描述
fuse就是用户态的文件系统,干这个事感觉就是量身定做的,下一篇重点介绍这种方式。

优点缺点
用起来简单,可以用现成的libfuse库稍微大了点(500k)
支持vfs
无需客户端

与上一种方式比较类似,都支持vfs,整体获取状态信息非常方便直接copy整个目录就好,上面的方式需自己写驱动来实现应用和内核通信,fuse方式通过/dev/fuse设备来通信,可以说两种方式共同点挺多的,不过libfuse别人已经写好了,拿来用就可以了。自己写非常少的代码就可以整出一套完善的状态信息系统。

方式的选用

不同方式各有优劣,大家可以根据自己目前系统的具体情况来选用,也可以几种方式混用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值