嵌入式Hacker (es-hacker)

Embedded bsp developer enjoys thinking and hacking opensource and develop boards(NanoPi, LicheePi, RPi...)

0%

Linux系统编程/分析开源软件Triggerhappy (1)


哈喽,我是老吴,继续记录我的学习心得

一、自制力太强导致低效专注

自制力很强的人的成功之道在于,在别人都放弃的情况下仍坚持不懈。

但是这反而会让他们难以关闭专注模式,导致无法进入解决难题所必要的发散模式。

但是,对于这种人 ( 我自己典型案例 ),需要特别重视学习另外一个技巧:多点倾诉和多点倾听。将把同伴、朋友或亲人的意见放在心上。

有时我在尝试解决一个 BUG 的时候,我很容易就进入专注模式,我会认为我一直在深挖问题的根源,迟早会确定问题。但是 更有效地方式是:一段时间的专注加上小片时间的沟通

当我尝试向同事解释我所遇到的问题时,一般收获都会很大:

  1. 帮自己整理思路,如果我的同事明白了我遇到的问题,说明我的思路清晰。而且这时候偶尔我自己也会灵光一闪,就想到一个新的思路。

  2. 由于同事是旁观者,很容易找到最不可理解以及最可疑的点。

  3. 同事的水平较高,经验丰富,很可能直接看出问题出在哪里。

所以,下次遇到 BUG 时,不要自己埋头苦干啦。持续的埋头苦干,可能只是无用功。

想解决问题,吃点东西,跟同事们聊会天吧。


二、Linux系统编程 / 分析开源软件 Triggerhappy / (1) 把握整体

正文目录:

1
1. HappyTrigger 是什么?
2
    1) 概述
3
    2) 简单地试用
4
    3) 配置文件
5
    4) 使用 socket 通信以动态增加和删除输入设备
6
2. 查看 HappyTrigger 源码文件
7
    1) 查看Makefile 文件以确定编译流程
8
    2) 确定 C source 文件
9
3. 分析入口文件 thd.c
10
    1) thd.c 的作用
11
    2) thd.c 的使用者
12
    3) 分解 thd.c
13
    4) 分解 thd.c/main()

写作目的:

  • 通过分析开源软件 Triggerhappy,练习 Linux 系统编程。

测试环境:

  • Ubuntu 16.04
  • Gcc 5.4.0

1. HappyTrigger 是什么?

1) 概述

Github 仓库: Triggerhappy

简单地说,Triggerhappy (简称 thd) 是一个轻量级的热键守护程序 ( lightweight hotkey daemon )。它比较适合小型的嵌入式系统,例如基于 Linux 的路由器。

Triggerhappy 提供了系统级的热键处理功能。它会监控指定的输入设备文件,解析接收到的事件数据并根据配置文件执行用户指定的操作 (user command)。例如你的设备的 UI 是由 2 个不同的程序分为处理前后端的,那么你可以使用 Triggerhappy 来处理 Home 键以返回到前端 UI。

另外一个典型的例子是,你可以用 Triggerhappy 配置音量的增减功能。

2) 简单地试用:

1
$ thd --dump /dev/input/event0
2
3
EV_KEY  KEY_VOLUMEDOWN  1       /dev/input/event0
4
# KEY_VOLUMEDOWN        1       command
5
EV_KEY  KEY_VOLUMEDOWN  0       /dev/input/event0
6
# KEY_VOLUMEDOWN        0       command

打印输入设备 event0 接受到的事件。

3) 配置文件:

1
$ cat /etc/triggerhappy/triggers.d/example.conf
2
3
KEY_VOLUMEUP    1        /usr/bin/amixer set Master 5%+
4
KEY_VOLUMEUP    2        /usr/bin/amixer set Master 5%+
5
KEY_VOLUMEDOWN    1        /usr/bin/amixer set Master 5%-
6
KEY_VOLUMEDOWN    2        /usr/bin/amixer set Master 5%-

0 表示松开按键时 (released) 触发 command;

1 表示按下按键时 (pressed) 触发 command;

2 表示按键保持被按下 (holding) 时触发 command;

支持组合键:

1
KEY_VOLUMEUP+KEY_LEFTSHIFT	1	/usr/bin/amixer set Master 15%+

4) 使用 socket 通信以动态增加和删除输入设备:

可以通过辅助程序 thd-cmd 来和 主程序 thd 进行通讯:

1
$ thd --socket /var/run/triggerhappy.socket
2
3
$ th-cmd --socket /var/run/triggerhappy.socket --add /dev/input/event0

上述命令动态地添加了要监测的输入设备 event0。

5) 更多细节:

1
$ man thd
2
$ man thd-cmd

2. 查看 HappyTrigger 源码文件

查看源码文件,是为了确定阅读代码的入口点,相关命令:

1
$ cd triggerhappy
2
$ ls -1X

1) 查看Makefile 文件以确定编译流程:

共有 3 个目标:

1
all: thd th-cmd man

目标 thd / th-cmd 都是通过源码生成 bin 文件,目标 man 则是安装用户手册。

thd 是怎么生成的?

