layout: post title: Linux Thread comments: true date: 2016-10-26 10:00:32+00:00 categories:

  • Linux
  • Tech tags:
  • linux
  • thread main-class: ‘Linux’ color: ‘#006798’

introduction: ‘The Note of APUE Thread’

Thread Identification

#include <pthread.h>

int pthread_equal(pthread_t tid1, pthread_t tid2);
  • Returns: nonzero if equal, 0 otherwise
#include <pthread.h>

pthread_t pthread_self(void);
  • Returns: the thread ID of the calling thread

Thread Creation

#include <pthread.h>

int pthread_create(pthread_t *restrict tidp,
                   const pthread_attr_t *restrict attr,
                   void *(*start_rtn)(void),
                   void *restrict arg);
  • Returns: 0 if OK, error number on failure
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

pthread_t ntid;


void printids(const char *s) {
    pid_t pid;
    pthread_t tid;

    pid = getpid();
    tid = pthread_self();
    printf("%s pid %u tid %u (0x%x)\n", s, (unsigned int) pid,
           (unsigned int) tid, (unsigned int) tid);
}

void *start_rtn(void *arg) {
    printids("new thread");
    return (void *) 0;
}

int main() {
    int err = pthread_create(&ntid, NULL, start_rtn, NULL);
    if (err != 0) {
        printf("can not create thread : %s\n", strerror(err));
    }
    printids("main thread");
    sleep(1);
    return 0;
}
  • result
    main thread pid 26921 tid 22664960 (0x159d700)
    new thread pid 26921 tid 14526208 (0xdda700)
    

Thread Termination

  • Three ways to terminate thread

    The thread can simply return from the start routine. The return value is the thread’s exit code. The thread can be canceled by another thread in the same process. The thread can call pthread_exit.

#include <pthread.h>

void pthread_exit(void *rval_ptr);
#include <pthread.h>

int pthread_join(pthread_t thread, void **rval_ptr);
  • Returns: 0 if OK, error number on failure
#include <pthread.h>
#include <stdio.h>
void* thr_fn1(void* arg){
    printf("I am thread 1 return 1\n");
    pthread_exit((void*)1);
}


void* thr_fn2(void* arg){
    printf("I am thread 2 return 2\n");
    pthread_exit((void*)2);
}

//ignore error handling
int main() {
    pthread_t p1,p2;
    void* pret;
    pthread_create(&p1,NULL,thr_fn1,NULL);
    pthread_join(p1,&pret);
    printf("return from p1 %d\n",(int)pret);

    pthread_create(&p2,NULL,thr_fn2,NULL);
    pthread_join(p2,&pret);
    printf("return from p2 %d\n",(int)pret);

    return 0;
}
I am thread 1 return 1
return from p1 1
I am thread 2 return 2
return from p2 2
  • stack
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

struct foo {
    int a, b, c, d;
};

void print_foo(const char *msg, const struct foo *fp) {
    printf(msg);
    printf("  structure at 0x%x\n", (unsigned) fp);
    printf("  foo.a = %d\n", fp->a);
    printf("  foo.b = %d\n", fp->b);
    printf("  foo.c = %d\n", fp->c);
    printf("  foo.d = %d\n", fp->d);
}

void *thr_fn1(void *arg) {
    struct foo foo = {1, 2, 3, 4};
    print_foo("thread 1:\n", &foo);
    pthread_exit((void *) &foo);
}

void *thr_fn2(void *arg) {
    printf("thread 2: ID is %d\n", pthread_self());
    pthread_exit((void *) 0);
}

int main() {
    struct foo *fp;
    pthread_t p1, p2;
    pthread_create(&p1, NULL, thr_fn1, NULL);
    pthread_join(p1, (void *) &fp);
    sleep(1);
    pthread_create(&p2, NULL, thr_fn2, NULL);
    pthread_join(p2, NULL);
    sleep(1);
    print_foo("parent \n", fp);
    return 0;
}
thread 1:
  structure at 0x7f351f40
  foo.a = 1
  foo.b = 2
  foo.c = 3
  foo.d = 4
