2011-09-11 9 views
35

¿Cuál es la lógica detrás de llamadas como getpid() que devuelve un valor de tipo pid_t en lugar de unsigned int? O int? ¿Cómo ayuda esto?pid_t (y tipos similares): ¿por qué? ¿Por qué?

Supongo que esto tiene que ver con la portabilidad? ¿Garantizar que pid_t tiene el mismo tamaño en diferentes plataformas que pueden tener diferentes tamaños de int s, etc.?

Respuesta

32

Creo que es todo lo contrario: hacer que el programa sea portátil en todas las plataformas, independientemente de si, por ejemplo, un PID tiene 16 o 32 bits (o incluso más).

+1

Enteros enteros más allá de xiint8_t xint16_t xint32_t xint64_t no resuelve ningún problema: hay conversiones de enteros automáticos en su lugar. Por lo tanto, todos y cada uno de los programas del mundo real de edad razonable (digamos 10 años) y tamaño (digamos 1MLOC) contienen miles de suposiciones silenciosas e indetectables sobre tamaños enteros. – zzz777

+0

@ zzz777 Oh Dios, tan cierto ... – Ivan

9

En diferentes plataformas y sistemas operativos, diferentes tipos (pid_t por ejemplo) pueden ser 32 bits (unsigned int) en una máquina de 32 bits o 64 bits (unsigned long) en una máquina de 64 bits. O, por alguna otra razón, un sistema operativo puede elegir tener un tamaño diferente. Además, al leer el código, deja claro que esta variable representa un "objeto", en lugar de solo un número arbitrario.

26

El motivo es permitir que las desagradables implementaciones históricas sigan siendo conformes. Suponga que su aplicación histórica tenía (bastante común):

short getpid(void); 

Por supuesto, los sistemas modernos quieren PID para ser al menos de 32 bits, pero si la norma de mandato:

int getpid(void); 

entonces todas las implementaciones históricos que tuvieron usado short se volvería no conforme. Esto se consideró inaceptable, por lo que se creó pid_t y se permitió que la implementación definiera pid_t de la manera que prefiera.

Tenga en cuenta que de ninguna manera está obligado a utilizar pid_t en su propio código, siempre que utilice un tipo que sea lo suficientemente grande como para almacenar cualquier pid (intmax_t, por ejemplo, funcionaría bien). La única razón por la que pid_tnecesita existe es para que el estándar defina getpid, waitpid, etc. en términos de ello.

+0

Ah, estándares. Lo tengo. ¿Pero qué pasa con el estilo/las mejores prácticas? ¿Es mejor usar 'pid_t' que' 'int' para contener un pid? Readability-wise (alusión a la respuesta @Maz)? ¿O es algo tan trivial y sin sentido que no merece este tipo de atención? – ntl0ve

+0

Si pudiera duplicar el voto ... pero la respuesta de zvrbas es una especie de versión comprimida de esta respuesta, así que también votaré zvrba. –

+0

Usar 'int' probablemente no sea una buena idea ya que no admitiría implementaciones futuras hipotéticas con pids de 64 bits. Solo usaría 'pid_t' para la mayoría de los usos, pero tenga en cuenta que si tiene una estructura de datos que puede almacenar" enteros genéricos "con un tipo grande (es decir,' intmax_t'), sería aceptable almacenar los pids de esa manera. –

6

El objetivo es hacer pid_t, o cualquier otro tipo de ordenación, independiente de la plataforma, de manera que funcione correctamente independientemente de cómo se implemente realmente. Esta práctica se utiliza para cualquier tipo que tiene que ser independiente de la plataforma, tales como:

  • pid_t: Tiene que ser lo suficientemente grande como para guardar un PID en el sistema de codificación para que estés. Mapas a int hasta donde yo sé, aunque no estoy muy familiarizado con la biblioteca C de GNU.
  • size_t: Una variable unsigned capaz de almacenar el resultado del operador sizeof. Generalmente es igual en tamaño al tamaño de la palabra del sistema que está codificando.
  • int16_t (intX_t): tiene que ser exactamente de 16 bits, independientemente de la plataforma, y ​​no se definirá en plataformas que no soportan bytes de 8 bits (por ejemplo, un sistema de 36 bits). Generalmente se asigna a short en las computadoras modernas, aunque puede ser int en las más antiguas.
  • int_least32_t (int_leastX_t): Tiene que ser el tamaño más pequeño posible que puede almacenar al menos 32 bits, como 36 bits en un sistema de 36 bits o de 72 bits.Generalmente se asigna a int en computadoras modernas, aunque puede ser long en las más antiguas.
  • int_fastX_t: Tiene que ser el tipo más rápido posible que puede almacenar al menos X bits. Generalmente, es del sistema tamaño de palabra si (X <= word_size) (o, a veces char para int_fast8_t), o actúa como int_leastX_t si (X > word_size))
  • intmax_t: tiene que ser el ancho máximo número entero soportado por el sistema. En general, será de al menos 64 bits en sistemas modernos, aunque algunos sistemas pueden admitir tipos extendidos mayores que long long (y si es así, se requiere que intmax_t sea el más grande de esos tipos).
  • Y más ...

