Exemple de reproduction POSIX minimale exécutable en C
Je recommande de comprendre l'API sous-jacente pour mieux comprendre ce qui se passe.
sleep.c
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
sleep(10000);
}
occupé.c
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(void) {
int ret = open("sleep.out", O_WRONLY|O_TRUNC);
assert(errno == ETXTBSY);
perror("");
assert(ret == -1);
}
Compilez et exécutez :
gcc -std=c99 -o sleep.out ./sleep.c
gcc -std=c99 -o busy.out ./busy.c
./sleep.out &
./busy.out
busy.out
passe les assertions, et perror
sorties :
Text file busy
Nous en déduisons donc que le message est codé en dur dans la glibc elle-même.
Alternativement :
echo asdf > sleep.out
fait une sortie Bash :
-bash: sleep.out: Text file busy
Pour une application plus complexe, vous pouvez également l'observer avec strace
:
strace ./busy.out
qui contient :
openat(AT_FDCWD, "sleep.out", O_WRONLY) = -1 ETXTBSY (Text file busy)
Testé sur Ubuntu 18.04, noyau Linux 4.15.0.
L'erreur ne se produit pas si vous unlink
premièrement
notbusy.c
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void) {
assert(unlink("sleep.out") == 0);
assert(open("sleep.out", O_WRONLY|O_CREAT) != -1);
}
Puis compilez et exécutez de manière analogue à ce qui précède, et ces assertions passent.
Cela explique pourquoi cela fonctionne pour certains programmes mais pas pour d'autres. Par exemple, si vous le faites :
gcc -std=c99 -o sleep.out ./sleep.c
./sleep.out &
gcc -std=c99 -o sleep.out ./sleep.c
qui ne génère pas d'erreur, même si la seconde gcc
L'appel écrit à sleep.out
.
Un rapide strace
montre que GCC commence par délier les liens avant d'écrire :
strace -f gcc -std=c99 -o sleep.out ./sleep.c |& grep sleep.out
contient :
[pid 3992] unlink("sleep.out") = 0
[pid 3992] openat(AT_FDCWD, "sleep.out", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
La raison pour laquelle il n'échoue pas est que lorsque vous unlink
et réécrire le fichier, il crée un nouvel inode, et garde un inode temporaire pour le fichier exécutable en cours d'exécution.
Mais si tu veux juste write
sans unlink
puis il essaie d'écrire sur le même inode protégé que l'exécutable en cours d'exécution.
POSIX 7 open()
http://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html
[ETXTBSY]
Le fichier est un fichier de procédure pure (texte partagé) qui est en cours d'exécution et oflag est O_WRONLY ou O_RDWR.
homme 2 ouvert
ETXTBSY
Le nom du chemin fait référence à une image exécutable qui est en cours d'exécution et pour laquelle un accès en écriture a été demandé.
source glibc
Un rapide grep sur 2.30 donne :
sysdeps/gnu/errlist.c:299: [ERR_REMAP (ETXTBSY)] = N_("Text file busy"),
sysdeps/mach/hurd/bits/errno.h:62: ETXTBSY = 0x4000001a, /* Text file busy */
et une frappe manuelle dans manual/errno.texi
:
@deftypevr Macro int ETXTBSY
@standards{BSD, errno.h}
@errno{ETXTBSY, 26, Text file busy}
An attempt to execute a file that is currently open for writing, or
write to a file that is currently being executed. Often using a
debugger to run a program is considered having it open for writing and
will cause this error. (The name stands for ``text file busy''.) This
is not an error on @gnuhurdsystems{}; the text is copied as necessary.
@end deftypevr