hvn-network

My Blog List

Thursday, September 15, 2016

Thread in Linux

created by Frank

What is a thread?

Like processes, threads are a mechanism that permits an application to perform multiple tasks concurrently. 

threads in process

thread stack in virtual memory address

thread life

Creating a thread

using the following functions to create a thread:
#include <pthread.h>
/*create a thread*/
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 
               void *(*start_routine) (void *), void *arg);

/*wait a thread finishing and get return value*/
int pthread_join(pthread_t *thread, void **retval);

/*thread returns value*/
int pthread_exit(void *retval);
example

#include <stdio.h>
#include <pthread.h>
static void *threadFunc(void *arg)
{
    char *s = (char *) arg;
    printf("%s", s);

    pthread_exit("thread finishes");
}

int main(int argc, char *argv[])
{
    pthread_t t1;
    void *res;
    int s;

    s = pthread_create(&t1, NULL, threadFunc, "Hello world\n");
    if (s != 0)
        printf("pthread_create");

    printf("Message from main()\n");
    s = pthread_join(t1, &res);
    if (s != 0)
        printf("pthread_join");

    printf("Thread returned %s\n", (char *) res);

    exit(EXIT_SUCCESS);
}
note: compile and link with -lpthread

some data type defined in pthread.h header file
data types in pthread.h
fork() in a thread

  • To the Linux kernel, there is no concept of a thread
  • Linux implements all threads as standard processes
  • Child process from the parent is that the child has only one thread, the fork() call clones just the thread which executed it.

Thread attributes

with attributes, we have many options to control threads
the thread attributes object

pthread_attr_t attr;
defination of pthread_attr_t
typedef struct {
    int __detachstate;
    int __schedpolicy;
    struct sched_param __schedparam;
    int __inheritsched;
    int __scope;
    size_t __guardsize;
    int __stackaddr_set;
    void *__stackaddr;
    unsigned long int __stacksize;
} pthread_attr_t;

firstly, we must initialize the attr
pthread_attr_init(pthread_attr_t *attr);

and, we must destroy the attr after using
pthread_attr_destroy(pthread_attr_t *attr);

Detachedstate

sometimes we don't want to a thread join to main thread, so we use detachedstate attribute.
There are 2 methods to detach a thread. After detaching, the thread can not join to main thread
Method 1; use pthread_detach(pthread_t thread);  

  • call in main thread: pthread_detach(thread);
  • call in the detach thread: pthread_detach(thread_self());

if we use this system call, after that calling thread_join will return error
example
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

void *thread_function(void *arg);
char message[] = "Hello World";
int thread_finished = 0;
void *thread_result;

int main() {
 int res, s;
 pthread_t a_thread;
 pthread_attr_t thread_attr;

 res = pthread_create(&a_thread, &thread_attr, thread_function, (void *)message);
 pthread_detach(a_thread);

 s = pthread_join(a_thread,&thread_result);
 if(s != 0)
  printf("error to join\n");
 
 (void)pthread_attr_destroy(&thread_attr);
 while(!thread_finished) {
  printf("Waiting for thread to say it's finished...\n");
  sleep(1);
 }
 printf("Other thread finished, bye!\n");
 exit(EXIT_SUCCESS);
}

void *thread_function(void *arg) 
{
        //pthread_detach(pthread_self());
 printf("thread_function is running. Argument was %s\n", (char *)arg);
 sleep(4);
 printf("Second thread setting finished flag, and exiting now\n");
 thread_finished = 1;
 pthread_exit(NULL);
}

Method 2:
there are 2 flags: PTHREAD_CREATE_JOINABLE and PTHREAD_CREATE_DETACHED of
use the call to set detach state for attr object:
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);  
use this call to get detach state:
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);  
example: 
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

void *thread_function(void *arg);
char message[] = "Hello World";
int thread_finished = 0;
void *thread_result;

