天道酬勤,学无止境

In C on linux how would you implement cp

问题

我在谷歌代码上发现了超过 50 行的代码,这对于我正在尝试做的事情完全没有必要。

我想用 C 做一个非常简单的cp实现。

这样我就可以使用缓冲区大小并查看它如何影响性能。

我只想使用像read()write()这样的 Linux API 调用,但我没有运气。

我想要一个定义为特定大小的缓冲区,以便可以将 file1 中的数据读入缓冲区,然后写入 file2 并继续直到 file1 达到 EOF。

这是我尝试过的,但它没有做任何事情

#include <stdio.h>
#include <sys/types.h>

#define BUFSIZE 1024

int main(int argc, char* argv[]){

    FILE fp1, fp2;
    char buf[1024];
    int pos;


    fp1 = open(argv[1], "r");
    fp2 = open(argv[2], "w");

    while((pos=read(fp1, &buf, 1024)) != 0)
    {
        write(fp2, &buf, 1024);
    }


    return 0;
}

它的工作方式是./mycopy file1.txt file2.txt

回答1

这段代码有一个重要的问题,即不管你读了多少字节,你总是写1024字节。

还:

  1. 您不检查命令行参数的数量。
  2. 您不检查源文件是否存在(如果它打开)。
  3. 您不检查目标文件是否打开(权限问题)。
  4. 您将具有不同类型的数组的地址传递给数组的第一个元素的指针。
  5. fp1的类型错误, fp2的类型也是错误的。

     #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> int main(int argc, char **argv) { char buffer[1024]; int files[2]; ssize_t count; /* Check for insufficient parameters */ if (argc < 3) return -1; files[0] = open(argv[1], O_RDONLY); if (files[0] == -1) /* Check if file opened */ return -1; files[1] = open(argv[2], O_WRONLY | O_CREAT | S_IRUSR | S_IWUSR); if (files[1] == -1) /* Check if file opened (permissions problems ...) */ { close(files[0]); return -1; } while ((count = read(files[0], buffer, sizeof(buffer))) != 0) write(files[1], buffer, count); return 0; }
回答2
回答3
#include <stdio.h>
int cpy(char *, char *);
int main(int argc, char *argv[])
{
    char *fn1 = argv[1];
    char *fn2 = argv[2];

    if (cpy(fn2, fn1) == -1) {
        perror("cpy");
        return 1;
    }
    reurn 0;
}
int cpy(char *fnDest, char *fnSrc)
{
    FILE *fpDest, *fpSrc;
    int c;

    if ((fpDest = fopen(fnDest, "w")) && (fpSrc = fopen(fnSrc, "r"))) {
        while ((c = getc(fpSrc)) != EOF)
            putc(fpDest);
        fclose(fpDest);
        fclose(fpSrc);

        return 0;
    }
    return -1;
}

首先,我们从命令行获取两个文件名( argv[1]argv[2] )。 我们不从*argv开始的原因是它包含程序名称。

然后我们调用cpy函数,它将第二个命名文件的内容复制到第一个命名文件的内容中。

cpy中,我们声明了两个文件指针:fpDest,目标文件指针和 fpSrc,源文件指针。 我们还声明c ,将被读取的字符。 它是int类型,因为EOF不适合char

如果我们可以成功打开文件(如果fopen不返回NULL ),我们从fpSrc获取字符并将它们复制到fpDest ,只要我们读取的字符不是EOF 。 一旦我们看到EOF ,我们关闭我们的文件指针,并返回 0,成功指示器。 如果我们无法打开文件,则返回-1 。 调用者可以检查返回值是否为 -1,如果是,则打印错误消息。

回答4

好问题。 与另一个好问题相关:

如何使用 C 在 Unix 上复制文件?

cp的“最简单”实现有两种方法。 一种方法使用某种文件复制系统调用函数——我们得到的最接近 Unix cp命令的 C 函数版本的东西。 另一种方法使用缓冲区和读/写系统调用函数,或者直接使用,或者使用 FILE 包装器。

可能仅在内核拥有的内存中发生的文件复制系统调用比在内核和用户拥有的内存中发生的系统调用要快,尤其是在网络文件系统设置(机器之间的复制)中。 但这需要测试(例如使用 Unix 命令time )并且将取决于编译和执行代码的硬件。

操作系统没有标准 Unix 库的人也可能想要使用您的代码。 然后你会想使用缓冲区读/写版本,因为它只取决于 <stdlib.h> 和 <stdio.h> (和朋友)。

<unistd.h>

这是一个使用 unix 标准库<unistd.h>中的函数copy_file_range将源文件复制到(可能不存在的)目标文件的示例。 复制发生在内核空间中。

