2010-02-01 11 views
13

En la página del manual de la llamada al sistema write(2) -¿Es un error un valor de retorno de 0 desde write (2) en C?

ssize_t write(int fd, const void *buf, size_t count);

que dice lo siguiente:

Valor de retorno

En caso de éxito, el número de bytes escrita son devueltos (cero indica no se ha escrito nada). En caso de error, -1 es devuelto, y errno se establece de manera adecuada. Si count es cero y el descriptor de archivo hace referencia a un archivo normal , se puede devolver 0 o se puede detectar un error . Para un archivo especial , los resultados no son portátiles.

Interpretaría que quiere decir que devolver 0 simplemente significa que no se escribió nada, por alguna razón arbitraria.

Sin embargo, Stevens in UNP trata a un valor de retorno de 0 como un error grave cuando se trata de un descriptor de archivo que es un socket TCP (esto es envuelto por otra función que llama a exit(1) en un corto recuento):

ssize_t /* Write "n" bytes to a descriptor. */ 
writen(int fd, const void *vptr, size_t n) 
{ 
    size_t  nleft; 
    ssize_t  nwritten; 
    const char *ptr; 

    ptr = vptr; 
    nleft = n; 
    while (nleft > 0) { 
     if ((nwritten = write(fd, ptr, nleft)) <= 0) { 
      if (nwritten < 0 && errno == EINTR) 
       nwritten = 0;  /* and call write() again */ 
      else 
       return(-1);   /* error */ 
     } 

     nleft -= nwritten; 
     ptr += nwritten; 
    } 
    return(n); 
} 

Solo trata 0 como un valor de retorno legítimo si el errno indica que la llamada a escritura fue interrumpida por el proceso que recibió una señal.

¿Por qué?

Respuesta

7

Stevens probablemente lo hace para detectar implementaciones antiguas de write() que se comportaron de manera diferente. Por ejemplo, el Single UNIX Spec dice (http://www.opengroup.org/onlinepubs/000095399/functions/write.html)

Cuando este volumen de IEEE Std 1003.1-2001 requiere -1 a ser devuelto y errno ajustado a [EAGAIN], la mayoría de las implementaciones históricos devuelven cero

+0

Eso parece indicar un estado válido que requiere un reintento similar a obtener un 'errno' de' EINTR'. Entonces, ¿por qué lo trataría como un error irrecuperable? –

+1

Buena pregunta. En mi copia de UNP (2da edición) writen() comprueba EINTR incluso si nwritten es 0 (página 78). Parece un error en la edición 1? –

+0

El código en mi pregunta es en realidad de la 3ª edición ... la respuesta de mark4o suena interesante ... –

0

Como dice su página man, el valor de retorno de 0 es "no portátil" para archivos especiales. Los sockets son archivos especiales, por lo que el resultado podría significar algo diferente para ellos.

Por lo general, para los sockets, un valor de 0 bytes de read() o write() indica que el socket se ha cerrado, y después de recibir 0, las llamadas posteriores devolverán -1 con un código de error.

+0

Escribir un socket cerrado generalmente causa un 'SIGPIPE' IIRC. Está interpretando la página man para decir que para archivos especiales, efectivamente no sabe qué significa un valor de retorno de 0, así que siempre trátelo como un error. Para mí, el "archivo especial" parece referirse solo al caso en el que pasaste una cuenta de cero, aunque podría estar equivocado. Ver la página de manual para 'enviar' también parece indicar que 0 es un valor legítimo de retorno en un socket. –

+0

Creo que la página man dice que pasar un conteo de 0 a un fd que pertenece a un archivo especial no es portátil, no es que el valor de retorno sea de alguna manera no portátil. –

1

Además, y para ser un poco pedante aquí, si no está escribiendo en un socket, me aseguraré de que la longitud del búfer ("count" en el primer ejemplo) realmente se está calculando correctamente. En el ejemplo de Stevens, ni siquiera ejecutaría la llamada a write() si la longitud del buffer fuera 0.

+0

Como menciono en la pregunta, estoy hablando específicamente de un fd que se refiere a un socket TCP. –

1

Esto asegurará que el código no gire indefinidamente, incluso si el descriptor de archivo no es un zócalo TCP o si las banderas no bloqueadas se activan. En algunos sistemas, ciertos modos legacy sin bloqueo (por ejemplo, O_NDELAY) hacen que write() devuelva 0 (sin establecer errno) si no se pueden escribir datos sin bloqueo, al menos para ciertos tipos de descriptores de archivos. (El estándar POSIX O_NONBLOCK usa un retorno de error para este caso.) Y algunos de los modos no bloqueantes en algunos sistemas se aplican al objeto subyacente (por ejemplo, socket, fifo) en lugar del descriptor de archivo, y así podría haber sido habilitado por otro proceso que tenga un descriptor de archivo abierto para el mismo objeto. El código se protege del giro en una situación así simplemente tratándolo como un error, ya que no está diseñado para usarse con modos sin bloqueo.

Cuestiones relacionadas