From: Simone Piccardi Date: Sat, 11 Aug 2007 19:06:11 +0000 (+0000) Subject: Materiale su splice, ed esempio non funzionante X-Git-Url: https://gapil.gnulinux.it/gitweb/?p=gapil.git;a=commitdiff_plain;h=eba75c4aaf390ff55ad6697ab80d4c1512ea2f39 Materiale su splice, ed esempio non funzionante --- diff --git a/fileadv.tex b/fileadv.tex index 5fa13de..1581fbb 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -2732,7 +2732,7 @@ mappatura che gi -\subsection{I/O vettorizzato} +\subsection{I/O vettorizzato: \func{readv} e \func{writev}} \label{sec:file_multiple_io} Un caso abbastanza comune è quello in cui ci si trova a dover eseguire una @@ -2913,8 +2913,8 @@ delle prestazioni rispetto all'uso in sequenza di \func{read} e \func{write}, che possono essere fatte da una applicazione in user space che ha una maggiore conoscenza su come questi sono strutturati.} e che anzi in certi casi si avevano dei peggioramenti, questo ha portato alla -decisione\footnote{per le motivazioni di questa scelta si può fare riferimento - a quanto illustrato da Linus Torvalds in +decisione\footnote{per alcune motivazioni di questa scelta si può fare + riferimento a quanto illustrato da Linus Torvalds in \href{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html} {\texttt{http://www.cs.helsinki.fi/linux/linux-kernel/2001-03/0200.html}}.} di consentire l'uso della funzione soltanto quando il file da cui si legge @@ -2924,20 +2924,62 @@ un errore di \errcode{EINVAL}. Nonostante i limiti illustrati resta comunque il dubbio se la scelta di disabilitare \func{sendfile} per il trasferimento di dati fra file di dati sia -davvero corretta; la funzione infatti se non altro consente di semplificare -l'interfaccia, evitando di dover gestire l'allocazione di un buffer temporaneo -per i trasferimenti dei dati in tutti quei casi in cui non c'è necessità -effettiva di fare controlli sugli stessi. Inoltre essa avrebbe comunque il -vantaggio di evitare trasferimenti di dati da e verso l'user space. +davvero corretta; la funzione infatti se non altro consentirebbe di +semplificare l'interfaccia per la copia dei dati, evitando di dover gestire +l'allocazione di un buffer temporaneo per il loro trasferimento in tutti quei +casi in cui non c'è necessità di fare controlli sugli stessi. Inoltre essa +avrebbe comunque il vantaggio di evitare trasferimenti di dati da e verso +l'user space. + +Il dubbio è stato rimosso con l'introduzione della system call +\func{splice},\footnote{avvenuto a partire dal kernel 2.6.17.} il cui scopo è +appunto quello di fornire un meccanismo generico per il trasferimento di dati +da o verso un file utilizzando un buffer intermedio gestito direttamente dal +kernel. Lo scopo della funzione può sembrare lo stesso di \func{sendfile}, ma +in realtà esse sono profondamente diverse nel loro meccanismo di +funzionamento; \func{sendfile} infatti, come accennato, non necessita affatto +(anzi nel caso di Linux viene sostanzialmente usata solo in questo caso) di +avere a disposizione un buffer interno, perché esegue un trasferimento diretto +di dati; questo la rende in generale molto più efficiente, ma anche limitata +nelle sue applicazioni. + +Il concetto che sta dietro a \func{splice} invece è diverso,\footnote{in + realtà la proposta originale di Larry Mc Voy non ne differisce poi tanto, + quello che la rende davvero diversa è stata la reinterpretazione che ne è + stata fatta nell'implementazione su Linux realizzata da Jens Anxboe, di cui + si può trovare un buon riassunto in \href{http://kerneltrap.org/node/6505} + {\texttt{http://kerneltrap.org/node/6505}}.} si tratta semplicemente di una +funzione che consente di fare delle operazioni di trasferimento dati da e +verso un buffer interamente gestito in kernel space, in maniera del tutto +generica. In questo caso il cuore della funzione (e delle affini +\func{vmsplice} e \func{tee}, che tratteremo più avanti) è appunto il buffer +in kernel space; questo è anche quello che ne ha semplificato +l'adozione,\footnote{la funzione infatti non è definita in nessuno standard, + e, allo stato attuale è disponibile soltanto su Linux.} perché +l'infrastruttura per la gestione di un buffer in kernel space è presente fin +dagli albori di Unix per la realizzazione delle \textit{pipe} (tratteremo +l'argomento in sez.~\ref{sec:ipc_unix}). Dal punto di vista concettuale allora +\func{splice} non è che un'altra interfaccia con cui esporre in userspace +l'oggetto \textsl{buffer in kernel space}. + +Così se per una \textit{pipe} o una \textit{fifo} il buffer viene utilizzato +(come illustrato in fig.~\ref{fig:ipc_pipe_singular}) come area di memoria +dove appoggiare i dati che vengono trasferiti da un capo all'altro della +stessa, creando un meccanismo di comunicazione fra processi, nel caso di +\func{splice} il buffer viene usato o come fonte dei dati che con saranno +scritti su un file, o come destinazione dei dati che vengono letti da un file. + + + + + -Fino al 2.6.17 il problema % TODO documentare le funzioni tee e splice % http://kerneltrap.org/node/6505 e http://lwn.net/Articles/178199/ e % http://lwn.net/Articles/179492/ % e http://en.wikipedia.org/wiki/Splice_(system_call) -% e http://kerneltrap.org/node/6505 diff --git a/img/pipe.dia b/img/pipe.dia index 8be4c57..801cbc6 100644 Binary files a/img/pipe.dia and b/img/pipe.dia differ diff --git a/ipc.tex b/ipc.tex index 6587147..75e8849 100644 --- a/ipc.tex +++ b/ipc.tex @@ -66,8 +66,9 @@ La funzione restituisce la coppia di file descriptor nel vettore accennato concetto di funzionamento di una pipe è semplice: quello che si scrive nel file descriptor aperto in scrittura viene ripresentato tale e quale nel file descriptor aperto in lettura. I file descriptor infatti non sono -connessi a nessun file reale, ma ad un buffer nel kernel, la cui dimensione è -specificata dal parametro di sistema \const{PIPE\_BUF}, (vedi +connessi a nessun file reale, ma, come accennato in +sez.~\ref{sec:file_sendfile_splice}, ad un buffer nel kernel, la cui +dimensione è specificata dal parametro di sistema \const{PIPE\_BUF}, (vedi sez.~\ref{sec:sys_file_limits}). Lo schema di funzionamento di una pipe è illustrato in fig.~\ref{fig:ipc_pipe_singular}, in cui sono illustrati i due capi della pipe, associati a ciascun file descriptor, con le frecce che diff --git a/sources/splicecp.c b/sources/splicecp.c new file mode 100644 index 0000000..feda8e6 --- /dev/null +++ b/sources/splicecp.c @@ -0,0 +1,150 @@ +/* splicecp.c + * + * Copyright (C) 2007 Simone Piccardi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/**************************************************************** + * + * Program splicecp.c: + * A sample program that copy a file using splice + * + * Author: Simone Piccardi + * Aug. 2007 + * + ****************************************************************/ +/* + * Include needed headers + */ +#define _GNU_SOURCE +#include /* file control functions */ +#include /* unix standard library */ +#include /* C standard library */ +#include /* error definitions and routines */ +#include /* standard I/O library */ +#include /* C strings library */ +#include +#include + +/* + * Function and globals definitions + */ +void usage(void); /* Help printing routine */ + +/* + * Main program + */ +int main(int argc, char *argv[]) +{ + /* + * Variables definition + */ + int i; + int size = 4096; + int pipefd[2]; + int in_fd, out_fd; + int nread, nwrite; + /* + * Input section: decode command line parameters + * Use getopt function + */ + opterr = 0; /* don't want writing to stderr */ + while ( (i = getopt(argc, argv, "hs:")) != -1) { + switch (i) { + /* + * Handling options + */ + case 'h': /* help option */ + printf("Wrong -h option use\n"); + usage(); + return -1; + break; + case 's': /* take wait time for childen */ + size = strtol(optarg, NULL, 10); /* convert input */ + break; + case '?': /* unrecognized options */ + printf("Unrecognized options -%c\n", optopt); + usage(); + default: /* should not reached */ + usage(); + } + } + /* + * Main body + */ + if ((argc - optind) != 2) { /* There must two argument */ + printf("Wrong number of arguments %d\n", argc - optind); + usage(); + } + /* open pipe, input and output file */ + if (pipe(pipefd) == -1) { + perror("Cannot create buffer pipe"); + exit(EXIT_FAILURE); + } + in_fd = open(argv[optind], O_RDONLY); + if (in_fd < 0) { + printf("Input error %s on %s\n", strerror(errno), argv[optind]); + exit(EXIT_FAILURE); + } + out_fd = open(argv[optind+1], O_CREAT|O_RDWR, 0644); + if (out_fd < 0) { + printf("Cannot open %s, error %s\n", argv[optind+1], strerror(errno)); + exit(EXIT_FAILURE); + } + /* copy loop */ + printf("Size %d\n", size); + while (1) { + nread = splice(in_fd, NULL, pipefd[1], NULL, size, 0); + printf("read %d bytes\n", nread); + if (nread == 0) break; + if (nread < 0) { + if (errno == EINTR) { + continue; + } else { + perror("read error"); + exit(EXIT_FAILURE); + } + } + do { + nwrite = splice(pipefd[0], NULL, out_fd, NULL, nread, 0); + printf("write %d bytes\n", nwrite); + if (nwrite == 0) continue; + if (nwrite < 0) { + if (errno == EINTR) + continue; + } else { + perror("write error"); + exit(EXIT_FAILURE); + } + nread -= nwrite; + printf("left %d bytes", nread); + } while (nread); + } + return EXIT_SUCCESS; +} + + + +/* + * routine to print usage info and exit + */ +void usage(void) { + printf("Program splicecp: copy two file using splice syscall\n"); + printf("Usage:\n"); + printf(" splicecp [-h] [-s N] filesrc filedst \n"); + printf(" -h print this help\n"); + printf(" -s N set a buffer size of N bytes \n"); + exit(1); +}