嵌入式Hacker (es-hacker)

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

0%

Linux系统编程-分析开源软件Triggerhappy (2)


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

一、构建思维组块

构建组块(chunking):

  • 就是利用一些概念,组合起信息碎片,这是一种心智上的飞跃。

构建组块的方法:

  • 把注意力集中在需要组块的信息上 。思维就像章鱼触手,一旦分心就无法抓紧。

  • 把基本概念打包成组块,然后理解这个基本概念。专注和发散模式的交替思考,总能理清头绪,把握概念。

  • 获取背景信息,不仅是知道如何进行组块,还要知道何时何地如何使用该组块。

学习活动包括 “以上至下、从下至上” 两个方向:

  • 从上至下的宏观学习,和从下至上的组块活动,两个过程对熟练掌握知识都有重要作用。

应用到软件开发上:

  • 大多数编程书里是教授的都是大量的细微的知识点,我们需要先把这些非常小的知识点体系化,形成多个组块,这是从下至上的组块活动。观察现有的优秀软件 (一般是开源软件) 是如何使用组块的,这是从上至下的宏观学习。

  • 学习软件开发要多练习抽象,写代码是抽象再组合,而读代码则是分解出一个个小的组块。


二、Linux系统编程 / 分析开源软件 Triggerhappy / (2) 把握核心流程

正文目录:

1
1. 分解 thd.c / start_readers()
2
1.1 thd.c / start_readers() 的作用
3
1.2 thd.c / start_readers() 的内容
4
    1) 根据命令行参数,判断是否需要打开 socket
5
    2) 将命令行中指定的输入设备添加到设备链表中
6
    3) 根据命令行参数,决定是否成为守护进程 (daemon)
7
    4) 根据命令行参数,决定是否创建 pidfile
8
    5) 真正开始监控设备并处理事件

写作目的:

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

测试环境:

  • Ubuntu 16.04
  • Gcc 5.4.0

1. 分解 thd.c / start_readers()

1.1 thd.c / start_readers() 的作用

1) 作用:

  • 启动读进程,虽然该程序是单进程的,复数 readers 可能是作者觉得有机会使用多线程吧。

2) 使用者:

  • 只有 thd.c / main() 会调用。

1.2 thd.c / start_readers() 的内容

1) 根据命令行参数,判断是否需要打开 socket:

1
/* open command pipe */
2
if (cmd_file) {
3
	cmd_fd = bind_cmdsocket(cmd_file);
4
	if (cmd_fd < 0) {
5
		return 1;
6
	}
7
}

cmd_file 对应参数 -s:

1
$ man thd
2
--socket file
3
    Open a unix domain socket at file; this socket can be used to send commands to the running daemon (by
4
    using the program th-cmd), e.g. for adding or removing devices.
  • 这里的 socket 类型是 UNIX domain socket (AF_UNIX),该 socket 允许位于同一主机系统上的进程之间相互通信的

  • bind_cmdsocket() 的实现位于 cmdsocket.c,thd 和 thd-cmd 会共用 cmdsocket.c 来通过 socket 通讯以动态地增减要监控的输入设备.

  • 后续再来细看 cmdsocket.c 的内容,分析该文件可以学习 UNIX domain socket 相关的知识

2) 将命令行中指定的输入设备添加到设备链表中:

1
/* add every device file supplied on command line */
2
for (i=start; i<argc; i++)
3
    add_device( dev, -1, grab_dev, tag_dev );
  • thd 要监控的设备可以从2个地方指定:1. 命令行参数,2. glob device,这里处理的是情况1。

  • add_device() 的实现位于 devices.c, 该文件通过链表 (device *device_list) 来管理所有要监控的输入设备,肯定会有这些操作:增加 (add_device)、删除 ( remove_device )、遍历 (for_each_device)。

  • 后续再来细看 devices.c 的内容,分析该文件可以学习到数据结构链表相关的知识

2) 将 glob device 指定的输入设备添加到设备链表中:

1
/* check device glob */
2
glob_t globbuf;
3
glob(dev_glob, GLOB_NOSORT, NULL, &globbuf);
4
for (i=0; i<globbuf.gl_pathc; i++)
5
    add_device(globbuf.gl_pathv[i], -1, grab_dev, tag_dev);

glob device 是什么?

1
$ man thd
2
--deviceglob pattern
3
    Open device files matching the glob pattern.
  • glob 库函数用于 Linux 文件系统中路径名称的模式匹配, 看来是用来处理类似 /dev/input/event* 的情况。

3) 根据命令行参数,决定是否成为守护进程 (daemon):

1
if (run_as_daemon)
2
    daemon(0,0);

什么是 daemon?

  • daemon 运行在后台,不与任何控制终端相关联。守护进程通常在系统启动时就运行,它们以 root 用户或者其他特殊的用户运行,并处理一些系统级的任务。

  • daemon 的两个基本要求:1. 必须作为 init 进程的子进程运行,2. 不与任何控制终端交互。

GNU C库提供了一个非标准的 daemon()函数,它将调用者变成一个 daemon,thd 就使用了该函数。

4) 根据命令行参数,决定是否创建 pidfile:

1
if (pidfile)
2
    write_pidfile( pidfile );
  • write_pidfile() 是 thd.c 的私有函数。

  • pidfile 为文本文件,一般内容只有一行, 记录了该进程的 PID。

  • 它的作用是防止进程启动多个副本。

5) 真正开始监控设备并处理事件:

1
process_events();
  • process_events() 是 thd.c 的私有函数。

  • 接下来需要重点分析该函数

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

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

  1. 分析 thd.c / process_events();
  2. 分析 thd.c / reload_triggerfile()

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


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

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

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

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

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

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

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

这是一篇有趣的文章吗?

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