《进程未close文件导致文件资源泄露问题定位 —— lsof》

1.demo.c

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

int main(void)
{
    int cnt = 0;
    int fd = 0;
    char name[64];

    while (1) 
    {
        snprintf(name, sizeof(name),"%d.txt", cnt);
        fd = creat(name, 644);
        sleep(10);
        ++cnt;
    }
    return 0;
}

运行程序:

fgao@fgao chapter1]#./hold_file &
[1] 3000

通过lsof定位问题:

[fgao@fgao chapter1]#lsof -p 3000

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
a.out 3000 fgao cwd DIR 253,2 4096 1321995 /home/fgao/works/my_git_codes/my_books/understanding_apue/sample_codes/chapter1
a.out 3000 fgao rtd DIR 253,1 4096 2 /
a.out 3000 fgao txt REG 253,2 6115 1308841 /home/fgao/works/my_git_codes/my_books/understanding_apue/sample_codes/chapter1/a.out
a.out 3000 fgao mem REG 253,1 157200 1443950 /lib/ld-2.14.90.so
a.out 3000 fgao mem REG 253,1 2012656 1443951 /lib/libc-2.14.90.so
a.out 3000 fgao 0u CHR 136,3 0t0 6 /dev/pts/3
a.out 3000 fgao 1u CHR 136,3 0t0 6 /dev/pts/3
a.out 3000 fgao 2u CHR 136,3 0t0 6 /dev/pts/3
a.out 3000 fgao 3w REG 253,2 0 1309088 /home/fgao/works/my_git_codes/ my_books/understanding_apue/sample_codes/chapter1/0.txt
a.out 3000 fgao 4w REG 253,2 0 1312921 /home/fgao/works/my_git_codes/my_books/understanding_apue/sample_codes/chapter1/1.txt
a.out 3000 fgao 5w REG 253,2 0 1327890 /home/fgao/works/my_git_codes/my_books/understanding_apue/sample_codes/chapter1/2.txt
a.out 3000 fgao 6w REG 253,2 0 1327891 /home/fgao/works/my_git_codes/my_books/understanding_apue/sample_codes/chapter1/3.txt
a.out 3000 fgao 7w REG 253,2 0 1327892 /home/fgao/works/my_git_codes/my_books/understanding_apue/sample_codes/chapter1/4.txt
a.out 3000 fgao 8w REG 253,2 0 1327893 /home/fgao/works/my_git_codes/my_books/understanding_apue/sample_codes/chapter1/5.txt
a.out 3000 fgao 9w REG 253,2 0 1327894 /home/fgao/works/my_git_codes/my_books/understanding_apue/sample_codes/chapter1/6.txt

lsof输出各列信息的意义如下:

COMMAND:进程的名称

PID:进程的id

USER:进程所有者

FD:文件描述符,应用程序通过文件描述符识别该文件。如cwd、txt等

TYPE:文件类型,如DIR、REG等

DEVICE:指定磁盘的名称

SIZE:文件的大小

NODE:索引节点(文件在磁盘上的标识)

NAME:打开文件的确切名称

 

FD列中的文件描述符cwd值表示应用程序的当前工作目录,默认是该应用程序启动的目录,除非它本身对这个目录进行更改。

FD是txt的文件是程序代码,如应用程序二进制文件本身或共享库,如上列表中显示的 /sbin/init 程序。

其次数值表示应用程序的文件描述符,这是打开该文件时返回的一个整数。如上的最后一行文件/dev/null,其文件描述符为2u,u表示该文件被打开并处于读取/写入模式,而不是只读 ® 或只写 (w) 模式。

同时还有大写的W表示该应用程序具有对整个文件的写锁。该文件描述符用于确保每次只能打开一个应用程序实例。初始打开每个应用程序时,都具有三个文件描述符,从 0 到 2,分别表示标准输入、输出和错误流。所以大多数应用程序所打开的文件的 FD 都是从 3 开始。

 

