进程占用磁盘的排查方式
工作中遇到了进程X 占用磁盘(或者说目录),引起磁盘无法格式化(可以近似认为目录无法被删除)的问题,之前出现这种情况,多为进程在磁盘(目录)上拥有打开的文件句柄,使用lsof命令可以看到有占用句柄的进程,故而在要进行磁盘格式化前后增加了模块退出机制,去除模块的对磁盘的占用。但本次的问题出现情况不同,对应进程X 从表现上看确实存在对磁盘的占用,使用lsof或者fuser命令都可以查看到占用磁盘路径的进程pid列表中存在对应进程X。但从代码层面看,进程X确实不涉及到对磁盘的占用。
fuser - identify processes using files or sockets
-m NAME, --mount NAME
指定文件或者挂载的设备
Usage:
fuser -m "/path/"
lsof - 列出当前系统打开文件
Usage:
lsof "/path"
父子进程关联
排查过程中发现,杀死进程X 单独启动,发现对磁盘目录的占用就去除了。
因之前遇到过由于未设置 FD_CLOEXEC 标记位,而出现了代码中只起了一个监听套接字,但在netstat中出现了有两个监听相同端口的套接字的奇怪现象,最终发现是由于在创建套接字时,没有 cloexec,而导致套接字被子进程所继承导致。故而怀疑该占用是不是也是由于父进程的操作而引起的,因为对应进程X 恰好是由进程A 通过system调用启动的。
system实际的执行流程就是调用,fork->exec->wait。而fork的过程中,子进程会继承父进程的如下资源
- 用户号UIDs和用户组号GIDs
- 环境 Envronment
- 堆栈
- 共享内存
- 打开的文件描述符
- 执行时关闭标志
- 信号控制设定
- 进程组号
- 当前工作目录
- 根目录
- 文件方式创建屏蔽字
- 资源限制
- 控制终端
其中可能引起占用的有两个点,一个为打开的文件描述符,一个为当前工作目录。
- 如果子进程包含打开的文件描述符,则lsof会显示打开的文件句柄的路径。但从代码中看进程X的实现代码确实不存在打开句柄的情况
- 故而更有可能的是子进程对父进程工作目录的继承,而引起的对路径的占用。
工作目录
关于工作目录,我们最常用的就是cd命令,通过cd命令,修改我们当前所在的shell的工作目录。调整了我们调用其他可执行文件的相对位置的起点。针对unix,可以使用 getcwd来获取当前的工作目录,也可以通过chdir来切换当前工作目录。
getcwd - copies an absolute pathname of the current working directory to the array pointed to by buf, which is of length size
Usage:
char szBuf[1024] = {0};
getcwd(szBuf, sizeof(szBuf));
chdir - changes the current working directory of the calling process to the directory specified in path
int chdir(const char *path);
通过排查找到父进程A 确实新引入了chdir,将工作目录切换到了磁盘路径,并在此后不再切换工作目录。
通过在模块结束时,重新将chdir工作目录切换到默认的工作目录,重新启动进程A,可以发现通过system调用的进程X不再占用 磁盘路径。磁盘可以正常被格式化。
这里的默认工作目录是什么呢?其实也就是进程A的父进程的工作目录,进程A是在开机启动过程中启动的,故而继承了系统shell的工作目录,这个工作目录是在/etc/bashrc中配置的。
这里要注意另外一点,父进程中切换工作目录是会影响后面system调用启动的子进程,但是子进程中调用chdir,或者脚本中调用cd,是不会影响到父进程的工作目录的。
参考
Linux中父子进程的继承关系
https://blog.csdn.net/feiyagogogo/article/details/79671398
popen/system与fork函数
https://blog.csdn.net/Cyrus_wen/article/details/80018721