thread 2: ID is 2134189824
parent
  structure at 0x7f351f40
  foo.a = 0
  foo.b = 0
  foo.c = 1
  foo.d = 0
  • heap
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

struct foo {
    int a, b, c, d;
};

void print_foo(const char *msg, const struct foo *fp) {
    printf(msg);
    printf("  structure at 0x%x\n", (unsigned) fp);
    printf("  foo.a = %d\n", fp->a);
    printf("  foo.b = %d\n", fp->b);
    printf("  foo.c = %d\n", fp->c);
    printf("  foo.d = %d\n", fp->d);
}

void *thr_fn1(void *arg) {
    struct foo *foo = malloc(sizeof(struct foo));
    foo->a = 1;
    foo->b = 2;
    foo->c = 3;
    foo->d = 4;
    print_foo("thread 1:\n", foo);
    pthread_exit((void *) foo);
}

void *thr_fn2(void *arg) {
    printf("thread 2: ID is %d\n", pthread_self());
    pthread_exit((void *) 0);
}

int main() {
    struct foo *fp;
    pthread_t p1, p2;
    pthread_create(&p1, NULL, thr_fn1, NULL);
    pthread_join(p1, (void *) &fp);
    sleep(1);
    pthread_create(&p2, NULL, thr_fn2, NULL);
    pthread_join(p2, NULL);
    sleep(1);
    print_foo("parent \n", fp);
    return 0;
}
thread 1:
  structure at 0x80008c0
  foo.a = 1
  foo.b = 2
  foo.c = 3
  foo.d = 4
thread 2: ID is 250177280
parent
  structure at 0x80008c0
  foo.a = 1
  foo.b = 2
  foo.c = 3
  foo.d = 4

Thread Cancel

#include <pthread.h>

int pthread_cancel(pthread_t tid);
  • Returns: 0 if OK, error number on failure

Note that pthread_cancel() doesn’t wait for the thread to terminate. It merely makes the request.

Thread Exit Handler

#include <pthread.h>

void pthread_cleanup_push(void (*rtn)(void *),void *arg);

void pthread_cleanup_pop(int execute);

Makes a call to pthread_exit Responds to a cancellation request Makes a call to pthread_cleanup_pop with a nonzero execute argument

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

void cleanup(void *arg) {
    printf("cleanup: %s\n", (char *) arg);
}

void *thr_fn1(void *arg) {
    printf("thread 1 start\n");
    pthread_cleanup_push(cleanup, "thread 1 first handler") ;
    pthread_cleanup_push(cleanup, "thread 1 second handler") ;
    printf("thread 1 push complete\n");
    if (arg)
        return ((void *) 1);
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
    return ((void *) 1);
}

void *thr_fn2(void *arg) {
    printf("thread 2 start\n");
    pthread_cleanup_push(cleanup, "thread 2 first handler") ;
    pthread_cleanup_push(cleanup, "thread 2 second handler") ;
    printf("thread 2 push complete\n");
    if (arg)
        pthread_exit((void *) 2);
    pthread_cleanup_pop(0);
    pthread_cleanup_pop(0);
    pthread_exit((void *) 2);
}

int main() {
    pthread_t p1, p2;
    void *ret;
    pthread_create(&p1, NULL, thr_fn1, (void *) 1);
    pthread_create(&p2, NULL, thr_fn2, (void *) 2);
    sleep(1);
    pthread_join(p1, &ret);
    printf("return from thread1 is : %d\n", (int) (ret));

    pthread_join(p2, &ret);
    printf("return from thread2 is : %d\n", (int) (ret));
    exit(0);
}
thread 1 start
thread 1 push complete
thread 2 start
thread 2 push complete
cleanup: thread 2 second handler
cleanup: thread 2 first handler
return from thread1 is : 1
return from thread2 is : 2
#include <pthread.h>

int pthread_detach(pthread_t tid);

Thread Synchronization

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

int a = 0;

void* thr_run(void* arg){
    sleep(1);
    ++a;
    return (void*)0;
}

