dbus源码走读-1 main.c

dbus版本:1.13.12

一、目录结构:

dbus根目录下只有一些用于构建的配置文件(CMakeLists.txt, autogen.sh configure.ac等等)和readme news 等等。其下有如下子目录:

build-aux

bus

cmake

dbus

doc

m4

test

tools

从目录名就可以看出代码主要在bus,dbus和tools中。其中dbus中的文件都是dbus打头的,不妨猜测它们是dbus层面的功能实现或供用户使用的API。bus下主要包括config-*.c containers.c dispatch.c driver.c policy.c signal.c services. stats.c等等, 可以看出它们处理的是相对更低层的功能。这相当于dbus把代码分成了bus和dbus两层。另外,从概念的角度,bus是比dbus更广泛的一个概念,所以bus是更低层的,这里有点面向对象的“依赖倒置原则"的味道。

尽管如此,bus或dbus下都有很多文件,从哪个文件开始呢?dbus有一个daemon进程,它应该对应一个main函数。bus目录下有一个main.c文件,里面正好有一个main函数,它就是dbus server进程的总入口。一番折腾我们到达了出发点。此后我们将从这里开始走都dbus源码。

二、起点:

bus/main.c/main()函数。

main()函数首先定义了几个结构体变量。主要涉及DBusError DBusString DBusPipe dbus_bool_t和BusContextFlags。

[2022-10-07]

首先映入眼帘的是void signal_handler(int sig)函数。很显然,这是dbus server处理信号的函数,那我们就来看看它处理了哪些信号吧。

在此之前需要看一个枚举类型,SignalAction, 定义了两个值: ACTION_RELOAD='r', 和ACTION_QUIT='q'。显然前者对应重新加载行为,后者是退出行为。

signal_handler的主题是一个switch语句,处理了SIGHUP和SIGTERM两个信号。即如果对dbus进程发送SIGHUP信号,它会执行重新加载行为,ubus也继承了这一特性,当ubusd收到SIGHUP信号时,会去重新加载acl文件。dbus也会执行一些类似的配置文件的重新加载。但是加载配置的操作并不是在signal_handler函数中执行的,signal_hander只是向reload_pipe socket中写入了一个action字符串"r"。reload_pipe被定义为一个socket pair(DBusSocket reload_pipe[2])。同理当收到SIGTERM信号后,它会向reload_pipe中写入退出命令字符串"q"。由于socket相关的系统调用会设置errno,因此signal_handler在一开始保存了原来的errno值,并在退出前进行了恢复。稍微想想就知道为什么要这么做。

第二函数是usage(),这就是用来打印命令行格式和选项的一个函数,不细说。

第三个函数是version(),就是显示版本信息和版权信息的,也不细说。

第四个是introspect()函数,字面意思是自捡函数。具体内容是初始化了一个DBusString, 即xml, 然后在里面写了一些内容,就是一个xml字符串,包含node ,interface和method等节点。然后把该字符串打印出来。主要的异常是内存分配失败,会输出OoM错误,并退出程序。没大理解到introspect的真正含义是什么。[TODO:]

接下来是一些check_xxx函数,包含配置文件(config_files)、地址(addresses)、地址描述符(addr_descriptor)、pid描述符(pid_descriptor)等。

第十个函数十handle_reload_watch(),这个函数就具体做重载操作的,它会从reload_pipe中读取命令,然后根据action进行相应的操作。如果是reload命令,就会调用bus_context_reload_config函数去重新加载配置文件。略过详细的加载过程。

第十一个函数十setup_reload_pipe(),这个就是为重新加载操作设置socket pair的函数。

第十二个函数close_reload_pipe,显然十关闭socket的函数。

以上这些都是main.c中的static函数,它们都是构建main函数的小模块。

第十三个函数才是main.c的主体,也是dbus的入口,main()函数。

[2022-10-12]

好吧,今天的目标是go through main()函数。

main()函数的结构很简单,基本都是顺序执行,但是它做了很多准备工作。

1. 重定向stdin和stdout 到/dev/null。_dbus_ensure_standard_fds()-->dup2()

2.关闭所有继承的file descriptor。_dbus_ensure_standard_fds()->opendir ("/proc/self/fd")

                            ->_dbus_fd_set_close_on_exec(fd)

3. 初始化配置文件、地址、pid_fd等字符串。

_dbus_string_init (&config_file);

_dbus_string_init (&address);

_dbus_string_init (&addr_fd);

_dbus_string_init (&pid_fd);

4. 解析命令行参数。支持以下参数:

--help、-h、-?

--version

--introspect

--nosyslog

--syslog

--syslog-only

--nofork

--fork

--systemd-activation

--nopidfile

--system

--session

--config-file=

--address=

--print-address=

--print-pid

5. 如果print_address is true, then read a number from file defined by address_fd, and assign to print_addr_pipe.

6. if print_pid_pipe is true, then read a number from file defined by pid_fd, and assign to print_pid_pipe.

7. 然后是执行bus_selinux_pre_init()->is_selinux_enabled (),检查是否开启了selinux。

8. 然后是执行bus_apparmor_pre_init (), 检查是否开了apparmor。

9. 然后是bus_context_new(), 即创建bus上下文,这是重点的重点。dbus的所有服务都是在bus_context中完成的。bus_context的创建过程后面会重点阅读。

10. setup_reload_pipe (bus_context_get_loop (context));设置reload管道。dbusd进程收到SIGHUP或SIGTERM信号后,会向该管道发送一个action消息,然后根据action类别执行相应的操作,SIGHUP:重新加载配置信息;SIGTERM:终止服务。

11. 设置信号处理函数

_dbus_set_signal_handler (SIGTERM, signal_handler);

_dbus_set_signal_handler (SIGHUP, signal_handler);

12. 然后是报告dbus_daemon 就绪,只在systemd系统中有效。

_dbus_daemon_report_ready()->sd_notify (0, "READY=1");

13. 最后是进入主循环。如果该函数返回,意味着进程退出。因此此函数调用以后的代码都是在做一些资源释放操作。
_dbus_loop_run (bus_context_get_loop (context));

14. 扫尾工作:

bus_context_shutdown (context);
bus_context_unref (context);
bus_selinux_shutdown ();
bus_apparmor_shutdown ();
bus_audit_shutdown ();

下一步我们将窥探bus_context_new()的内部,看看dbus_daemon是怎么工作的。

posted @ 2022-10-12 15:31  耕读编码  阅读(710)  评论(0)    收藏  举报