En cuanto a la E/S real va, el código que he escrito un millón de veces en diversas formas para copiar datos desde una secuencia a otra es algo como esto. Devuelve 0 en caso de éxito, o -1 con errno configurado en error (en cuyo caso podría haberse copiado cualquier número de bytes).
Tenga en cuenta que para copiar los archivos normales, puede omitir la materia EAGAIN, ya que los archivos regulares siempre están bloqueando E/S. Pero, inevitablemente, si escribe este código, alguien lo usará en otros tipos de descriptores de archivos, así que considérelo como regalo de promoción.
Hay una optimización específica de archivos que hace GNU cp
, que no me he molestado aquí, que para bloques largos de 0 bytes en lugar de escribir simplemente extiende el archivo de salida buscando el extremo.
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
}
Para abrir el archivo de entrada:
int fdin = open(infile, O_RDONLY|O_BINARY, 0);
if (fdin == -1) return -1;
de abrir el archivo de salida es tramposo. Como base, que quiere:
int fdout = open(outfile, O_WRONLY|O_BINARY|O_CREAT|O_TRUNC, 0x1ff);
if (fdout == -1) {
close(fdin);
return -1;
}
pero hay factores de confusión:
- lo que necesita especial de los casos cuando los archivos son los mismos, y no puedo recordar cómo hacerlo de forma portátil .
- si el nombre del archivo de salida es un directorio, es posible que desee copiar el archivo en el directorio.
- si el archivo de salida ya existe (abrir con O_EXCL para determinar esto y verificar si EEXIST está en error), es posible que desee hacer algo diferente, como lo hace
cp -i
.
- es posible que desee que los permisos del archivo de salida reflejen los del archivo de entrada.
- es posible que desee que se copien otros metadatos específicos de la plataforma.
- es posible que desee o no desee desvincular el archivo de salida por error.
Obviamente las respuestas a todas estas preguntas podrían ser "hacer lo mismo que cp
". En ese caso, la respuesta a la pregunta original es "ignorar todo lo que yo o alguien más haya dicho, y usar la fuente de cp
".
Por cierto, obtener el tamaño del clúster del sistema de archivos es casi inútil. Casi siempre verá que la velocidad aumenta con el tamaño del búfer mucho después de haber superado el tamaño de un bloque de disco.
Su muestra no puede compensar buf por cantidad ya escrita, lo que causará que las escrituras incompletas se reinicien desde la parte superior – Hasturkun
Gracias. Siempre hay un error. –
El OP solicitó una solución portátil, pero me parece que no funciona en Windows. Para empezar, 'poll()' falta, y 'ssize_t' es una extensión POSIX. No es insuperable, pero el código definitivamente no funciona como está. –