int main() {
    int count = 10000;
    pthread_t ts[count];
    for (int i = 0; i < count; ++i) {
        pthread_create(&ts[i],NULL,thr_run,NULL);
    }

    for (int i = 0; i < count; ++i) {
        pthread_join(ts[i],NULL);
    }
    printf("a=%d\n",a);
    return 0;
}
  • result
a=9989

Thread Mutex

#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *restrict mutex,
                       const pthread_mutexattr_t *restrict attr);

int pthread_mutex_destroy(pthread_mutex_t *mutex);
#include <pthread.h>

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_trylock(pthread_mutex_t *mutex);

int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • All return: 0 if OK, error number on failure
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
pthread_mutex_t mutex;
int a = 0;

void* thr_run(void* arg){
    pthread_mutex_lock(&mutex);
    ++a;
    pthread_mutex_unlock(&mutex);
    return (void*)0;
}

int main() {
    pthread_mutex_init(&mutex,NULL);
    int count = 10000;
    pthread_t ts[count];
    for (int i = 0; i < count; ++i) {
        pthread_create(&ts[i],NULL,thr_run,NULL);
    }

    for (int i = 0; i < count; ++i) {
        pthread_join(ts[i],NULL);
    }
    printf("a=%d\n",a);
    return 0;
}
a=10000

ReaderWriter Locks

#include <pthread.h>

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
                        const pthread_rwlockattr_t *restrict attr);

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
  • Both return: 0 if OK, error number on failure

#include <pthread.h>

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
  • Both return: 0 if OK, error number on failure

Condition Variables

#include <pthread.h>

int pthread_cond_init(pthread_cond_t *restrict cond,
                      pthread_condattr_t *restrict attr);

int pthread_cond_destroy(pthread_cond_t *cond);
#include <pthread.h>

int pthread_cond_wait(pthread_cond_t *restrict cond,
                      pthread_mutex_t *restrict mutex);

int pthread_cond_timedwait(pthread_cond_t *restrict cond,
                           pthread_mutex_t *restrict mutex,
                           const struct timespec *restrict timeout);

Thread Control

Thread Limits

/*************************************************************************
	> File Name: thread_limits.c
	> Author: Lion
	> Mail: lion.good.very.first@gmail.com
	> Created Time: Sat 05 Nov 2016 02:29:02 PM CST
 ************************************************************************/

#include<stdio.h>
#include <unistd.h>
#include <limits.h>

int main() {
#ifdef _SC_THREAD_DESTRUCTOR_ITERATIONS
    printf("PTHREAD_DESTRUCTOR_ITERATIONS=%d\n", sysconf(_SC_THREAD_DESTRUCTOR_ITERATIONS));
#endif

#ifdef _SC_THREAD_KEYS_MAX
    printf("PTHREAD_KEYS_MAX=0x%X\n", sysconf(_SC_THREAD_KEYS_MAX));
#endif

#ifdef _SC_THREAD_STACK_MIN
    printf("PTHREAD_STACK_MIN=0x%X\n", sysconf(_SC_THREAD_STACK_MIN));
#endif

#ifdef _SC_THREAD_THREADS_MAX
    printf("PTHREAD_THREADS_MAX=0x%X\n", sysconf(_SC_THREAD_THREADS_MAX));
#endif
    return 0;
}
  • Results
PTHREAD_DESTRUCTOR_ITERATIONS=4
PTHREAD_KEYS_MAX=0x400
PTHREAD_STACK_MIN=0x4000
PTHREAD_THREADS_MAX=0xFFFFFFFF

Thread Attributes

#include <pthread.h>

int pthread_attr_init(pthread_attr_t *attr);

int pthread_attr_destroy(pthread_attr_t   *attr);
  • Table of Thread Attributes | Name | Description | | :————- | :————- | | detachstate | detached thread attribute | | guardsize | guard buffer size in bytes at end of thread stack | | stackaddr | lowest address of thread stack | | stacksize | size in bytes of thread stack |

detachstate attribute