1
thd: $(THD_COMPS:%=%.o)
2
    -> thd: thd.o keystate.o trigger.o eventnames.o devices.o cmdsocket.o obey.o ignore.o uinput.o triggerparser.o

这里可以确定 thd 相关的源码文件。

th-cmd 是怎样生成的?

1
th-cmd: $(THCMD_COMPS:%=%.o)
2
    -> th-cmd: th-cmd.o cmdsocket.o

看来 thd 和 thd-cmd 共用代码 cmdsocket.c

2) 确定 C source 文件:

1
cmdsocket.c
2
devices.c
3
eventnames.c
4
ignore.c
5
keystate.c
6
obey.c
7
th-cmd.c
8
thd.c
9
trigger.c
10
triggerparser.c
11
uinput.c

目前只需要知道入口文件是 thd.c,其他文件的功能我们可以随意猜测。

3) 确定 C header 文件:

1
cmdsocket.h
2
command.h
3
devtag.h
4
eventnames.h
5
eventtable.h
6
ignore.h
7
keystate.h
8
obey.h
9
thd.h
10
trigger.h
11
triggerparser.h
12
uinput.h
13
version.h

3. 分析入口文件 thd.c

1) thd.c 的作用:
Triggerhappy 的主程序,负责核心业务逻辑。

2) thd.c 的使用者:
就是 Triggerhappy 的用户。

3) 分解 thd.c:

公开函数:

1
void show_help(void)
2
void cleanup(void)
3
int main(int argc, char *argv[])
4
int start_readers(int argc, char *argv[], int start, char *dev_glob)

由于 thd.c 是 Triggerhappy 的主程序,它基本不需要对外部暴露接口。目前我们只要留意 main() 函数就好。

私有函数:

1
static void print_event(char* dev, struct input_event ev) 
2
static void print_triggerline(struct input_event ev, keystate_holder ksh) 
3
static int read_event( device *dev ) 
4
static void check_device( device *d ) 
5
static void process_devices(void) 
6
static void add_device_to_fdset( device *d ) 
7
static void process_events(void) 
8
static int write_pidfile( char *pidfile ) 
9
static void list_event_table(int type, int max) 
10
static void list_events(void) 
11
static int reload_triggerfile(void) 
12
static void handle_signal(int sig)

这些函数是我们接下来需要重点关注的,我们要在最合适的时机进行分析。

私有变量:

1
static char *cmd_file 
2
static int cmd_fd 
3
static char *uinput_dev 
4
static int dump_events 
5
static int run_as_daemon 
6
static char *triggerfile 
7
static char *pidfile 
8
static keystate_holder *keystate 
9
static ignore *ignored_keys 
10
static int normalize_events 
11
static char *user 
12
static int exiting 
13
static int reload_conf 
14
static fd_set rfds;
15
static int max_fd 
16
static struct option long_options[]

4) 分解 thd.c/main() :
main() 主要做了下面几件事。

1> 解析参数,这里可以配合 man thd 的参数列表来查看

1
getopt_long (argc, argv, "t:s:dhpni:u:g:", long_options, &option_index);

2> 加载配置文件 (/etc/triggerhappy/triggers.d/*.conf)

1
reload_triggerfile()

reload_triggerfile() 是 thd.c 里的私有函数,后续再细看。

3> init keystate holder

1
init_keystate_holder(&keystate);

一些必要的初始化工作,暂时不用太在意。

4> set initial trigger mode

1
change_trigger_mode("");

一些必要的初始化工作,暂时不用太在意。

5> 设置信号处理函数

1
sigaction(SIGINT,&handler,0);
2
sigaction(SIGTERM,&handler,0);
3
sigaction(SIGHUP,&handler,0);

thd 是要成为守护进程的,并且保持长期运行,设置信号是为了避免被信号杀死且实现重加载配置文件等功能。

6> 开始监控所有输入设备

1
start_readers(argc, argv, optind, dev_glob);

start_readers() 是 thd.c 里的私有函数,接下来需要重点分析该函数

鉴于大多数人的注意力无法在一篇文章里上集中太久,更多的内容请大家先自行去阅读吧,不是自己理解到的东西是消化不了的。有机会的话我会把更多的分析心得放在后面的文章。

总结一下,接下来需要跟进的事情:

  1. 分析 thd.c / start_readers()
  2. 分析 thd.c / reload_triggerfile()

这么看来,目前的任务还比较轻松。


三、思考技术,也要思考人生

学习技术,更要学习如何生活

你和我各有一个苹果,如果我们交换苹果的话,我们还是只有一个苹果。但当你和我各有一个想法,我们交换想法的话,我们就都有两个想法了。

嵌入式系统 (Linux、RTOS、OpenWrt、Android) 和 开源软件 感兴趣,想和更多人互相交流学习,请关注公众号:嵌入式Hacker,一起来学习吧。

无论是关注或转发,还是打赏,都是对作者莫大的支持。觉得文章对你有价值的话,不妨点个 在看和点赞 哦。

ps:
欢迎加入我的微信群:加我,我拉你进群,暗号(加群)。

祝各位工作顺利,家庭幸福,财源滚滚~

这是一篇有趣的文章吗?

欢迎关注我的其它发布渠道