cstdio中的文件操作函数
stdio.h中定义了文件删除函数remove,文件重命名函数rename,打开临时文件函数tmpfile,生成临时文件名函数tmpnam。接下来我们一起来分析一下rename对应的源码实现。
文件重命名函数rename
使用新的文件名替换旧的文件名
在glibc/libio/stdio.h中与rename相关的函数增加了两个renameat(替换时增加了FD的关联),renameat2(替换时增加FD的关联,flags信息,表示三种不同的模式:RENAME_NOREPLACE,RENAME_EXCHANGE,RENAME_WHITEOUT)
实现方式---unix方式
代码参考:/glibc/sysdeps/unix/sysv/linux/rename.c
实际上就是通过调用INLINE_SYSCALL_CALL实现的
INLINE_SYSCALL_CALL的实现,继续调用__INLINE_SYSCALL_DISP,注意这里实际上是将__INLINE_SYSCALL和后面的数据做了连接操作("##"在宏中做字符串连接)
这里有必要分析一下__SYSCALL_CONCAT (b,__INLINE_SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__)
中__INLINE_SYSCALL_NARGS(__VA_ARGS__)
的作用,
可以看到,它在传入参数的后面添加了7-0共8个参数传递给__INTERNAL_SYSCALL_NARGS_X,该宏按顺序筛选出第9个数n,即如果__VA_ARGS__只有一个参数,n=0,两个参数n=1,
所以INLINE_SYSCALL_CALL (rename, old, new)
三个参数,n=2,被宏展开为__INLINE_SYSCALL2(rename,old,new)
,再按照宏展开为INTERNAL_SYSCALL (rename, 2, old, new)
对应架构的实现如下: glibc/sysdeps/unix/sysv/linux/x86_64/sysdep.h
其中number为连接出的_NR_rename,这也说明了最开始判断#if defined (__NR_rename)
的原因,
arg1,arg2分别为输入的old,new,调用汇编进行执行。
renameat和renameat2的调用与之类似,这里就不做过多说明了,需要注意的是renameat调用中使用的flags是0(默认值)。
实现方式---mach内核架构实现
glibc/sysdeps/mach/hurd/rename.c,实际上是调用__dir_rename实现的,不做更深入解读了
实现方式---posix实现
glibc/sysdeps/posix/rename.c
逻辑也比较好理解,使用__link完成对应的rename操作,然后使用__unlink断开oldname,大部分代码是针对出现异常时,即返回值小于0的异常处理,如前一处条件中考虑到了EEXIST(文件存在)的情况,那就需要删除新文件,重新尝试__link,删除old文件时也是,出现异常,同时也要将新文件删除,保证最后失败不会带来额外的影响。具体__link和__unlink的调用基本也都是基于syscall实现的,就不展开细说了。