/* copy.c
 *
 * Defines function copy:
 *
 * Copy source file to destination file on the same filesystem (possibly NFS).
 * If the destination file does not exist, it is created. If the destination
 * file does exist, the old data is truncated to zero and replaced by the 
 * source data. The copy takes place in the kernel space.
 *
 * Compile with:
 *
 * gcc copy.c -o copy -Wall -g
 */

#define _GNU_SOURCE 
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>

/* On versions of glibc < 2.27, need to use syscall.
 * 
 * To determine glibc version used by gcc, compute an integer representing the
 * version. The strides are chosen to allow enough space for two-digit 
 * minor version and patch level.
 *
 */
#define GCC_VERSION (__GNUC__*10000 + __GNUC_MINOR__*100 + __gnuc_patchlevel__)
#if GCC_VERSION < 22700
static loff_t copy_file_range(int in, loff_t* off_in, int out, 
  loff_t* off_out, size_t s, unsigned int flags)
{
  return syscall(__NR_copy_file_range, in, off_in, out, off_out, s,
    flags);
}
#endif

/* The copy function.
 */
int copy(const char* src, const char* dst){
  int in, out;
  struct stat stat;
  loff_t s, n;
  if(0>(in = open(src, O_RDONLY))){
    perror("open(src, ...)");
    exit(EXIT_FAILURE);
  }
  if(fstat(in, &stat)){
    perror("fstat(in, ...)");
    exit(EXIT_FAILURE);
  }
  s = stat.st_size; 
  if(0>(out = open(dst, O_CREAT|O_WRONLY|O_TRUNC, 0644))){
    perror("open(dst, ...)");
    exit(EXIT_FAILURE);
  }
  do{
    if(1>(n = copy_file_range(in, NULL, out, NULL, s, 0))){
      perror("copy_file_range(...)");
      exit(EXIT_FAILURE);
    }
    s-=n;
  }while(0<s && 0<n);
  close(in);
  close(out);
  return EXIT_SUCCESS;
}

/* Test it out.
 *
 * BASH:
 *
 * gcc copy.c -o copy -Wall -g
 * echo 'Hello, world!' > src.txt
 * ./copy src.txt dst.txt
 * [ -z "$(diff src.txt dst.txt)" ]
 *
 */

int main(int argc, char* argv[argc]){
  if(argc!=3){
    printf("Usage: %s <SOURCE> <DESTINATION>", argv[0]);
    exit(EXIT_FAILURE);
  }
  copy(argv[1], argv[2]);
  return EXIT_SUCCESS;
}

它基于我的 Ubuntu 20.x Linux 发行版的 copy_file_range 手册页中的示例。 检查您的手册页:

> man copy_file_range

然后按jEnter直到进入示例部分。 或通过键入/example进行搜索。

仅限 <stdio.h>/<stdlib.h>

这是一个仅使用stdlib/stdio的示例。 缺点是它在用户空间中使用了中间缓冲区。

/* copy.c
 *
 * Compile with:
 * 
 * gcc copy.c -o copy -Wall -g
 *
 * Defines function copy:
 *
 * Copy a source file to a destination file. If the destination file already
 * exists, this clobbers it. If the destination file does not exist, it is
 * created. 
 *
 * Uses a buffer in user-space, so may not perform as well as 
 * copy_file_range, which copies in kernel-space.
 *
 */

#include <stdlib.h>
#include <stdio.h>

#define BUF_SIZE 65536 //2^16

int copy(const char* in_path, const char* out_path){
  size_t n;
  FILE* in=NULL, * out=NULL;
  char* buf = calloc(BUF_SIZE, 1);
  if((in = fopen(in_path, "rb")) && (out = fopen(out_path, "wb")))
    while((n = fread(buf, 1, BUF_SIZE, in)) && fwrite(buf, 1, n, out));
  free(buf);
  if(in) fclose(in);
  if(out) fclose(out);
  return EXIT_SUCCESS;
}

/* Test it out.
 *
 * BASH:
 *
 * gcc copy.c -o copy -Wall -g
 * echo 'Hello, world!' > src.txt
 * ./copy src.txt dst.txt
 * [ -z "$(diff src.txt dst.txt)" ]
 *
 */
int main(int argc, char* argv[argc]){
  if(argc!=3){
    printf("Usage: %s <SOURCE> <DESTINATION>\n", argv[0]);
    exit(EXIT_FAILURE);
  }
  return copy(argv[1], argv[2]);
}

在仍然使用类 Unix C API 的同时确保总体可移植性的另一种方法是使用 GNOME(例如 GLib、GIO)进行开发

https://docs.gtk.org/glib/ https://docs.gtk.org/gio/

标签

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