linux cp 命令的实现


        使用Apc中,在频繁上线源码时会机率性地出现class ‘xxx’ not found in yyy的错误,上线通常是用cp 或rsync 进行的,分析Apc代码后,怀疑是源码文件在覆盖过程中,由于st_mtime改变了被Apc取得,缓存到一个不完整的class,导致出错。因为对cp的原理不清楚,网络相关的文章也少,所以就看了下cp代码,源码可以从这获得: http://ftp.gnu.org/gnu/coreutils/。


一、源码分析

主要文件有:copy.c  cp.c cp-hash.c。

咋们跳过参数解析,直接进入文件拷贝的代码。

 
    source_desc = open (src_path, O_RDONLY);
    //...

    /* These semantics are required for cp.
       The if-block will be taken in move_mode.  */
    if (*new_dst)      
    {
        dest_desc = open (dst_path, O_WRONLY | O_CREAT, dst_mode); //如果目标文件不存在,则创建打开
    }
    else
    {
        dest_desc = open (dst_path, O_WRONLY | O_TRUNC, dst_mode); //如果目标文件存在,则截短长度为0打开,也就是清空文件

        if (dest_desc < 0 && x->unlink_dest_after_failed_open)     //如果打开文件出错,就删除文件,然后重新创建
        {
            if (unlink (dst_path))
            {
                error (0, errno, _("cannot remove %s"), quote (dst_path));
                return_val = -1;
                goto close_src_desc;
            }

            /* Tell caller that the destination file was unlinked.  */
            *new_dst = 1;

            /* Try the open again, but this time with different flags.  */
            dest_desc = open (dst_path, O_WRONLY | O_CREAT, dst_mode);
        }
    }

    //...
    for (;;)   
    {
        ssize_t n_read = read (source_desc, buf, buf_size);
        //文件拷贝,包括一些文件空洞的处理代码,预了解文件空洞是什么的同学请移步到 blog:http://mazheng.org/?p=289 。
    }

    //...

    return return_val;
}


二、实战

下面通过两个实例来对代码进行回顾和验证,通过strace 观察系统调用情况。

sample:

1、cp ,目标文件/usr/local/nginx/html/echo.php存在;

2、cp ,目标文件/usr/local/nginx/html/echo.php不存在。

sudo strace   ./cp ~/echo.php /usr/local/nginx/html/echo.php 

输出1:

lstat("/usr/local/nginx/html/echo.php", {st_mode=S_IFREG|0664, st_size=1223, ...}) = 0

stat("/usr/local/nginx/html/echo.php", {st_mode=S_IFREG|0664, st_size=1223, ...}) = 0
stat("/home/chenfei-s/echo.php", {st_mode=S_IFREG|0664, st_size=1223, ...}) = 0
stat("/usr/local/nginx/html/echo.php", {st_mode=S_IFREG|0664, st_size=1223, ...}) = 0
open("/home/chenfei-s/echo.php", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=1223, ...}) = 0
open("/usr/local/nginx/html/echo.php", O_WRONLY| O_TRUNC) = 4  若文件存在,把文件长度截断为0
fstat(4, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
fstat(3, {st_mode=S_IFREG|0664, st_size=1223, ...}) = 0
read(3, "<?php\n\necho 'xxxxxx';\necho 'xxxx"..., 4096) = 1223
write(4, "<?php\n\necho 'xxxxxx';\necho 'xxxx"..., 1223) = 1223
read(3, "", 4096)                       = 0
close(4)                                = 0
close(3)                                = 0

exit_group(0)                           = ?


输出2:

lstat("/usr/local/nginx/html/echo.php", 0x7fffd3d69b20) = -1 ENOENT (No such file or directory)
stat("/home/chenfei-s/echo.php", {st_mode=S_IFREG|0664, st_size=1223, ...}) = 0
open("/home/chenfei-s/echo.php", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=1223, ...}) = 0
open("/usr/local/nginx/html/echo.php", O_WRONLY|O_CREAT, 0100644) = 4
fstat(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
fstat(3, {st_mode=S_IFREG|0664, st_size=1223, ...}) = 0
read(3, "<?php\n\necho 'xxxxxx';\necho 'xxxx"..., 4096) = 1223
write(4, "<?php\n\necho 'xxxxxx';\necho 'xxxx"..., 1223) = 1223
read(3, "", 4096)                       = 0
close(4)                                = 0
close(3)                                = 0
exit_group(0)                           = ?


三、总结

    从上我们可以得出结论:cp 拷贝文件时,如果目标文件存在,则把文件截短为0,然后把新内容写入;如果目标文件不存在,则创建文件,然后把文件写入目标文件。


四、后记

      既然Apc是通过st_mtime来判断一个文件是不是最新的,我们引申出一个问题:open("/usr/local/nginx/html/echo.php", O_WRONLY|O_TRUNC)  这个系统调用会不会改变文件的st_mtime呢?

遂做了个测试,代码如下:

  #include <stdio.h>
  #include <sys/stat.h>
  #include <fcntl.h>
  #include <sys/types.h>
  #include <string.h>
  
  int main(int argc, char **argv)
  {
      char *file = "/home/echo.php";
      int fd;
      char buf[] = "willas test!";
  
      fd = open (file, O_WRONLY|O_TRUNC);
                                                                                                                                                                                             
      sleep(100);
      write(fd,buf,strlen(buf));
      
      return 0;
  }   

我们使用stat echo.php 来观察执行前,执行中,执行后st_mtime的变化:

执行前:

Access: 2013-08-29 17:44:18.000000000 +0800
Modify: 2013-08-29 18:50:27.000000000 +0800
Change: 2013-08-29 18:50:27.000000000 +0800

执行中:

Access: 2013-08-29 17:44:18.000000000 +0800
Modify: 2013-08-29 18:58:33.000000000 +0800
Change: 2013-08-29 18:58:33.000000000 +0800

执行后:

Access: 2013-08-29 17:44:18.000000000 +0800
Modify: 2013-08-29 19:00:13.000000000 +0800
Change: 2013-08-29 19:00:13.000000000 +0800

我们看到文件以O_TRUNC打开后st_mtime时间是会变化的,19:00:13 - 18:58:33 刚好是sleep 的100秒!这就给Apc问题了一个很好的回答,更新源码过程中可能会出现apc 取得空文件或不完整文件两种可能,最终导致出错。

但是执行完cp后st_mtime也有改变,如上,写入后st_mtime变为  19:00:13 ,Apc应该会再次取得源文件进行编译缓存,class ‘xxx’ not found in yyy 应该只会短暂地出现,那问题到底出在哪?有待进一步研究Apc源码。



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值