UClibc daemon and pthread_create that fit in embedded Linux

Overview

[ʻUClibc](http://www.uclibc.org/) is a standard C that is most suitable for Linux environments with limited hardware such as resources, as you may know if you are engaged in embedded Linux development. It is a library. Normally, PC Linux uses libraries such as glibc`, but in various environments, these lightweight libraries are used.

By the way, I think you should be cautious because multithreading and multiprocessing smell dangerous when mixed. However, if you're not careful and simply try to create a daemon for a multithreaded implementation, you'll fall into a trap.

Please note that the cases described below are found in ʻuClibc` and probably not on PC Linux mentioned above. Also, it may only be an older version.

phenomenon

See the code below:

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;
}

This code is a common example of multithreading, where the main thread displays the message 10 times, while the do_something function is executed in another thread and the message is displayed 5 times. Eventually the two threads will end with pthread_join.

As you can imagine, the output looks like this:

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.

(The process is finished)

Now that I want to run this process as a daemon in the background, it's salty and convenient daemon () (http://linuxjm.osdn.jp/html/LDP_man-pages/man3/daemon.3. Call html) and ask them to deamonize.

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;
}

Then what if the output looks like this and never ends:

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 )
(The process does not end)

Apparently the main thread got stuck around pthread_create () and got stuck.

What is happening

A search for "uClibc pthread daemon" seems to be quite problematic and will bring up forum and mailing list information:

According to the information in the mailing list below, the cause of the problem is that fork () called indaemon ()calls the internal symbolfork ()instead offork ()in libpthread. This seems to be caused by the thread manager of pthread losing the PID of the main thread afterfork ()(that's what it is).

The solution to this is to implement a similar daemon () process on your own, like using the fork () of the pthread: https://dev.openwrt.org/browser/trunk/package/fuse/patches/300-workaround-uclibc-pthread-breakage.patch?rev=13312

The whole with the 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;
}

Apparently, this worked. I'm happy.

Recommended Posts

UClibc daemon and pthread_create that fit in embedded Linux
Put Linux in your Chromebook and use R ...
Tools that fit in your hand (programming language)
An app that you must put in Linux