sergey
Tengo 10 procesos que intentan abrir el mismo archivo más o menos al mismo tiempo usando la llamada abierta (O_CREAT) y luego lo eliminan. ¿Hay alguna forma sólida de averiguar qué proceso realmente creó el archivo y cuál ya abrió el archivo creado, por ejemplo, si quiero contar con precisión cuántas veces se abrió ese archivo en tal escenario?
Supongo que podría poner un mutex global en la operación de apertura de archivos y hacer una secuencia de llamadas open() usando los indicadores O_CREAT y O_EXCL, pero eso no se ajusta a mi definición de “robusto”.
sospecharnos
Usar O_EXCL
bandera con O_CREAT
. Esto fallará si el archivo existe y errno se establecerá en EEXIST
. Si falla, intente abrir de nuevo sin O_CREAT
y sin O_EXCL
modos.
p.ej
int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
if ((fd == -1) && (EEXIST == errno))
{
/* open the existing file with write flag */
fd = open(path, O_WRONLY);
}
-
¿Puede aclarar “intentar abrir de nuevo sin O_CREAT”? Si omito O_CREAT de su código, me quedaré solo con O_EXCL, y de acuerdo con este “En general, el comportamiento de O_EXCL no está definido si se usa sin O_CREAT”.
– sergey
03/04/2013 a las 21:36
-
Si la creación de O_EXCL falla porque EEXISTS, inténtelo de nuevo sin el
O_EXCL
y sin elO_CREAT
! Tenga en cuenta que cuando tiene O_CREAT,open()
toma 3 argumentos; el tercero es el modo de permisos de archivos (0644 o similar). También necesita uno de O_RDONLY, O_WRONLY u O_RDWR (aunque omitirlos es equivalente a O_RDONLY, pero crear un archivo en modo de solo lectura es modestamente inútil).–Jonathan Leffler
03/04/2013 a las 21:43
-
He modificado la publicación que, con suerte, aclarará.
– sospechar
03/04/2013 a las 21:44
-
ok, pero mientras el procesador está ejecutando los comentarios justo después de la verificación si, y alguien más abre mi archivo, ¿qué hago entonces?
– sergey
03/04/2013 a las 21:45
-
@Sergey: Entonces la descripción de su problema no está completa, por lo que no podemos responderla.
– jxh
03/04/2013 a las 21:46
jxh
Basado aproximadamente en sus comentarios, desea algo similar a esta función:
/* return the fd or negative on error (check errno);
how is 1 if created, or 0 if opened */
int create_or_open (const char *path, int create_flags, int open_flags,
int *how) {
int fd;
create_flags |= (O_CREAT|O_EXCL);
open_flags &= ~(O_CREAT|O_EXCL);
for (;;) {
*how = 1;
fd = open(path, create_flags);
if (fd >= 0) break;
if (errno != EEXIST) break;
*how = 0;
fd = open(path, open_flags);
if (fd >= 0) break;
if (errno != ENOENT) break;
}
return fd;
}
Esta solución no es a prueba de balas. Puede haber casos (¿enlaces simbólicos, tal vez?) que harían que se repita para siempre. Además, puede bloquearse en vivo en ciertos escenarios de simultaneidad. Dejaré la resolución de tales problemas como un ejercicio. 🙂
En su pregunta editada, usted plantea:
Tengo 10 procesos que intentan abrir el mismo archivo más o menos al mismo tiempo usando la llamada abierta (O_CREAT) y luego lo eliminan.
Una solución hack-ish, pero más a prueba de balas, sería dar a cada proceso una identificación de usuario diferente. Entonces, solo usa el regular open(path, O_CREAT|...)
llamar. A continuación, puede consultar el archivo con fstat()
en el descriptor de archivo y verifique el st_uid
campo de la stat
estructura. Si el campo es igual al ID de usuario de los procesos, entonces fue el creador. De lo contrario, fue un abridor. Esto funciona ya que cada proceso elimina el archivo después de abrirlo.
-
Para luchar contra el bloqueo en vivo, recurriría a algún tipo de exclusión mutua compartida global después de algunas iteraciones. Pero luego la solución se convierte en algo que esperaba evitar.
– sergey
03/04/2013 a las 22:36
-
Acepto esta respuesta ya que es lo más cercano a lo que quería lograr.
– sergey
4 de abril de 2013 a las 10:49
-
En este caso, pero también en general, considera compilar tus programas con
_FORTIFY_SOURCE
establecido en 1, lo que agrega algunas comprobaciones solo en tiempo de compilación.– usuario11509478
20 de mayo de 2019 a las 10:29
Usar
(O_CREAT|O_EXCL)
para obtener un error si el archivo ya existe. Cuando obtiene el error, verifique elerrno
para ver si es porque existe, luego vuelva a abrirlo como quiera, sabiendo que ya existe.– jxh
03/04/2013 a las 21:29
¿Y luego hacer qué? Pero, ¿qué pasa si otro proceso lo abre después de mi verificación pero antes de que “reabra como yo quiera”?
– sergey
03/04/2013 a las 21:30
Su problema en su descripción no está completamente especificado entonces. Actualice su pregunta con el problema real al que se enfrenta. Muestre algo de código y señale dónde algo no está sucediendo de la manera esperada.
– jxh
03/04/2013 a las 21:37
Gracias, la parte importante la he marcado en negrita.
– sergey
03/04/2013 a las 21:38
El idioma clásico, antes de que existiera un O_CREAT, era llamar
open()
para abrir un archivo existente ycreat()
para crearlo si elopen()
ha fallado. loscreat()
Se supone que la función debe implementarse como si fueraint creat(const char *path, mode_t mode) { return open(path, O_WRONLY|O_CREAT|O_TRUNC, mode); }
así que no lo recomiendo particularmente, pero no hay otra forma de saber si creó un nuevo archivo o abrió uno existente (al hacerlo). Hay un problema de TOCTOU con la técnica abrir/crear o abrir/abrir.–Jonathan Leffler
03/04/2013 a las 21:38