#include <pthread.h>
int pthread_attr_getdetachstate(const pthread_attr_t *restrict attr,
                                int *detachstate);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
  • Return : 0 if OK, error number on failure
#include<stdio.h>
#include <pthread.h>

void *thr_fn(void *args) {

}

int main() {
    int err;
    pthread_t p;
    pthread_attr_t attr;

    err = pthread_attr_init(&attr);
    if (err != 0)
        return err;
    err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    if (0 == err)
        pthread_create(&p, &attr, thr_fn, NULL);
    pthread_attr_destroy(&attr);
    return 0;
}

thread stack attribute

#include <pthread.h>

int pthread_attr_getstack(const pthread_attr_t *restrict attr,
                          void **restrict stackaddr,
                          size_t *restrict stacksize);

int pthread_attr_setstack(const p

```c
#include <pthread.h>

int pthread_attr_getstack(const pthread_attr_t *restrict attr,
                          void **restrict stackaddr,
                          size_t *restrict stacksize);

int pthread_attr_setstack(const pthread_attr_t *attr,
                          void *stackaddr, size_t *stacksize);
#include<stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

long fibonacci(int fibo) {
    if (fibo == 0 || fibo == 1)
        return 1;
    else
        return fibonacci(fibo - 1) + fibonacci(fibo - 2);
}

void *thr_fn(void *args) {
    int a = 0;
    int b = 1;
    long arr[1<<16] ;//alloc more than 64k on stack
    printf("ret = %d\n", fibonacci(20));
    return (void *) 0;
}

int main() {
    pthread_attr_t attr;
    int err;
    void *addr;
    size_t size;
    pthread_t p;
    if (err = pthread_attr_init(&attr)) {
        return err;
    }

    pthread_attr_getstacksize(&attr, &size);
    printf("size=0x%x\n", size);


    pthread_attr_setstacksize(&attr, 1024*64);//64k

    pthread_attr_getstacksize(&attr, &size);
    printf("size=0x%x\n", size);

    if (err = pthread_create(&p, &attr, thr_fn, NULL)) {
        return err;
    }

    sleep(3);

    pthread_attr_getstacksize(&attr, &size);
    printf("size=0x%x\n"l 11

```c, size);

pthread_attr_getstack(&attr,&addr,&size);
printf("size=0x%x\n", size);

pthread_join(p, NULL);
return 0; } ```
  • Results
size=0x800000
size=0x10000
Command terminated by signal 11
#define	SIGSEGV		11	/* Segmentation violation (ANSI).  */
#include <pthread.h>

int pthread_attr_getguardsize(const pthread_attr_t *restrict attr,
                                size_t *restrict guardsize);

int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);

The guardsize thread attribute controls the size of the memory extent after the end of the thread’s stack to protect against stack overflow.

Mutex Attributes

#include <pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t *attr);

int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
  • Both return: 0 if OK, error number on failure
#include <pthread.h>

int pthread_mutexattr_getpshared(const pthread_mutexattr_t* restrict attr,
                                 int *restrict pshared);

int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,
                                 int pshared);
  • Both return: 0 if OK, error number on failure

  • process-shared attribute check _POSIX_THREAD_PROCESS_SHARED
  • PTHREAD_PROCESS_PRIVATE is the default behavior.Within a process, multiple threads can access the same synchronization object
  • PTHREAD_PROCESS_SHARED shared between multiple processes may be used for synchronization by those processes

  • The type attribute
Mutex type Relock without unlock? Unlock when not owned? Unlock when unlocked?
PTHREAD_MUTEX_NORMAL deadlock undefined undefined
PTHREAD_MUTEX_ERRORCHECK returns error returns error returns error
PTHREAD_MUTEX_RECURSIVE allowed returns error returns error
PTHREAD_MUTEX_DEFAULT undefined undefined undefined
#include <pthread.h>

int pthread_mutexattr_gettype(const pthread_mutexattr_t *
                              restrict attr,
                               int *restrict type);

int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
  • Both return: 0 if OK, error number on failure