Mecánicamente, permite el instalador del compilador para typedef el tipo apropiado al identificador (ya sea un tipo estándar o un tipo interno llamado torpemente-) detrás de las escenas, ya sea mediante la creación apropiada archivos de encabezado, codificándolo en el ejecutable del compilador, o algún otro método. Por ejemplo, en un sistema de 32 bits, Microsoft Visual Studio implementará las intX_t y tipos similares de la siguiente manera (Nota: Los comentarios añadidos por mí):

// Signed ints of exactly X bits. 
typedef signed char int8_t; 
typedef short int16_t; 
typedef int int32_t; 

// Unsigned ints of exactly X bits. 
typedef unsigned char uint8_t; 
typedef unsigned short uint16_t; 
typedef unsigned int uint32_t; 

// Signed ints of at least X bits. 
typedef signed char int_least8_t; 
typedef short int_least16_t; 
typedef int int_least32_t; 

// Unsigned ints of at least X bits. 
typedef unsigned char uint_least8_t; 
typedef unsigned short uint_least16_t; 
typedef unsigned int uint_least32_t; 

// Speed-optimised signed ints of at least X bits. 
// Note that int_fast16_t and int_fast32_t are both 32 bits, as a 32-bit processor will generally operate on a full word faster than a half-word. 
typedef char int_fast8_t; 
typedef int int_fast16_t; 
typedef int int_fast32_t; 

// Speed-optimised unsigned ints of at least X bits. 
typedef unsigned char uint_fast8_t; 
typedef unsigned int uint_fast16_t; 
typedef unsigned int uint_fast32_t; 

typedef _Longlong int64_t; 
typedef _ULonglong uint64_t; 

typedef _Longlong int_least64_t; 
typedef _ULonglong uint_least64_t; 

typedef _Longlong int_fast64_t; 
typedef _ULonglong uint_fast64_t; 

En un sistema de 64 bits, sin embargo, no pueden necesariamente se implementará de la misma manera, y puedo garantizar que no se implementarán de la misma manera en un sistema arcaico de 16 bits, suponiendo que se puede encontrar una versión de MSVS compatible con uno.

En general, permite que el código funcione correctamente independientemente de las especificaciones de su implementación y cumple los mismos requisitos en cualquier sistema compatible con estándares (por ejemplo, pid_t se puede garantizar que sea lo suficientemente grande para contener cualquier PID válido en el sistema en cuestión, no importa qué sistema estés codificando). También le impide tener que conocer los detalles y tener que buscar nombres internos con los que no esté familiarizado. En resumen, se asegura de que su código funcione igual independientemente de si pid_t (o cualquier otro typedef similar) se implementa como int, short, long, long long, o incluso __Did_you_really_just_dare_me_to_eat_my_left_shoe__, por lo que no es necesario.


Además, sirve como una forma de documentación, que le permite saber qué es una variable dada de un vistazo. Considere lo siguiente:

int a, b; 

.... 

if (a > b) { 
    // Nothing wrong here, right? They're both ints. 
} 

Ahora, vamos a intentarlo de nuevo:

size_t a; 
pid_t b; 

... 

if (a > b) { 
    // Why are we comparing sizes to PIDs? We probably messed up somewhere. 
} 

Si se utiliza como tal, puede ayudar a localizar los segmentos potencialmente problemáticas de código antes de los descansos nada, y puede hacer que la solución de problemas mucho más fácil de lo que sería de otra manera.

2

Cada proceso en el programa tiene un ID de proceso específico. Al llamar a pid, conocemos la identificación asignada del proceso actual. Conocer el pid es excepcionalmente importante cuando usamos fork(), porque devuelve 0 y !=0 valores para las copias secundarias y secundarias de manera receptiva.Estos dos vídeos tienen explicaciones claras: video#1Video#2

Un ejemplo: Supongamos que tenemos el siguiente programa c:

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <unistd.h> 


int main (int argc, char *argv[]) 
{ 

    printf("I am %d\n", (int) getpid()); 
    pid_t pid = fork(); 
    printf("fork returned: %d\n", (int) pid); 
    if(pid<0){ 
    perror("fork failed"); 
    } 
    if (pid==0){ 
    printf("This is a child with pid %d\n",(int) getpid()); 
    }else if(pid >0){ 
    printf("This is a parent with pid %d\n",(int)getpid()); 
    } 

    return 0; 
} 

Si lo ejecuta, obtendrá 0 para el niño y no zero/greater than zero para el padre.

0

Una cosa a destacar, en la mayoría de las respuestas que vi algo en la línea de "usando pid_t hace que el trabajo de código en diferentes sistemas", lo cual no es necesariamente cierto.

Creo que la redacción exacta debe ser: hace que el código 'compile' en diferentes sistemas.

Como, por ejemplo, compilando el código en un sistema que utiliza pid_t 32 bits producirá un binario que probablemente ruptura si se ejecuta en otro sistema que utiliza pid_t de 64 bits.

Cuestiones relacionadas