fork + pthread_create 記憶體空間差異

大家知道,pthread_create()函數的線程函數必須是靜態的函數以標準的__cdecl的方式調用的,而C++的成員函數是以__thiscall的方式調用的,相當於一個普通函數有一個默認的const ClassType* this參數

為數據封裝的需要,我常常把線程函數封裝在一個類的內部,定義一個類的私有靜態成員函數來作為pthread的線程函數,通常如下:

pthread_create(xx,xxx,xxx,帶入main thread stack value or this class 的 instace);

因此在子thread 可以存取共享 main thread 的 變數 or this instace

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/syscall.h>

#define GetProcessInfo(s) do { sprintf(s, "%d %d  %s:%d %s", getpid(), gettid(), __FILE__, __LINE__, __func__);} while (0)

pid_t gettid()
{
    pid_t tid;
    tid = syscall(SYS_gettid);
    return tid;
}

class B
{
public :
    int data;

    B()
    {
        data = 100;
    }

    static void* print(void* __this)
    {
        B* _this = (B*)__this;
        char s[100];

        while (1) {
            GetProcessInfo(s);
            printf("%s, %p %d\n", s, _this,  _this->data);
            sleep(1);
        }

        pthread_exit(NULL);
    }

    static void* add(void* __this)
    {
        B* _this = (B*)__this;
        char s[100];

        while (1) {
            _this->data += 1;
            GetProcessInfo(s);
            printf("%s, %p %d\n", s, _this,  _this->data);
            sleep(2);
        }

        pthread_exit(NULL);
    }

    void start_thread()
    {
        pthread_t thread_add, thread_print;
        // 帶入 this 可以讓子thread 存取 main thread B class 的 b instace
        pthread_create(&(thread_print), NULL, print, (void*)this);
        pthread_create(&(thread_add), NULL, add, (void*)this);
    }
};

int main(int argc, char* argv[])
{
    char s[100];
    B b;
    b.start_thread();

    while (1) {
        GetProcessInfo(s);
        printf("%s,%d\n", s,  b.data);
        sleep(3);
    }




    return 0;
}
## #define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/syscall.h>

#define GetProcessInfo(s) do { sprintf(s, "%d %d  %s:%d %s", getpid(), gettid(), __FILE__, __LINE__, __func__);} while (0)

pid_t gettid()
{
    pid_t tid;
    tid = syscall(SYS_gettid);
    return tid;
}

void* incrementA(void* value)
{
    int* val = (int*) value;
    char s[100];
    GetProcessInfo(s);
    printf("%s, val=%d\n", s, *val);    
    *val += 100;
    sleep(5);
    *val += 100;
    GetProcessInfo(s);
    printf("%s, val=%d\n", s, *val);    
    return 0;
}

void* incrementB(void* value)
{
    int* val = (int*) value;
    char s[100];
    sleep(1);
    GetProcessInfo(s);
    printf("%s, val=%d\n", s, *val);    
    sleep(10);
    *val += 10;
    GetProcessInfo(s);
    printf("%s, val=%d\n", s, *val);    
    return 0;
}

int main(int argc, char** argv)
{

    int stackvar = 0;

    pthread_t thread1, thread2;
    int iret1, iret2;

    // 帶入 stackvar 可以讓子thread 存取 main thread 資料
    iret1 = pthread_create(&thread1, NULL, incrementA, (void*) &stackvar);
    iret2 = pthread_create(&thread2, NULL, incrementB, (void*) &stackvar);

    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);

    char s[100];
    GetProcessInfo(s);

    printf("%s, %d\n",s, stackvar);

    return 0;
}

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/syscall.h>

/**************************************
 * fork-pthread.c
 *
 * pthread_join一般是主線程來調用,用來等待子線程退出,因為是等待,所以是阻塞的,一般主線程會依次join所有它創建的子線程。
 * pthread_exit一般是子線程調用,用來結束當前線程。
 * 一個線程對應一個pthread_join()調用,對同一個線程進行多次pthread_join()調用是邏輯錯誤。
 * -1: fork() 發生錯誤, 無 child process 產生.
 *  0: 在child process裡
 * >0: 在parent process裡, fork() 回傳值, 即是 child process 的 PID
 *************************************/

#define DELAY 10

int g = 100;

pid_t gettid()
{
    pid_t tid;
    tid = syscall(SYS_gettid);
    return tid;
}

class B
{
public :
    int a;

    B()
    {
        a = 100;
    }

    static void* print(void* __this)
    {
        B * _this =(B *)__this;
        while (1) {
            printf("print pid=%d, tid=%d, a=%d\n", getpid(), gettid(), _this->a);
            sleep(1);
        }

        pthread_exit(NULL);
    }

    static void* add(void* __this)
    {
        B * _this =(B *)__this;
        while (1) {
            _this->a += 1;
            printf("add pid=%d, tid=%d, a=%d\n", getpid(), gettid(), _this->a);
            sleep(2);
        }

        pthread_exit(NULL);
    }

    void start_thread()
    {
        int err;
        pthread_t thread_add, thread_print;
        err = pthread_create(&(thread_print), NULL, print, (void*)this);
        err = pthread_create(&(thread_add), NULL, add, (void*)this);
    }
};


void* func(void* param)
{
    int slept = DELAY;

    B b;
    b.start_thread();

    printf("-------------------------------------------------------------------\n");
    printf("func pid=%d, tid=%d, a=%d\n", getpid(), gettid(), b.a);

    printf("thread pid=%d, tid=%d, g=%d, g address=%p[+]\n", getpid(), gettid(), g,
           (void*)&g);
    g = 88;
    printf("thread pid=%d, tid=%d, g=%d, g address=%p[-]\n", getpid(), gettid(), g,
           (void*)&g);
    fflush(stdout);


    while ((slept = sleep(slept)) != 0) ;

    pthread_exit(NULL);
}

int main(int argc, char* argv[])
{
    int err;
    pid_t pid;
    int status;
    pthread_t thread;
    int slept = DELAY;
    printf("main pid=%d tid=%d\n", getpid(), gettid());
    fflush(stdout);

    g = 100;

    pid = fork();

    if (pid == -1) {
        perror("fork");
        exit(EXIT_FAILURE);
    }

    if (pid == 0) {
        // fils
        printf("child pid=%d tid=%d, g=%d, g address=%p[+]\n", getpid(), gettid(), g,
               (void*)&g);
        g = 50;
        printf("child pid=%d tid=%d, g=%d, g address=%p[-]\n", getpid(), gettid(), g,
               (void*)&g);

        fflush(stdout);

        while ((slept = sleep(slept)) != 0) ;

        return (EXIT_SUCCESS);
    } else {
        // père
        err = pthread_create(&(thread), NULL, &func, NULL);

        if (err != 0) {
            perror("pthread_create");
            exit(EXIT_FAILURE);
        }

        printf("parent pid=%d tid=%d, g=%d, g address=%p[+]\n", getpid(), gettid(), g,
               (void*)&g);

        // 主thread等到子thread結束
        err = pthread_join(thread, NULL);
        printf("parent pid=%d tid=%d, g=%d, g address=%p[-]\n", getpid(), gettid(), g,
               (void*)&g);

        if (err != 0) {
            perror("pthread_join");
            exit(EXIT_FAILURE);
        }

        printf("waitpid=%d\n", pid);

        // 父行程等子行程結束
        int fils = waitpid(pid, &status, 0);

        if (fils == -1) {
            perror("wait");
            exit(EXIT_FAILURE);
        }

        if (!WIFEXITED(status)) {
            fprintf(stderr, "Erreur de waitpid\n");
            exit(EXIT_FAILURE);
        }
    }

    return (EXIT_SUCCESS);
}

函數模版不單可以替換類型本身,還能替換類型的成員函數。

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/syscall.h>

using namespace std;

#define GetProcessInfo(ss) do { sprintf(ss, "%d %d  %p  %s:%d %s", getpid(), gettid(),(void*)this, __FILE__, __LINE__, __func__);} while (0)

pid_t gettid()
{
    pid_t tid;
    tid = syscall(SYS_gettid);
    return tid;
}

template <typename TYPE, void (TYPE::*_RunThread)() >
void* _thread_t(void* param)
{
    TYPE* This = (TYPE*)param;
    // 多 wrapper 一層讓_RunThread 不需要是static 函數
    This->_RunThread();
    return NULL;
}

class MyClass
{
public:
    MyClass()
    {
        data = 66;
        char s[100];
        GetProcessInfo(s);
        printf("%s, data=%d\n", s, data);
        pthread_create(&tid, NULL, _thread_t<MyClass, &MyClass::_RunThread>, this);
    }

    ~MyClass()
    {
        char s[100];
        GetProcessInfo(s);
        printf("%s, data=%d\n", s, data);

        pthread_cancel(tid);
        pthread_join(tid, NULL);
    }

    void _RunThread()
    {
        char s[100];
        GetProcessInfo(s);

        while (1) {
            printf("%s, data=%d\n", s, data);
            data+= 3;
            sleep(1);
            //this->DoSomeThing();
            //...
        }
    }

    int data;

private:
    pthread_t tid;
};

int main(int argc, char* argv[])
{
    MyClass a;

    while (1) {
        printf("main data=%d\n", a.data);
        sleep(2);
        //this->DoSomeThing();
        //...
    }

    return 0;
}

C++ 建構解構都由產生 instance 的pid 解構

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/syscall.h>

pid_t gettid()
{
    pid_t tid;
    tid = syscall(SYS_gettid);
    return tid;
}

void get_pid_tid(char s[]) {
    sprintf(s, "pid=%d, tid=%d\n", getpid(), gettid());
}

class testClass
{
public:
    testClass();
    ~testClass();
private:
    static void* thread_func(void*);
    pthread_t thrid;
};

testClass::testClass()
{
    char s[50];
    get_pid_tid(s);
    printf("[%s:%d,%s,%s]\n", __FILE__, __LINE__, __func__, s);
    pthread_create(&thrid, NULL, thread_func, this);
}

testClass::~testClass()
{
    char s[50];
    get_pid_tid(s);
    printf("[%s:%d,%s,%s]\n", __FILE__, __LINE__, __func__, s);
    pthread_cancel(thrid);
    pthread_join(thrid, NULL);
}

void* testClass::thread_func(void* param)
{
    int i = 0;
    testClass* self = (testClass*)param;
    char s[50];
    get_pid_tid(s);

    printf("[%s:%d,%s,%s]thread start\n", __FILE__, __LINE__, __func__,s);

    while (1) {
        printf("[%s:%d,%s]%d,%s\n", __FILE__, __LINE__, __func__, i++,s);
        sleep(1);
    }

    printf("[%s:%d,%s,%s]thread stop\n", __FILE__, __LINE__, __func__,s);
    return (void*)0;
}

int main(int argc, char** argv)
{
    testClass* test;
    test = new testClass();
    sleep(10);
    delete test;
    return 0;
}