loop设备的删除

前段时间工作时,测试反馈了这样的一个问题:用-o loop选项mount 500个squashfs文件系统,然后umount -d卸载,对比执行前后的环境,发现系统内存减少了50M左右

最开始怀疑少了的内存是cache占用的,echo 3 > /proc/sys/vm/drop_caches 清cache之后,free内存并没有明显的变化

后面查看到/dev目录下有很多loopx文件,但是我们umount命令加了-d参数,按理解应该会释放loop设备才是:
man umount:
-d, --detach-loop
              When the unmounted device was a loop device, also free this loop device.
并且这些loop设备在umount之后,使用losetup -d 也删除不掉,怀疑是busybox不支持-d参数,阅读了一下busybox的losetup的源码
int losetup_main(int argc UNUSED_PARAM, char **argv)
{
……
/* -d LOOPDEV */
if (opt == OPT_d && argv[0]) {
if (del_loop(argv[0]))
bb_simple_perror_msg_and_die(argv[0]);
return EXIT_SUCCESS;
}
……

}

int FAST_FUNC del_loop(const char *device)

{

int fd, rc;

fd = open(device, O_RDONLY);

if (fd < 0)

return 1;

rc = ioctl(fd, LOOP_CLR_FD, 0);

close(fd);

return rc;

}

是通过ioctl下发LOOP_CLR_FD命令字去删除loop设备的,转到内核代码:

lo_ioctl:

case LOOP_CLR_FD:

/* loop_clr_fd would have unlocked lo_ctl_mutex on success */

err = loop_clr_fd(lo);

if (!err)

goto out_unlocked;

break;

阅读loop_clr_fd代码,都是清除struct loop_device *lo的一些成员,gendisk没有删除,甚至连lo都没有free,loop_clr_fd是起不到深处loop设备的作用的。所以不是busybox不支持-d参数,而是-d参数的实现根本就不回去删除gendisk设备和释放lo占用的内存

因为在loop_clr_fd中没有free lo,所以严重怀疑少了的内存还在被loop占用的:


/dev目录下还有个loop-control设备,查看该设备的ioctl实现:

static long loop_control_ioctl(struct file *file, unsigned int cmd, unsigned long parm)

{

struct loop_device *lo;

int ret = -ENOSYS;

mutex_lock(&loop_index_mutex);

switch (cmd) {

case LOOP_CTL_ADD:

……

case LOOP_CTL_REMOVE:

ret = loop_lookup(&lo, parm);

if (ret < 0)

break;

mutex_lock(&lo->lo_ctl_mutex);

if (lo->lo_state != Lo_unbound) {

ret = -EBUSY;

mutex_unlock(&lo->lo_ctl_mutex);

break;

}

if (lo->lo_refcnt > 0) {

ret = -EBUSY;

mutex_unlock(&lo->lo_ctl_mutex);

break;

}

lo->lo_disk->private_data = NULL;

mutex_unlock(&lo->lo_ctl_mutex);

idr_remove(&loop_index_idr, lo->lo_number);

loop_remove(lo);

break;

case LOOP_CTL_GET_FREE:

…………

}

mutex_unlock(&loop_index_mutex);

return ret;

}

LOOP_CTL_REMOVE命令字的实现调用了loop_remove,在里面删除了loop设备的gendisk和释放了lo指针


于是写了个简单的小程序:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>

#define LOOP_CTL_REMOVE0x4C81

int main()
{

int fd = 0;

int ret = 0;

int i = 0;

fd = open("/dev/loop-control", O_RDWR);

if (fd < 0) {

printf("open dev loop-control failed \n");

return 1;

}

for(i=0; i<500; i++) {

ret = ioctl(fd, LOOP_CTL_REMOVE, i)

if (ret < 0)

printf("remove loop%d failed \n", i);

}

return 0;

}
在跑完测试后的环境运行,果然/dev目录下的loop设备都被清楚了,并且内存也没有释放回来了。


附:测试用例代码
#!/bin/bash

image=/tmp/squashfs.img
mount_dir=/tmp/mount_dir
image_dir=/tmo/image_dir
mkdir -p $mount_dir $image_dir

mem_before=`cat /proc/meminfo  | grep MemFree | awk '{print $2}'`

for i in `seq 500`
do
mkdir $mount_dir/mount$i
cp $image $image_dir/test$i.img
mount -t squashfs -o loop $image_dir/test$i.img $mount_dir/mount$i
done

for j in `seq 500 0 -1`
do
umount -d /dev/loop$j
done

mem_after=`cat /proc/meminfo  | grep MemFree | awk '{print $2}'`

mem_diff=`expr mem_before - mem_after`
if [ $memdiff -gt 32000 ]; then
echo "memory leak, test failed!"
exit 1
fi

echo "test success"
exit 0


版权声明:本文为m0_37693708原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。