int main() {
 int res;
 pthread_t a_thread;
 pthread_attr_t thread_attr;

 res = pthread_attr_init(&thread_attr);
 res = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
 res = pthread_create(&a_thread, &thread_attr, thread_function, (void *)message);

 pthread_join(a_thread,&thread_result);
 (void)pthread_attr_destroy(&thread_attr);
 while(!thread_finished) {
  printf("Waiting for thread to say it's finished...\n");
  sleep(1);
 }
 printf("Other thread finished, bye!\n");
 exit(EXIT_SUCCESS);
}

void *thread_function(void *arg) 
{
 printf("thread_function is running. Argument was %s\n", (char *)arg);
 sleep(4);
 printf("Second thread setting finished flag, and exiting now\n");
 thread_finished = 1;
 pthread_exit(NULL);
}


Schedpolicy

This controls how threads are scheduled.
Use 2 functions:
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);

3 flags:

  • SCHED_OTHER: default
  • SCHED_RR: (only running process with super permission, real time scheduling), uses the round-robin scheduling scheme
  • SCHED_FIFO: (only running process with super permission, real time scheduling), uses the firsts in - first out scheduling scheme

Schedparam

In schedpolicy, if we choose policy is SCHED_OTHER, this is a parter to schedpolicy and allows control over the scheduling of threads

int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);
int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);

where: 
const struct sched_param{
      int sched_priority;
}

Inherisched

use 2 functions:

int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit);
int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit);
2 flags of inherit:
  • PTHREAD_EXPLICIT_SCHED (default): scheduling is explicitly set by the attributes
  • PTHREAD_INHERIT_SCHED: a new thread will instead use the parametters that its creator thread was using

example
/* pthreads_sched_test.c */

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>

#define handle_error_en(en, msg) \
        do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

static void
usage(char *prog_name, char *msg)
{
    if (msg != NULL)
        fputs(msg, stderr);

    fprintf(stderr, "Usage: %s [options]\n", prog_name);
    fprintf(stderr, "Options are:\n");
    #define fpe(msg) fprintf(stderr, "\t%s", msg);          /* Shorter */
    fpe("-a Set scheduling policy and priority in\n");
    fpe("                 thread attributes object\n");
    fpe("                  can be\n");
    fpe("                     f  SCHED_FIFO\n");
    fpe("                     r  SCHED_RR\n");
    fpe("                     o  SCHED_OTHER\n");
    fpe("-A               Use default thread attributes object\n");
    fpe("-i {e|i}         Set inherit scheduler attribute to\n");
    fpe("                 'explicit' or 'inherit'\n");
    fpe("-m Set scheduling policy and priority on\n");
    fpe("                 main thread before pthread_create() call\n\n");
    exit(EXIT_FAILURE);
}

static int
get_policy(char p, int *policy)
{
    switch (p) {
    case 'f': *policy = SCHED_FIFO;     return 1;
    case 'r': *policy = SCHED_RR;       return 1;
    case 'o': *policy = SCHED_OTHER;    return 1;
    default:  return 0;
    }
}

static void
display_sched_attr(int policy, struct sched_param *param)
{
    printf("    policy=%s, priority=%d\n",
            (policy == SCHED_FIFO)  ? "SCHED_FIFO" :
            (policy == SCHED_RR)    ? "SCHED_RR" :
            (policy == SCHED_OTHER) ? "SCHED_OTHER" :
            "???",
            param->sched_priority);
}

static void
display_thread_sched_attr(char *msg)
{
    int policy, s;
    struct sched_param param;

    s = pthread_getschedparam(pthread_self(), &policy, &param);
    if (s != 0)
        handle_error_en(s, "pthread_getschedparam");

    printf("%s\n", msg);
    display_sched_attr(policy, &param);
}

static void *
thread_start(void *arg)
{
    display_thread_sched_attr("Scheduler attributes of new thread");

    return NULL;
}

