En programación informática , un identificador es una referencia abstracta a un recurso que se utiliza cuando el software de aplicación hace referencia a bloques de memoria u objetos administrados por otro sistema, como una base de datos o un sistema operativo .
Un identificador de recurso puede ser un identificador opaco , en cuyo caso suele ser un número entero (a menudo un índice de matriz en una matriz o "tabla" que se utiliza para gestionar ese tipo de recurso), o puede ser un puntero que permite el acceso a más información. Los identificadores de recursos comunes incluyen descriptores de archivo , sockets de red , conexiones de base de datos , identificadores de proceso (PID) e identificadores de trabajo . Los PID y los identificadores de trabajo son números enteros explícitamente visibles; mientras que los descriptores de archivo y los sockets (que a menudo se implementan como una forma de descriptor de archivo) se representan como números enteros, normalmente se consideran opacos. En las implementaciones tradicionales, los descriptores de archivo son índices en una tabla de descriptores de archivo (por proceso), de ahí una tabla de archivos (para todo el sistema) .
Mientras que un puntero contiene la dirección del elemento al que hace referencia, un identificador es una abstracción de una referencia que se gestiona de forma externa; su opacidad permite que el referente sea reubicado en la memoria por el sistema sin invalidar el identificador, lo que lo hace similar a la memoria virtual para punteros, pero aún más abstracto. De manera similar, la capa adicional de indirección también aumenta el control que tiene el sistema de gestión sobre las operaciones realizadas en el referente. Normalmente, el identificador es un índice o un puntero a una matriz global de lápidas .
Una pérdida de identificador es un tipo de error de software que ocurre cuando un programa informático no libera un identificador que había asignado previamente. Se trata de una forma de pérdida de recursos , análoga a una pérdida de memoria para la memoria asignada previamente.
En términos de computación segura , debido a que el acceso a un recurso a través de un identificador está mediado por otro sistema, un identificador funciona como una capacidad : no solo identifica un objeto, sino que también asocia derechos de acceso . Por ejemplo, mientras que un nombre de archivo es falsificable (es solo un identificador que se puede adivinar), un identificador es otorgado a un usuario por un sistema externo y, por lo tanto, representa no solo la identidad, sino también el acceso otorgado .
Por ejemplo, si un programa desea leer el archivo de contraseñas del sistema ( /etc/passwd
) en modo de lectura/escritura ( O_RDWR
), podría intentar abrir el archivo mediante la siguiente llamada:
int fd = abierto ( "/etc/passwd" , O_RDWR );
Esta llamada solicita al sistema operativo que abra el archivo especificado con los derechos de acceso especificados. Si el sistema operativo lo permite, abre el archivo (crea una entrada en la tabla de descriptores de archivos por proceso ) y devuelve un identificador (descriptor de archivo, índice en esta tabla) al usuario: el acceso real lo controla el sistema operativo y el identificador es un símbolo de ello. Por el contrario, el sistema operativo puede denegar el acceso y, por lo tanto, no abrir el archivo ni devolver un identificador.
En un sistema basado en capacidades, los identificadores pueden pasarse entre procesos, con derechos de acceso asociados. Tenga en cuenta que en estos casos el identificador debe ser algo distinto de un entero pequeño único en todo el sistema, de lo contrario es falsificable. No obstante, un entero de este tipo puede utilizarse para identificar una capacidad dentro de un proceso; por ejemplo, el descriptor de archivo en Linux no se puede falsificar porque su valor numérico por sí solo no tiene sentido, y solo en el contexto del proceso puede hacer referencia a algo. Sin embargo, la transferencia de un identificador de este tipo requiere un cuidado especial, ya que su valor a menudo tiene que ser diferente en los procesos de envío y recepción.
Por otra parte, en sistemas que no se basan en capacidades, cada proceso debe adquirir su propio identificador independiente, especificando la identidad del recurso y los derechos de acceso deseados (por ejemplo, cada proceso debe abrir un archivo por sí mismo, proporcionando el nombre del archivo y el modo de acceso). Este uso es más común incluso en sistemas modernos que sí admiten el paso de identificadores, pero está sujeto a vulnerabilidades como el problema del diputado confuso .
Los identificadores fueron una solución popular para la gestión de memoria en los sistemas operativos de la década de 1990, como Mac OS [1] y Windows . La estructura de datos FILE en la biblioteca de E/S estándar de C es un identificador de archivo , que se abstrae de la representación de archivo subyacente (en Unix, estos son descriptores de archivo ). Al igual que otros entornos de escritorio , la API de Windows usa en gran medida identificadores para representar objetos en el sistema y para proporcionar una vía de comunicación entre el sistema operativo y el espacio de usuario . Por ejemplo, una ventana en el escritorio se representa mediante un identificador de tipo HWND
(identificador, ventana).
Los identificadores doblemente indirectos (en los que el identificador no es necesariamente un puntero, sino que puede ser, por ejemplo, un entero) han caído en desuso en los últimos tiempos, a medida que el aumento de la memoria disponible y los algoritmos de memoria virtual mejorados han hecho que el uso del puntero más simple sea más atractivo. Sin embargo, muchos sistemas operativos aún aplican el término a los punteros a estructuras de datos opacas y "privadas" ( punteros opacos ) o a índices en matrices internas que se pasan de un proceso a su cliente .