Vous avez une condition de concurrence (c'est-à-dire) vous n'avez pas la sécurité de thread implicite de stdio.
Le problème est encore plus grave. Vous pouvez obtenir des messages "func" en double. Le problème est que l'utilisation clone
n'a pas les mêmes garanties que pthread_create
. (c'est-à-dire) Vous faites pas obtenir les variantes de thread sécurisé de printf
.
Je ne sais pas avec certitude, mais, IMO le verbiage sur les flux stdio et la sécurité des threads, en pratique, ne s'applique que lorsque vous utilisez pthreads
. Par conséquent, vous devrez gérer votre propre verrouillage inter-thread. Voici une version de votre programme recodée pour utiliser pthread_create
. Il semble fonctionner sans incident:
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#define STACK_SIZE 1024*1024
void *func(void* param) {
printf("I am func, pid %d\n", getpid());
return (void *) 0;
}
int main(int argc, char const *argv[]) {
printf("I am main, pid %d\n", getpid());
void* ptr = malloc(STACK_SIZE);
printf("I am calling clone\n");
pthread_t tid;
pthread_create(&tid,NULL,func,NULL);
//int res = clone(func, ptr + STACK_SIZE, CLONE_VM, NULL);
int res = 0;
// works fine with sleep() call
// sleep(1);
if (res == -1) {
printf("clone error: %d", errno);
} else {
printf("I created child with pid: %d\n", res);
}
pthread_join(tid,NULL);
printf("Main done, pid %d\n", getpid());
return 0;
}
Voici un script de test, je l'ai utilisé pour vérifier les erreurs [il est un peu rude, mais devrait être correct]. Courir contre votre version et il va avorter rapidement. La version pthread_create
semble passer très bien
#!/usr/bin/perl
# clonetest -- clone test
#
# arguments:
# "-p0" -- suppress check for duplicate parent messages
# "-c0" -- suppress check for duplicate child messages
# 1 -- base name for program to test (e.g. for xyz.c, use xyz)
# 2 -- [optional] number of test iterations (DEFAULT: 100000)
master(@ARGV);
exit(0);
# master -- master control
sub master
{
my(@argv) = @_;
my($arg,$sym);
while (1) {
$arg = $argv[0];
last unless (defined($arg));
last unless ($arg =~ s/^-(.)//);
$sym = $1;
shift(@argv);
$arg = 1
if ($arg eq "");
$arg += 0;
${"opt_$sym"} = $arg;
}
$opt_p //= 1;
$opt_c //= 1;
printf("clonetest: p=%d c=%d\n",$opt_p,$opt_c);
$xfile = shift(@argv);
$xfile //= "clone1";
printf("clonetest: xfile='%s'\n",$xfile);
$itermax = shift(@argv);
$itermax //= 100000;
$itermax += 0;
printf("clonetest: itermax=%d\n",$itermax);
system("cc -o $xfile -O2 $xfile.c -lpthread");
$code = $? >> 8;
die("master: compile error\n")
if ($code);
$logf = "/tmp/log";
for ($iter = 1; $iter <= $itermax; ++$iter) {
printf("iter: %d\n",$iter)
if ($opt_v);
dotest($iter);
}
}
# dotest -- perform single test
sub dotest
{
my($iter) = @_;
my($parcnt,$cldcnt);
my($xfsrc,$bf);
system("./$xfile > $logf");
open($xfsrc,"<$logf") or
die("dotest: unable to open '$logf' -- $!\n");
while ($bf = <$xfsrc>) {
chomp($bf);
if ($opt_p) {
while ($bf =~ /created/g) {
++$parcnt;
}
}
if ($opt_c) {
while ($bf =~ /func/g) {
++$cldcnt;
}
}
}
close($xfsrc);
if (($parcnt > 1) or ($cldcnt > 1)) {
printf("dotest: fail on %d -- parcnt=%d cldcnt=%d\n",
$iter,$parcnt,$cldcnt);
system("cat $logf");
exit(1);
}
}
MISE À JOUR:
Avez-vous pu recréer problème de clone avec OPs?
Absolument.Avant que j'ai créé la version pthreads, en plus de tester la version originale de l'OP, je versions aussi créé que:
(1) ajouté setlinebuf
au début de main
(2) ajouté fflush
juste avant le clone
et __fpurge
comme la première déclaration de func
(3) a ajouté un fflush
dans func
avant la return 0
version (2) a éliminé le double messages parent, mais les messages enfants en double sont restés
Si vous souhaitez voir cela par vous-même, téléchargez la version OP à partir de la question, de ma version et du script de test. Ensuite, exécutez le script de test sur la version OP. J'ai posté suffisamment d'informations et de fichiers pour que tout le monde puisse recréer le problème.
Notez qu'en raison des différences entre mon système et les OP, je n'ai pas pu reproduire le problème en seulement 3-4 essais. Donc, c'est pourquoi j'ai créé le script.
Le script effectue 100 000 tests et le problème se manifeste généralement entre 5 000 et 15 000.
Notez que sur un système rare où 'int/unsigned' est de 16 bits, mais' size_t' est de 32 bits: '#define STACK_SIZE 1024 * 1024 * ptr vide = malloc (STACK_SIZE); 'est un problème. À chaque fois que les maths sont faites dans une "constante", faites attention au débordement. Suggérer '#define STACK_SIZE ((size_t) 1024 * 1024)' – chux
Puisqu'il est apparemment difficile de reproduire ce problème, peut-être auriez-vous l'amabilité de nous en dire plus sur votre architecture. Par exemple. Système d'exploitation et version; version glibc; version gcc. (Aussi, tous les indicateurs de compilateur non-par défaut que vous utilisez) – rici
Ubuntu 16.04, GLIBC 2.23-0ubuntu3, gcc 5.4.0. CPU i7-5600U. Aucun indicateur de compilation supplémentaire. – lstipakov