Что касается фактического ввода-вывода, то код, который я написал миллион раз в разных ипостасях для копирования данных из одного потока в другой, выглядит примерно так. В случае успеха возвращается 0 или -1 с ошибкой, установленной в случае ошибки (в этом случае может быть скопировано любое количество байтов).
Обратите внимание, что для копирования обычных файлов вы можете пропустить EAGAIN, поскольку обычные файлы всегда блокируют ввод / вывод. Но неизбежно, если вы напишите этот код, кто-то будет использовать его для файловых дескрипторов других типов, так что считайте его халявой.
Существует оптимизация для конкретного файла, которую выполняет GNU cp
, которую я здесь не беспокоил, что для длинных блоков по 0 байтов вместо записи вы просто расширяете выходной файл, ища конец.
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
}
Чтобы открыть входной файл:
int fdin = open(infile, O_RDONLY|O_BINARY, 0);
if (fdin == -1) return -1;
Открыть выходной файл довольно сложно. В качестве основы вы хотите:
int fdout = open(outfile, O_WRONLY|O_BINARY|O_CREAT|O_TRUNC, 0x1ff);
if (fdout == -1) {
close(fdin);
return -1;
}
Но есть смешивающие факторы:
- вам нужен специальный случай, когда файлы одинаковы, и я не могу вспомнить, как это сделать переносимо.
- если выходное имя файла является каталогом, вы можете скопировать файл в каталог.
- если выходной файл уже существует (откройте его с помощью O_EXCL, чтобы определить это и проверьте EEXIST на наличие ошибок), вы можете захотеть сделать что-то другое, как
cp -i
.
- вам может потребоваться, чтобы разрешения выходного файла отражали разрешения входного файла.
- возможно, вы захотите скопировать другие метаданные для конкретной платформы.
- вы можете, а можете и не захотеть отсоединять выходной файл при ошибке.
Очевидно, что ответы на все эти вопросы могут быть "сделать так же, как cp
". В этом случае ответ на первоначальный вопрос звучит так: «игнорируйте все, что я или кто-либо еще сказал, и используйте источник cp
».
Кстати, получение размера кластера файловой системы практически бесполезно. Вы почти всегда увидите увеличение скорости при увеличении размера буфера после того, как пройдете размер блока диска.