Theo như I/O thực tế, mã tôi đã viết hàng triệu lần trong các bí quyết khác nhau để sao chép dữ liệu từ luồng này sang luồng khác sẽ diễn ra như thế này. Nó trả về 0 thành công, hoặc -1 với errno được đặt trên lỗi (trong trường hợp này, bất kỳ số lượng byte nào có thể đã được sao chép).
Lưu ý rằng để sao chép các tệp thông thường, bạn có thể bỏ qua nội dung EAGAIN vì các tệp thông thường luôn chặn I/O. Nhưng chắc chắn nếu bạn viết mã này, ai đó sẽ sử dụng nó trên các loại mô tả tập tin khác, vì vậy hãy xem xét nó một freebie.
Có tối ưu hóa tệp cụ thể mà GNU cp
thực hiện, mà tôi không bận tâm ở đây, đối với các khối dài 0 byte thay vì viết, bạn chỉ cần mở rộng tệp đầu ra bằng cách tìm kiếm kết thúc.
void block(int fd, int event) {
pollfd topoll;
topoll.fd = fd;
topoll.events = event;
poll(&topoll, 1, -1);
// no need to check errors - if the stream is bust then the
// next read/write will tell us
}
int copy_data_buffer(int fdin, int fdout, void *buf, size_t bufsize) {
for(;;) {
void *pos;
// read data to buffer
ssize_t bytestowrite = read(fdin, buf, bufsize);
if (bytestowrite == 0) break; // end of input
if (bytestowrite == -1) {
if (errno == EINTR) continue; // signal handled
if (errno == EAGAIN) {
block(fdin, POLLIN);
continue;
}
return -1; // error
}
// write data from buffer
pos = buf;
while (bytestowrite > 0) {
ssize_t bytes_written = write(fdout, pos, bytestowrite);
if (bytes_written == -1) {
if (errno == EINTR) continue; // signal handled
if (errno == EAGAIN) {
block(fdout, POLLOUT);
continue;
}
return -1; // error
}
bytestowrite -= bytes_written;
pos += bytes_written;
}
}
return 0; // success
}
// Default value. I think it will get close to maximum speed on most
// systems, short of using mmap etc. But porters/integrators
// might want to set it smaller, if the system is very memory
// constrained and they don't want this routine to starve
// concurrent ops of memory. And they might want to set it larger
// if I'm completely wrong and larger buffers improve performance.
// It's worth trying several MB at least once, although with huge
// allocations you have to watch for the linux
// "crash on access instead of returning 0" behaviour for failed malloc.
#ifndef FILECOPY_BUFFER_SIZE
#define FILECOPY_BUFFER_SIZE (64*1024)
#endif
int copy_data(int fdin, int fdout) {
// optional exercise for reader: take the file size as a parameter,
// and don't use a buffer any bigger than that. This prevents
// memory-hogging if FILECOPY_BUFFER_SIZE is very large and the file
// is small.
for (size_t bufsize = FILECOPY_BUFFER_SIZE; bufsize >= 256; bufsize /= 2) {
void *buffer = malloc(bufsize);
if (buffer != NULL) {
int result = copy_data_buffer(fdin, fdout, buffer, bufsize);
free(buffer);
return result;
}
}
// could use a stack buffer here instead of failing, if desired.
// 128 bytes ought to fit on any stack worth having, but again
// this could be made configurable.
return -1; // errno is ENOMEM
}
Để mở tập tin đầu vào:
int fdin = open(infile, O_RDONLY|O_BINARY, 0);
if (fdin == -1) return -1;
Mở tập tin đầu ra là tinh quái. Là một cơ sở, bạn muốn:
int fdout = open(outfile, O_WRONLY|O_BINARY|O_CREAT|O_TRUNC, 0x1ff);
if (fdout == -1) {
close(fdin);
return -1;
}
Nhưng cũng có những yếu tố gây nhiễu:
- bạn cần phải đặc biệt hợp cụ thể khi các tập tin là như nhau, và tôi không thể nhớ làm thế nào để làm điều đó portably .
- nếu tên tệp đầu ra là một thư mục, bạn có thể muốn sao chép tệp vào thư mục.
- nếu tệp đầu ra đã tồn tại (mở với O_EXCL để xác định điều này và kiểm tra EEXIST có lỗi), bạn có thể muốn làm điều gì đó khác, như
cp -i
.
- bạn có thể muốn các quyền của tệp đầu ra phản ánh các quyền của tệp đầu vào.
- bạn có thể muốn siêu dữ liệu nền tảng cụ thể khác được sao chép.
- bạn có thể hoặc không muốn hủy liên kết tệp đầu ra do lỗi.
Rõ ràng là câu trả lời cho tất cả các câu hỏi này có thể là "làm tương tự như cp
". Trong trường hợp đó, câu trả lời cho câu hỏi ban đầu là "bỏ qua mọi thứ tôi hoặc bất kỳ ai khác đã nói và sử dụng nguồn của cp
".
Btw, nhận kích thước cụm của hệ thống tệp bên cạnh vô dụng. Bạn sẽ hầu như luôn thấy tốc độ tăng lên với kích thước bộ đệm dài sau khi bạn đã vượt qua kích thước của một khối đĩa.
Mẫu của bạn không thể bù đắp buf theo số tiền đã ghi, điều này sẽ gây ra ghi không đầy đủ để khởi động lại từ đầu – Hasturkun
Cảm ơn bạn. Luôn có một lỗi. –
OP yêu cầu giải pháp di động, nhưng tôi thấy nó không hoạt động trên Windows. Để bắt đầu 'poll()' bị thiếu, và 'ssize_t' là một phần mở rộng POSIX. Không thể vượt qua, nhưng mã chắc chắn không hoạt động như vậy. –