您的位置:首页 > 运维架构 > Linux

linux cp 命令的实现

2013-09-13 11:19 375 查看
使用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源码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: