Démon UClibc et pthread_create adaptés à Linux embarqué

Aperçu

[ʻUClibc](http://www.uclibc.org/) est un C standard qui convient le mieux à un environnement Linux avec un matériel limité tel que des ressources, comme vous le savez peut-être si vous êtes engagé dans le développement Linux embarqué. C'est une bibliothèque. Dans PC Linux normal, des bibliothèques telles que glibc` sont utilisées, mais dans divers environnements, de telles bibliothèques légères sont utilisées.

En passant, je pense que le multi-thread et le multi-processus sont prudents car ils sentent dangereux lorsqu'ils sont mélangés. Cependant, si vous êtes alerte et essayez simplement de créer un démon pour une implémentation multithread, vous tomberez dans un piège.

Veuillez noter que les cas décrits ci-dessous se trouvent dans ʻuClibc` et probablement pas sur PC Linux mentionné ci-dessus. En outre, il peut s'agir uniquement d'une version plus ancienne.

phénomène

Voir le code ci-dessous:

thread_test.c


#include <stdio.h>
#include <stdlib.h>

#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>


void* do_something( void *object )
{
    size_t i;

    for ( i = 0; i < 5; ++i )
    {
        printf(
            "Hello, this is another thread! "
            "( times = %zd, tid = %p )\n",

            i,
            ( void * ) pthread_self()
        );
        sleep( 2 );
    }

    return NULL;
}


int main()
{
    size_t    i;
    pthread_t thread;

    printf( "New thread will be created. ( tid = %p )\n", ( void * ) pthread_self() );
    pthread_create( &thread, NULL, do_something, NULL );

    for ( i = 0; i < 10; ++i )
    {
        printf( "The old thread keeps going on... ( tid = %p )\n", ( void * ) pthread_self() );
        sleep( 1 );
    }

    pthread_join( thread, NULL );
    printf( "threads are joined.\n" );

    return 0;
}

Ce code est un exemple courant de multithreading, où le thread principal affiche le message 10 fois, tandis que la fonction do_something est exécutée dans un autre thread et le message est affiché 5 fois. Finalement, les deux threads se termineront par pthread_join.

Comme vous pouvez l'imaginer, la sortie ressemble à ceci:

stdout


New thread will be created. ( tid = 0x1111 )
The old thread keeps going on... ( tid = 0x1111 )          
Hello, this is another thread! ( times = 0, tid = 0x1234 )
The old thread keeps going on... ( tid = 0x1111 )
The old thread keeps going on... ( tid = 0x1111 )
Hello, this is another thread! ( times = 1, tid = 0x1234 )
The old thread keeps going on... ( tid = 0x1111 )
Hello, this is another thread! ( times = 2, tid = 0x1234 )
The old thread keeps going on... ( tid = 0x1111 )
The old thread keeps going on... ( tid = 0x1111 )
Hello, this is another thread! ( times = 3, tid = 0x1234 )
The old thread keeps going on... ( tid = 0x1111 )
The old thread keeps going on... ( tid = 0x1111 )
Hello, this is another thread! ( times = 4, tid = 0x1234 )
The old thread keeps going on... ( tid = 0x1111 )
The old thread keeps going on... ( tid = 0x1111 )
threads are joined.

(Le processus est terminé)

Maintenant, je veux exécuter ce processus en tant que démon en arrière-plan, donc c'est salé et pratique daemon () (http://linuxjm.osdn.jp/html/LDP_man-pages/man3/daemon.3. Appelez html) et demandez-leur de se désamoniser.

daemonized_thread.c


int main()
{
    size_t    i;
    pthread_t thread;

    // process should be daemonized here!
    if ( daemon( 1, 1 ) )
    {
        fprintf( stderr, "daemonization failed." );
        exit( 1 );
    }

    printf( "New thread will be created. ( tid = %p )\n", ( void * ) pthread_self() );
    pthread_create( &thread, NULL, do_something, NULL );

    for ( i = 0; i < 10; ++i )
    {
        printf( "The old thread keeps going on... ( tid = %p )\n", ( void * ) pthread_self() );
        sleep( 1 );
    }

    pthread_join( thread, NULL );
    printf( "threads are joined.\n" );

    return 0;
}

Que faire si la sortie ressemble à ceci et ne se termine jamais:

stdout


New thread will be created. ( tid = 0x1111 )
Hello, this is another thread! ( times = 0, tid = 0x1234 )
Hello, this is another thread! ( times = 1, tid = 0x1234 )
Hello, this is another thread! ( times = 2, tid = 0x1234 )
Hello, this is another thread! ( times = 3, tid = 0x1234 )
Hello, this is another thread! ( times = 4, tid = 0x1234 )
(Le processus ne se termine pas)

Apparemment, le thread principal est resté bloqué autour de pthread_create () et bloqué.

Qu'est-ce qui se passe

Une recherche sur "uClibc pthread daemon" semble être assez problématique et affichera des informations sur le forum et la liste de diffusion:

D'après les informations de la liste de diffusion ci-dessous, la cause du problème est que fork () appelé dansdaemon ()appelle le symbole interne fork () ʻau lieu de fork () ʻin libpthread. Cela semble être dû au fait que le gestionnaire de threads de pthread perd le PID du thread principal après fork () (c'est ce que c'est).

La solution à cela est d'implémenter un processus similaire daemon () vous-même, comme utiliser le fork () de pthread: https://dev.openwrt.org/browser/trunk/package/fuse/patches/300-workaround-uclibc-pthread-breakage.patch?rev=13312

Le tout avec la solution

daemonized_thread_fixed.c


#include <stdio.h>
#include <stdlib.h>

#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>


int my_daemon( int nochdir, int noclose )
{
    int res;
    int fd;

    switch ( res = fork() )
    {
        case -1:
            return 1;

        case 0:
            break;

        default:
            exit( 0 );
    }

    if ( ( res = setsid() ) == -1 )
    {
        return 1;
    }

    if ( !nochdir )
    {
        chdir( "/" );
    }

    if ( !noclose && ( fd = open( "/dev/null", O_RDWR, 0 ) ) != -1 )
    {
        dup2( fd, STDIN_FILENO );
        dup2( fd, STDOUT_FILENO );
        dup2( fd, STDERR_FILENO );

        if ( fd > 2 )
        {
            close( fd );
        }
    }

    return 0;
}


void* do_something( void *object )
{
    size_t i;

    for ( i = 0; i < 5; ++i )
    {
        printf(
            "Hello, this is another thread! "
            "( times = %zd, tid = %p )\n",

            i,
            ( void * ) pthread_self()
        );
        sleep( 2 );
    }

    return NULL;
}


int main()
{
    size_t    i;
    pthread_t thread;

    if ( my_daemon( 1, 1 ) )
    {
        fprintf( stderr, "daemonization failed." );
        exit( 1 );
    }

    printf( "New thread will be created. ( tid = %p )\n", ( void * ) pthread_self() );
    pthread_create( &thread, NULL, do_something, NULL );

    for ( i = 0; i < 10; ++i )
    {
        printf( "The old thread keeps going on... ( tid = %p )\n", ( void * ) pthread_self() );
        sleep( 1 );
    }

    pthread_join( thread, NULL );
    printf( "threads are joined.\n" );

    return 0;
}

Apparemment, cela a fonctionné. Je suis heureux.

Recommended Posts

Démon UClibc et pthread_create adaptés à Linux embarqué
Mettez Linux dans Chromebook et utilisez R ...
Des outils qui tiennent dans votre main (langage de programmation)
Une application que vous devez mettre sous Linux