int
main(int argc, char *argv[])
{
    int s, opt, inheritsched, use_null_attrib, policy;
    pthread_t thread;
    pthread_attr_t attr;
    pthread_attr_t *attrp;
    char *attr_sched_str, *main_sched_str, *inheritsched_str;
    struct sched_param param;

    /* Process command-line options */

    use_null_attrib = 0;
    attr_sched_str = NULL;
    main_sched_str = NULL;
    inheritsched_str = NULL;

    while ((opt = getopt(argc, argv, "a:Ai:m:")) != -1) {
        switch (opt) {
        case 'a': attr_sched_str = optarg;      break;
        case 'A': use_null_attrib = 1;          break;
        case 'i': inheritsched_str = optarg;    break;
        case 'm': main_sched_str = optarg;      break;
        default:  usage(argv[0], "Unrecognized option\n");
        }
    }

    printf("%s %s %s\n", main_sched_str, attr_sched_str, inheritsched_str);

    if (use_null_attrib && (inheritsched_str != NULL || attr_sched_str != NULL))
        usage(argv[0], "Can't specify -A with -i or -a\n");

    /* Optionally set scheduling attributes of main thread,
       and display the attributes */

    if (main_sched_str != NULL) {
        if (!get_policy(main_sched_str[0], &policy))
            usage(argv[0], "Bad policy for main thread (-m)\n");
        param.sched_priority = strtol(&main_sched_str[1], NULL, 0);

        s = pthread_setschedparam(pthread_self(), policy, &param);
        if (s != 0)
            handle_error_en(s, "pthread_setschedparam");
    }

    display_thread_sched_attr("Scheduler settings of main thread");
    printf("\n");

    /* Initialize thread attributes object according to options */

    attrp = NULL;

    if (!use_null_attrib) {
        s = pthread_attr_init(&attr);
        if (s != 0)
            handle_error_en(s, "pthread_attr_init");
        attrp = &attr;
    }

    if (inheritsched_str != NULL) {
        if (inheritsched_str[0] == 'e')
            inheritsched = PTHREAD_EXPLICIT_SCHED;
        else if (inheritsched_str[0] == 'i')
            inheritsched = PTHREAD_INHERIT_SCHED;
        else
            usage(argv[0], "Value for -i must be 'e' or 'i'\n");

        s = pthread_attr_setinheritsched(&attr, inheritsched);
        if (s != 0)
            handle_error_en(s, "pthread_attr_setinheritsched");
    }

    if (attr_sched_str != NULL) {
        if (!get_policy(attr_sched_str[0], &policy))
            usage(argv[0],
                    "Bad policy for 'attr' (-a)\n");
        param.sched_priority = strtol(&attr_sched_str[1], NULL, 0);

        s = pthread_attr_setschedpolicy(&attr, policy);
        if (s != 0)
            handle_error_en(s, "pthread_attr_setschedpolicy");
        s = pthread_attr_setschedparam(&attr, &param);
        if (s != 0)
            handle_error_en(s, "pthread_attr_setschedparam");
    }

    /* If we initialized a thread attributes object, display
       the scheduling attributes that were set in the object */

    if (attrp != NULL) {
        s = pthread_attr_getschedparam(&attr, &param);
        if (s != 0)
            handle_error_en(s, "pthread_attr_getschedparam");
        s = pthread_attr_getschedpolicy(&attr, &policy);
        if (s != 0)
            handle_error_en(s, "pthread_attr_getschedpolicy");

        printf("Scheduler settings in 'attr'\n");
        display_sched_attr(policy, &param);

        s = pthread_attr_getinheritsched(&attr, &inheritsched);
        printf("    inheritsched is %s\n",
                (inheritsched == PTHREAD_INHERIT_SCHED)  ? "INHERIT" :
                (inheritsched == PTHREAD_EXPLICIT_SCHED) ? "EXPLICIT" :
                "???");
        printf("\n");
    }

    /* Create a thread that will display its scheduling attributes */

    s = pthread_create(&thread, attrp, &thread_start, NULL);

    s = pthread_join(thread, NULL);
    if (s != 0)
        handle_error_en(s, "pthread_join");

    exit(EXIT_SUCCESS);
}

Scope

This attribute controls how scheduling of a thread is calculated. Since Linux only currently supports the value PTHREAD_SCOPE_SYSTEM, we will not look at this further here

Stacksize

Linux implements threads with a large amount of stack by default, so the feature is generally redundant on Linux and consequently not implemented.
To set the stacksize of thread, use:
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

To get the stacksize of thread, use
$ulimit -s
8192   # The stack size limit is 8 MB
or
int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);


