linux cp产生缓存,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

#include

#include

#include

#include

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源码。

### 实现 `cp` 命令功能 为了在 Linux 下使用 C 语言实现类似于 `cp` 的命令,可以编写一段程序来复制文件的内容。下面是一个简单的例子: #### 复制单个文件的代码示例 ```c #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { if (argc != 3) { fprintf(stderr, "Usage: %s source destination\n", argv[0]); exit(EXIT_FAILURE); } const char* src = argv[1]; const char* dst = argv[2]; FILE *sourceFile = fopen(src, "rb"); if (!sourceFile) { perror("Failed to open source file"); exit(EXIT_FAILURE); } FILE *destFile = fopen(dst, "wb+"); if (!destFile) { fclose(sourceFile); // 关闭源文件指针 perror("Failed to create destination file"); exit(EXIT_FAILURE); } size_t nread; char buf[BUFSIZ]; // 使用缓冲区提高效率 while ((nread = fread(buf, 1, sizeof(buf), sourceFile)) > 0) { fwrite(buf, 1, nread, destFile); if (ferror(destFile)) { printf("Error writing to the output file.\n"); break; } if (feof(sourceFile)) break; } fclose(sourceFile); fclose(destFile); return EXIT_SUCCESS; } ``` 这段代码展示了如何打开两个文件流 (`FILE`) 并通过读取和写入操作完成数据传输[^2]。 - 首先检查参数数量是否正确。 - 打开源文件用于二进制模式下的只读(`"rb"`). - 创建目标文件并准备向其中写入数据(`"wb+"`)。 - 利用循环不断从源文件中读取数据到缓存数组 `buf[]`, 然后再将其内容写出至新创建的目标文件里直到遇到文件结束符或发生错误为止。 - 完成后记得关闭所有已打开的文件描述符以防止资源泄漏。 此方法适用于大多数情况下的简单文件拷贝需求;然而对于更复杂的场景比如支持递归复制整个目录树,则需要额外考虑路径遍历逻辑以及权限等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值