系统rm的处理机制

本文探讨了Linux系统中`rm`命令删除文件或目录的处理机制。涉及的主要系统调用包括`sys_unlink`和`sys_rmdir`,它们分别用于删除文件和目录。在删除过程中,系统会进行权限检查、安全删除以及调用文件系统的inode删除接口。对于非空目录,推测删除操作可能由shell解析并递归执行。核心源文件如`remove.h`、`remove.c`、`rm.c`和`rmdir.c`定义了相关数据结构和操作函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在linux下,常常可以执行rm -rf /home/tmp/test这样的命令删除一个目录,或是使用其他的参数删除一个文件或目录,纠结在系统内部,这些删除命令是如何处理的呢?

      这些命令其实是由系统提供的可执行程序实现的,而这些程序调用了库函数或是直接调用了系统调用函数,主要的相关的系统调用有两个,下面先介绍这些相关的系统调用函数:

  • asmlinkage long sys_unlink(const char __user *pathname);

             < do_unlinkat

                   < vfs_unlink:检查删除的条目是否是一个挂载点,如果是则返回EBUSY的错误;否则删除该inode,如果这个条目是一个私有条目,则调用安全删除机制将其删除,否则直接调用文件系统的inode删除接口删除;

                                < security_inode_unlink:安全删除,如果是一个非私有数据,则返回0;

                                   dir->i_op->unlink:调用具体文件系统的删除接口;

  

 

  • asmlinkage long sys_rmdir(const char __user *pathname);

              < do_rmdir:查找路径,然后判断删除目录的类型,对目录上锁,找到相应的dentry,调用vfs层的目录删除接口

                       < vfs_rmdir:对目录项上锁,判断删除的目录是否为挂载点,如果是挂载点则返回EBUSY错误;否则,判断删除的inode是否为私有节点,如果是的话则使用安全删除机制删除,返回直接调用文件系统的删除命令;

                                < security_inode_rmdir:

                                    dir->i_op->rmdir:

现在重点介绍块inode中的元数据操作

:

const struct inode_operations ext3_dir_inode_operations = {


.create = ext3_create,

.lookup = ext3_lookup,

.link = ext3_link,

.unlink = ext3_unlink,

.symlink = ext3_symlink,

.mkdir = ext3_mkdir,

.rmdir = ext3_rmdir,

.mknod = ext3_mknod,

.rename = ext3_rename,

.setattr = ext3_setattr,

#ifdef CONFIG_EXT3_FS_XATTR

.setxattr = generic_setxattr,

.getxattr = generic_getxattr,

.listxattr = ext3_listxattr,

.removexattr = generic_removexattr,

#endif

.permission = ext3_permission,

};



static int ext3_unlink(struct inode * dir, struct dentry *dentry)
{
	int retval;
	struct inode * inode;
	struct buffer_head * bh;
	struct ext3_dir_entry_2 * de;
	handle_t *handle;


	/* Initialize quotas before so that eventual writes go
	 * in separate transaction */
	DQUOT_INIT(dentry->d_inode);
//申请一个原子操作
	handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS(dir->i_sb));
	if (IS_ERR(handle))
		return PTR_ERR(handle);


	if (IS_DIRSYNC(dir))
		handle->h_sync = 1;


	retval = -ENOENT;
//找到inode对应的buffer head
	bh = ext3_find_entry (dentry, &de);
	if (!bh)
		goto end_unlink;


	inode = dentry->d_inode;


	retval = -EIO;
	if (le32_to_cpu(de->inode) != inode->i_ino)
		goto end_unlink;


	if (!inode->i_nlink) {
//inode中的i_nlink字段为0,表示删除的目标文件不存在
		ext3_warning (inode->i_sb, "ext3_unlink",
			      "Deleting nonexistent file (%lu), %d",
			      inode->i_ino, inode->i_nlink);
		inode->i_nlink = 1;
	}
//通过将目标目录下与前一个目录下合并,删除目标目录项
	retval = ext3_delete_entry(handle, dir, de, bh);
	if (retval)
		goto end_unlink;
	dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
	ext3_update_dx_flag(dir);
	ext3_mark_inode_dirty(handle, dir);
	drop_nlink(inode);
	if (!inode->i_nlink)
//如果inode的i_nlink为0,则认为当前节点为孤儿节点,加入到super block中的孤儿节点链表中
		ext3_orphan_add(handle, inode);
	inode->i_ctime = dir->i_ctime;
	ext3_mark_inode_dirty(handle, inode);
	retval = 0;


end_unlink:
	ext3_journal_stop(handle);
	brelse (bh);
	return retval;
}