Thread cancellation 

the function to request a thread to terminate
int pthread_cancel(pthread_t thread); 
set cancel state
int pthread_setcancelstate(int state, int *oldstate); 

/*
2 states: 
PTHREAD_CANCEL_ENABLE: default
PTHREAD_CANCEL_DISABLE: cancellation is disable 
*/
set cancel type
int pthread_setcanceltype(int type, int *oldtype); 

/*
2 types: 
PTHREAD_CANCEL_ASYNCHRONOUS: cancel immediatetly
PTHREAD_CANCEL_DEFERRER: wait until the cancellation point excution.
*/
Cancellation points in linux
cancellation points
Another method to create a cancellation point: void pthread_testcancel(void);
Cleanup handlers:
#include <pthread.h>
void pthread_cleanup_push(void (*routine)(void*), void *arg);
void pthread_cleanup_pop(int execute);
example
#include <stdio.h>
#include <pthread.h>

void *threadFunc(void *arg)
{
 int j; int i=0;
 //pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
 //pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
 //pthread_setcanceltype(PTHREAD_CANCEL_DEFERRER, NULL);
 printf("new thread start\n");  //may be a cancellation point
 for (j = 1; ; ++j)
 {
  i++;          
  //pthread_testcancel();
 }
 printf("Loop %d\n", j); //may be a cancellation point 
 sleep(1);   //a cancellation point
 return NULL;
}

int main(int argc, char *argv[])
{
 pthread_t thr;
 int s;
 void *res;

 pthread_create(&thr, NULL, &threadFunc, NULL);
 sleep(3);

 pthread_cancel(thr);

 pthread_join(thr, &res);

 if(res == PTHREAD_CANCELED)
  printf("thread was cancel\n");
 else printf("thread was not canceled (should not happen!)\n");

 return 0;
}

Thread synchronization

Mutex

Statically allocated mutexes
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
Dynamically allocated mutexes
pthread_mutex_t mtx;
pthread_mutexattr_t attr;

int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
Locking and unlocking a mutex
/*lock mutex, blocking*/
int pthread_mutex_lock(pthread_mutex_t *mtx);

/*unlock mutex*/
int pthread_mutex_unlock(pthread_mutex_t *mtx);

/*lock mutex, non-blocking*/
int pthread_mutex_trylock(pthread_mutex_t *mtx);

/*lock mutex, timeout*/
int pthread_mutex_timelock(pthread_mutex_t *mtx);
  example
 
#include <stdio.h>
#include <pthread.h>

pthread_mutex_t mutex;
int count;


void* func(void *arg) {
  pthread_mutex_lock(&mutex);
  printf("This is thread %d\n",*(int*)arg);
  printf("count = %d\n", count++);
  sleep(1);
  pthread_mutex_unlock(&mutex);
}

int main(int argc, char **argv)
{
 int i;
 pthread_t tid[5];
 count=0;
 pthread_mutex_init(&mutex,NULL);

 for(i=0; i < 5; i++)
 {
  pthread_create(&tid[i],NULL,func, (void*)&tid[i]);
  
 }
 for(i=0; i < 5; i++)
 {
  pthread_join(&tid[i],NULL);
 }
 return 0;
}

  Performance of mutexes

Programs without mutexes is faster programs using mutexes, but in the typical case, a thread would spend
much more time doing other work, and perform relatively fewer mutex lock and unlock operations, so
that the performance impact of using a mutex is not significant in most applications
  Mutex deadlock
mutex deadlock


  Condition variable

A condition variable allows one thread to inform other threads about changes in the state of a shared variable (or other shared resource) and allows the other threads to wait (block) for such notification
/*statically*/
pthread_cond_t cond= PTHREAD_COND_INITIALIZER;

/*dynamically*/
pthread_cond_t cond;
pthread_conattr_t attr;

int pthread_cond_init(phtread_cond_t *cond, const pthread_conattr_t *attr);
   example
#include 

References

Richard Stones adn Neil Matthew, Begin Linux Programming, 2nd Edition
Michael Kerrisk, The programming interface, 2010

No comments:

Post a Comment