分享

linux下删除正在运行的程序文件

 补丁牛仔裤 2023-05-04 发布于广东

linux下删除正在运行的程序文件

答:不会有任何影响。

Linux是通过link的数量来控制文件删除的,只有当一个文件不存在任何link的时候,这个文件才会被删除。一般来说,每个文件都有2个link计数器:i_count 和 i_nlink。

i_count 和 i_nlink,从VFS inode结构体中可以找到:

struct inode {
struct hlist_node i_hash;
struct list_head i_list;
struct list_head i_sb_list;
struct list_head i_dentry;
unsigned long i_ino; //索引节点号
atomic_t i_count; //引用计数器
unsigned int i_nlink; //硬链接数目

i_count:当前文件使用者(或被调用)的数量
i_nlink:介质连接的数量(硬链接的数量)

当一个文件被某一个进程引用时,对应i_count数就会增加;当创建文件的硬链接的时候,对应i_nlink数就会增加。

对于删除命令rm而言,实际就是减少磁盘引用计数i_nlink。如果该文件正在被某个进程调用,比如系统正在写入的日志文件,执行了rm操作,虽然目录看不到了日志文件,但没有真正删除这个文件,i_count并不为0,你通过df统计就发现磁盘还没有释放。当只有i_nlink及i_count都为0的时候,这个文件才会真正被删除。

从上述描述可知,如果再程序中打开1一个文件的话,这个文件如果被rm,其实没有被真正的rm,因为i_count 为1,
但是如果是删除进程本身的可执行文件的话,会怎么样呢?

接下来我们要做个试验,实验前,需要知道几个命令:

  1. 查看link的方法:

ls -ihl

2.查看i_count的方法:

fuser -uv [绝对路径文件名]

查看i_count方法说白了就是确认有哪些进程调用该文件。所以通过查询文件被使用情况便可知晓。-u指在每个进程后显示所属的用户名,-v指详细模式。

试验开始:

利用的代码(每2秒打印一次 hello world):

int main()
{

	while(1)
	{
		printf("hello world\n");
		sleep(2);
	}
	return 0;
}

启动程序后,用命令查看 link与i_count的数量
1.ls -ihl

39792385 -rwxrwxr-x 1 mxg mxg 8.5K 8月 16 15:06 11
39792388 -rw-rw-r-- 1 mxg mxg 122 8月 16 15:06 1.c

可知:11这个进程(1.c编译来的),i_link数量为1(如果 ln 11 33 后,link数量就变为2了)

2.fuser -uv /home/mxg/workSpace/test/11

USER PID ACCESS COMMAND
/home/mxg/workSpace/test/11:
mxg 20493 …e. (mxg)11

可知:11这个可执行文件是被它自己打开的(当然如果再开个shell在运行11的话,此命令结果会出现两个 【(mxg)11】,即11可执行文件被两个进程(都是11)所利用),即运行时 11这个文件的i_count为1,那么接下来,看下/proc/下的文件描述符:

mxg@mxg:/proc/20493/fd$ ls
0 1 2

发现只有 0,1,2 ,好像不太对啊,其实 可执行文件比较特殊,在这里:

mxg@mxg:/proc/20493$ ls -al exe
lrwxrwxrwx 1 mxg mxg 0 8月 16 15:09 exe -> /home/mxg/workSpace/test/11

即,/proc/pid/下的exe链接到了可执行文件,接下来,当我们删除 11后(rm -rf 11),发现进程还是会好好的运行,这是为何?
我们推测是因为 i_count为1(在使用中),所以实际上没有真正的删除此文件。
紧接着,在次看一下 exe:

mxg@mxg:/proc/20493$ ls -al exe
lrwxrwxrwx 1 mxg mxg 0 8月 16 15:09 exe -> /home/mxg/workSpace/test/11 (deleted)

显示被删除了,而且用 fuser -uv /home/mxg/workSpace/test/11
也看不出来 i_count是否为0了。那怎么办呢?

我们尝试一下:

/proc/20493$ cp exe /home/mxg/workSpace/test/22

之后运行 22 ,发现22 可以正产执行了…,这其实已经验证 i_count一定不等于0的(因为被正常拷贝了),注意上面的 cp命令不能加 -rf,因为 加 -rf是拷贝连接,而cp本身默认就是拷贝实体
说明 11 的实体还在文件系统中,没有被消除。
另外,我们可以通过内核打印方式判断出 i_count确实为1.

删除文件的调用路径:

void iput(struct inode *inode)
{
	if (inode) {
		BUG_ON(inode->i_state & I_CLEAR);
		printk("---mxg--- inode->i_count=[%d]\n", inode->i_count);
		if (atomic_dec_and_lock(&inode->i_count, &inode->i_lock))
			iput_final(inode);
	}
}

static void iput_final(struct inode *inode)
{
	printk("---mxg--- iput_final\n");
	struct super_block *sb = inode->i_sb;
	const struct super_operations *op = inode->i_sb->s_op;
	int drop;

	WARN_ON(inode->i_state & I_NEW);

	if (op->drop_inode)
		drop = op->drop_inode(inode); //这里判断 i_link是否为0 (具体文件系统实现) 
										//ext4_drop_inode
	else
		drop = generic_drop_inode(inode); //这里判断 i_link是否为0

	if (!drop && (sb->s_flags & MS_ACTIVE)) {
		inode->i_state |= I_REFERENCED;
		if (!(inode->i_state & (I_DIRTY|I_SYNC)))
			inode_lru_list_add(inode);
		spin_unlock(&inode->i_lock);
		return;
	}

	if (!drop) { 
		inode->i_state |= I_WILL_FREE;
		spin_unlock(&inode->i_lock);
		write_inode_now(inode, 1);
		spin_lock(&inode->i_lock);
		WARN_ON(inode->i_state & I_NEW);
		inode->i_state &= ~I_WILL_FREE;
	}

	inode->i_state |= I_FREEING;
	if (!list_empty(&inode->i_lru))
		inode_lru_list_del(inode);
	spin_unlock(&inode->i_lock);

	evict(inode);

do_unlinkat
->iput
->iput_final //这个函数会真正的删除文件

经测试发现:
当 11在运行的时候,i_count为2,并且没有进入 iput_final 函数,即11最终没有被删除
当 11在没有运行的时候,i_count为2->之后变化为:1(至于为什么由此变化,目前还不太清楚),进入 iput_final 函数,即11最终被删除

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多