一个EMFILE问题定位:lsof、ulimit的应用,以及简单分析
关键词:errno、EMFILE、ulimit、lsof等等。
背景是在对程序进行压力测试,运行了一段时间之后出现一个复位操作失败。
这个复位操作通过打开一个设备,进行读写操作,已达到控制GPIO输入输出的目的。
1. 初步分析原因
经过初步分析发觉fopen()返回NULL指针,说明fopen()错误了。
但是要想知道错误原因,还需要借助errno。通过errno为24,即可知道出错的原因为EMFILE。
由errno-base.h可知,EMFILE是打开文件过多的意思。
#define EMFILE 24 /* Too many open files */
2. 找到是谁打开了哪个文件而没有释放
首先明白系统对资源使用限制的,通过ulimit可以查看限制或者修改限制。
关于文件打开文件数目的限制通过ulimit -n查看,或者ulimit -n <file null>修改限制。
通过ulimit -n命令可以查看linux系统里打开文件描述符的最大值,一般缺省值是1024。
ulimit -a -f: file size (blocks) unlimited -t: cpu time (seconds) unlimited -d: data seg size (kb) unlimited -s: stack size (kb) 8192 -c: core file size (blocks) 0 -m: resident set size (kb) unlimited -l: locked memory (kb) 64 -p: processes 3274 -n: file descriptors 1024 -v: address space (kb) unlimited -w: locks unlimited -e: scheduling priority 0 -r: real-time priority 0
lsof显示系统所有打开文件,那么很简单通过lsof即可查看到相关信息。
通过lsof可以看到打开的文件非常多,而且主要集中在uImage这个文件。
... 14232 /heop/package/AiApp/AiApp /heop/package/AiApp/uImage 14232 /heop/package/AiApp/AiApp /heop/package/AiApp/uImage 14232 /heop/package/AiApp/AiApp /heop/package/AiApp/uImage ...
然后看到uImage这个文件被打开了1000多次,问题就很明了了。
通过走查代码,发现uImage文件在被打开后由于某些条件未fclose()。解决也比较简单。
3. EMFILE出现代码走查
open系统调用入口是do_sys_open(),通过get_unused_fd_flags()来获得可用的句柄号。
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) { ... fd = get_unused_fd_flags(flags); if (fd >= 0) { ... } putname(tmp); return fd; } SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode) { if (force_o_largefile()) flags |= O_LARGEFILE; return do_sys_open(AT_FDCWD, filename, flags, mode); }
然后就看看句柄是如何分配的,以及有什么限制。
通过rlimit(RLIMIT_NOFILE)可以获得系统对打开文件数目的限制,等同于ulimit -n。
然后__alloc_fd()应该着这个范围之内。当判断fd>=end的时候,返回-EMFILE。
int get_unused_fd_flags(unsigned flags) { return __alloc_fd(current->files, 0, rlimit(RLIMIT_NOFILE), flags); } int __alloc_fd(struct files_struct *files, unsigned start, unsigned end, unsigned flags) { ... error = -EMFILE; if (fd >= end) goto out; ... out: spin_unlock(&files->file_lock); return error; }