mv操作的机制

来源:互联网 发布:js省市区三级联动菜单 编辑:程序博客网 时间:2024/06/11 17:07
一、[腾讯面试题]
进程testMv会把log打印到testMv.log,执行下列操作,log会打印在哪里:
(1)执行mv操作
$ mv testMv.log testMv.log.bak
log打印在testMv.log.bak
(2)执行mv操作
$ mv testMv.log ../tmp/testMv.log.bak
log打印在/tmp/testMv.log.bak
(3)执行mv操作,然后再创建testMv.log文件
$ mv testMv.log testMv.log.bak
$ touch testMv.log
log打印在testMv.log.bak
(4)执行mv操作,然后再创建testMv.log文件,重新启动进程testMv
$ mv testMv.log testMv.log.bak
$ touch testMv.log
$ ./testMv
log打印在testMv.log

二、mv操作的机制
# Linux文件系统
Virtual File System(VFS)是底层文件系统的主要接口。 VFS采用了一组数据结构来描述文件系统,这些数据有超级块、inode、dentry和数据块。
index和dentry是两个针对文件系统的缓存。
Linux为每一个文件分配一个唯一的inode节点,dentry实现了文件名和inode编号的映射。 文件名存放在目录项(dentry)中,文件属性存放在inode中,文件内容存放在数据块中。
Linux查找操作文件系统中的文件时,首先读取超级块信息,找到文件名对应的inode, 然后根据inode找到磁盘中的文件,进而根据inode中的信息来完成文件的各种操作。
也就是说,Linux通过inode来寻找磁盘中的文件,而不是通过文件名来寻找。

# mv在同一个分区内执行的是rename操作,不会更改inode的信息
(1)查看磁盘分区
xl@ubuntu:~/test$ df -i
Filesystem      Inodes  IUsed  IFree IUse% Mounted on
udev            377961    482 377479    1% /dev
tmpfs           382831    709 382122    1% /run
/dev/sda1      1245184 282584 962600   23% /
tmpfs           382831      9 382822    1% /dev/shm
tmpfs           382831      6 382825    1% /run/lock
tmpfs           382831     16 382815    1% /sys/fs/cgroup
vmhgfs-fuse          0      0      0     - /mnt/hgfs
tmpfs           382831     30 382801    1% /run/user/1000
(2)查看testMv.log文件信息
xl@ubuntu:~/test$ ls -li testMv.log
153133 -rw-rw-r-- 1 xl xl     0 Mar  9 07:06 testMv.log

xl@ubuntu:~/test$ stat testMv.log
  File: 'testMv.log'
  Size: 0             Blocks: 0          IO Block: 4096   regular empty file
Device: 801h/2049d    Inode: 153133      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/      xl)   Gid: ( 1000/      xl)
Access: 2017-03-09 07:06:17.360029378 -0800
Modify: 2017-03-09 07:06:12.520029359 -0800
Change: 2017-03-09 07:06:12.520029359 -0800
 Birth: -
(3)执行mv操作
xl@ubuntu:~/test$ mv testMv.log testMv.log.bak
(4)查看testMv.log.bak文件信息
xl@ubuntu:~/test$ ls -li testMv.log.bak
153133 -rw-rw-r-- 1 xl xl     0 Mar  9 07:06 testMv.log.bak

xl@ubuntu:~/test$ stat testMv.log.bak
  File: 'testMv.log.bak'
  Size: 0             Blocks: 0          IO Block: 4096   regular empty file
Device: 801h/2049d    Inode: 153133      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1000/      xl)   Gid: ( 1000/      xl)
Access: 2017-03-09 07:06:17.360029378 -0800
Modify: 2017-03-09 07:06:12.520029359 -0800
Change: 2017-03-09 07:17:03.832032020 -0800
 Birth: -
通过执行mv操作可以看到,testMv.log和testMv.log.bak的inode值是一样的,都是153133, 说明两个文件对应的磁盘空间并没有改变,只是换了一个名字。

# 通过strace打印出mv操作的执行过程,附件mv.log是相应的log
(1)strace mv A B, 其中B原本存在
(2)strace mv A B, 其中B原本不存在
附件上传在资源中,之后会把链接更新进来

三、fopen()和fclose()操作
Linux中,进程是通过文件描述符(fd)而不是文件名来访问文件的,文件描述符实际上是一个整数。
Linux中专门用一个数据结构file来保存打开文件的文件位置,这个结构称为打开的文件描述符。file结构保存了指向该文件索引节点的指针。
fopen()在打开文件时是根据文件名,之后的fread,fwrite等操作都是根据文件描述符进行的。
fclose()函数关闭了文件的流,实际上是进行了一次写文件的操作。如果写权限不存在或者超过了文件大小,文件内容写入失败,这是fclose()函数就会出错。


四、几种操作场景和结果分析
(一)
1)进程A:fopen fileA, sleep 10
2)进程B:mv fileB fileA
3)进程A:fclose fileA
结果:执行mv操作之后,立即可以看到fileA中的文件已经变为了fileB,fclose成功。
(二)
1)进程A:fopen fileA, sleep 10
2)进程B:mv fileA fileB
3)进程A:fclose fileA
结果:执行mv操作之后,fileA文件已不存在,仅有fileB文件,且fileB文件的内容为原来fileA的内容。关闭文件fclose成功。
(三)
1)进程A:fopen fileA, 通过fwrite写入到文件的一行之后进行sleep 10
2)进程B:mv fileB fileA
3)进程A:fclose fileA
结果:执行mv操作之后,可以立即看到fileA中的内容变为了fileB,当执行完操作之后,可以看到fileA中文件为fileB,且关闭文件fileB依然是成功的。
原因:mv仅仅是rename操作,fopen和fclose都是对磁盘进行操作的。fopen写入的是磁盘位置,所以虽然mv使得文件名变了,但是磁盘位置并没有改变还是可以正常写入并关闭的。
(四)
1)进程A:fopen fileA, 通过fwrite写入到文件的一行之后进行sleep 10
2)进程B:mv fileA fileB
3)进程A:fclose fileA
结果:执行mv操作之后,可以看到文件fileA已经不存在,当fclose之后,可以看到fileB中的内容为写入的内容。

(五)
1)open fileA, 写入到文件的一行之后进行sleep
2)mv fileB fileA
3)close fileA
结果:执行完mv fileB fileA之后,文件fileA的内容立即为fileB文件的内容,而且close文件也是成功的。
(六)
1)open fileA, 写入到文件的一行之后进行sleep
2)mv fileA fileB
3)close fileA
结果:执行完mv fileB fileA之后,由于A中已经有了部分的内容,可以看到fileB中文件的内容成为fileA文件的内容,文件关闭也是成功的。

# fopen(),fwrite(),fclose()都是带缓存的,比如在执行文件读操作的时候,从磁盘文件将数据先读入内存"缓冲区",装满之后再从内存"缓冲区"依次读入接收的变量。
执行文件写操作的时候,先将数据写入到内存的"缓冲区"中,待缓冲区装满之后再写入到文件中。

# fopen()和open()等函数区别
fopen等是ANSIC标准中的C语言库函数,在不同的系统中调用不同的内核API,
open(),close(),write(),read()是标准的UNIX函数,参数不是指向文件的指针,而是指向文件的句柄,并且是不带缓冲的。
因此以上的实验将fopen换成open,fclose换成close,在实验中可以看到,在通过write写入的时候,可以看到文件的大小是递增的,说明是直接写入到磁盘空间上的。

参考文献
http://baidutech.blog.51cto.com/4114344/743731





0 0
原创粉丝点击