与 FD 列相比,Type 列则比较直观。文件和目录分别为REG和DIR。而CHR和BLK,分别表示字符和块设备,或者UNIX、FIFO 和 IPv4,分别表示 UNIX 域套接字、先进先出 (FIFO) 队列和网际协议 (IP) 套接字。

 

2.lsof语法

lsof命令的语法格式为:

lsof [options] filename

lsof常用形式为:
lsof test.txt 显示开启文件test.txt的进程
  lsof -c abc 显示abc进程现在打开的文件
  lsof -cp 1234 列出进程号为1234的进程所打开的文件
  lsof -g gid 显示归属gid的进程情况
  lsof +d /usr/local/ 显示/usr/local/目录下被进程开启的文件
  lsof +D /usr/local/ 同上,但是会搜索目录下的目录(即递归搜索),时间较长
  lsof -d 4 显示fd为4的进程
  lsof -i 用以显示符合条件的进程情况
  lsof -i[46] [protocol][@hostname|hostaddr][:service|port]
    46 --> IPv4 or IPv6
    protocol --> TCP or UDP
    hostname --> Internet host name
    hostaddr --> IPv4地址
    service --> /etc/service中的 service name (可以不止一个)
    port --> 端口号 (可以不止一个)
  lsof `which httpd`        //查看哪个进程在使用apache的可执行文件
  lsof /etc/passwd          //查看哪个进程在占用/etc/passwd
  lsof /dev/hda6            //查看哪个进程在占用hda6
  lsof /dev/cdrom           //查看哪个进程在占用光驱
  lsof -c sendmail          //查看sendmail进程现在打开的文件
  lsof -c courier -u ^zahn  //显示出哪些文件被以courier打头的进程打开,但是并不属于用户zahn
  lsof -p 30297             //列出进程id为30297的进程所打开的文件
  lsof -D /tmp              //显示所有在/tmp目录下打开文件的进程
   
  lsof -u1000               //查看uid是1000的用户的进程的文件使用情况
  lsof -utony               //查看用户tony的进程的文件使用情况
  lsof -u^tony              //查看不是用户tony的进程的文件使用情况(^是取反的意思)
  lsof -i                   //显示所有已经打开的端口
  lsof -i:80                //查看80端口被哪个进程占用
  lsof -i -U                //显示所有打开的端口和UNIX domain文件
  lsof -i UDP@[url]www.akadia.com:123 //显示哪些进程打开了到www.akadia.com的UDP的123(ntp)端口的链接
  lsof -i tcp@ohaha.ks.edu.tw:ftp -r //不断查看目前ftp连接的情况(-r,lsof会永远不断的执行,直到收到中断信号,+r,lsof会一直执行,直到没有档案被显示,缺省是15s刷新)
  lsof -i tcp@ohaha.ks.edu.tw:ftp -n //lsof -n 不将IP转换为hostname,缺省是不加上-n参数

 

3.使用实例

3.1 查找谁在使用文件系统

  在卸载文件系统时,如果该文件系统中有任何已经打开的文件,操作通常将会失败。那么通过lsof可以找出哪些进程在使用当前要卸载的文件系统,如下:

  lsof /GTES11/
  COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
  bash 4208 root cwd DIR 3,1 4096 2 /GTES11/
  vim 4230 root cwd DIR 3,1 4096 2 /GTES11/

  在这个示例中,用户root正在其/GTES11目录中进行一些操作,一个bash实例正在运行,并且它当前的目录为/GTES11;另一个则显示的是vim正在编辑/GTES11下的文件。想要成功地卸载/GTES11,应该在通知用户以确保情况正常之后,中止这些进程。 这个示例说明了应用程序的当前工作目录非常重要,因为它仍保持着文件资源,并且可以防止文件系统被卸载。这就是为什么大部分守护进程(后台进程)将它们的目录更改为根目录或服务特定的目录(如 sendmail 示例中的 /var/spool/mqueue)的原因,以避免该守护进程阻止卸载不相关的文件系统。

 

posted @ 2022-06-14 20:22  一个不知道干嘛的小萌新  阅读(121)  评论(0编辑  收藏  举报