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是怎么工作的。
浙公网安备 33010602011771号