static int ext3_rmdir (struct inode * dir, struct dentry *dentry)
{
	int retval;
	struct inode * inode;
	struct buffer_head * bh;
	struct ext3_dir_entry_2 * de;
	handle_t *handle;

	/* Initialize quotas before so that eventual writes go in
	 * separate transaction */
	DQUOT_INIT(dentry->d_inode);
	handle = ext3_journal_start(dir, EXT3_DELETE_TRANS_BLOCKS(dir->i_sb));
	if (IS_ERR(handle))
		return PTR_ERR(handle);

	retval = -ENOENT;
	bh = ext3_find_entry (dentry, &de);
	if (!bh)
		goto end_rmdir;

	if (IS_DIRSYNC(dir))
		handle->h_sync = 1;

	inode = dentry->d_inode;

	retval = -EIO;
	if (le32_to_cpu(de->inode) != inode->i_ino)
		goto end_rmdir;

	retval = -ENOTEMPTY;
	//判断删除的目录是否是空目录,如果不为空目录,则
	//返回ENOTEMPTY的错误
	if (!empty_dir (inode))
		goto end_rmdir;

	//将删除的目标dentry与磁盘上的前一个dentry合并,
	//从而删除目标dentry
	retval = ext3_delete_entry(handle, dir, de, bh);
	if (retval)
		goto end_rmdir;
	if (inode->i_nlink != 2)
		ext3_warning (inode->i_sb, "ext3_rmdir",
			      "empty directory has nlink!=2 (%d)",
			      inode->i_nlink);
	inode->i_version++;
	//将i_nlink设置为0
	clear_nlink(inode);
	/* There's no need to set i_disksize: the fact that i_nlink is
	 * zero will ensure that the right thing happens during any
	 * recovery. */
	inode->i_size = 0;
	ext3_orphan_add(handle, inode);
	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
	ext3_mark_inode_dirty(handle, inode);
	drop_nlink(dir);
	ext3_update_dx_flag(dir);
	ext3_mark_inode_dirty(handle, dir);

end_rmdir:
	ext3_journal_stop(handle);
	brelse (bh);
	return retval;
}


接下来看看coreutils中具体的程序流程:与删除相关的源文件主要有

  • remove.h
  • remove.c
  • rm.c
  • rmdir.c

1.首先看remove.h文件,该文件定义了一些与删除操作相关的数据结构:

struct rm_options:定义了rm操作的参数,即上层应用指定的rm -r -f -i等之类的选项

enum RM_status:定义了删除操作的返回状态,即删除成功、出错或是目标目录非空等

 

2.接着重点看看remove.c文件,与rm相关的操作核心函数就在这个文件中

  • 数据结构
enum Prompt_action
  {
    PA_DESCEND_INTO_DIR = 2,   //进入到目录内部
    PA_REMOVE_DIR              //删除目录
  };
struct AD_ent   //在active directory堆栈中的一个条目,每个条目对应一个active directory
{
  Hash_table *unremovable;   //相应目录中,不能删除的条目的文件名(如.和..,以及调用删除操作失败的条目)
  enum RM_status status;
  union
  {
    struct dev_ino a; //指示上级目录,当调用chdir进入子目录后,再调用chdir ..时通过该参数回到上次访问的目录下
    struct saved_cwd saved_cwd; //重构初始工作目录必须的信息,只有最底层条目才有该结构
  } u;
};
struct dirstack_state
{
  struct obstack dir_stack; //正在处理的目录的文件名,当用户进入子目录后,将在堆栈中压入一个新的条目,使用chdir时将堆栈栈顶的元素出栈
  struct obstack len_stack; //目录名长度堆栈
  struct obstack Active_dir; //active目录的栈,第一个条目是初始工作目录,当用户调用chdir时,将相应的条目压栈
  struct cycle_check_state cycle_check_state;
  jmp_buf current_arg_jumpbuf;
};
typedef struct dirstack_state Dirstack_state;




 

  • 处理函数

 

 

 

注意:ext3_rmdir只能删除一个空目录,如果目录非空,则函数将返回ENOTEMPTY的错误。无论是ext3_unlink还是ext3_rmdir函数,当目录为非空时,都不会变量该目录的子目录递归删除非空的目录,因此推测,rm -rf /hone/test这样的shell命令删除一个非空的目录,应该是由shell对命令进行解析和递归调用,传给系统的调用应该只能删除一个目录或是文件,而不能批量删除目录和文件。

参考资料:

http://www.ibm.com/developerworks/cn/linux/l-cn-usagecounter/index.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值