精华内容
下载资源
问答
  • pthread_atfork函数 函数原型: int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)); 函数功能 简而言之,该函数在调用**fork()**前执行prepare函数,调用fork()后在子进程...

    pthread_atfork函数

    函数原型:

    int pthread_atfork(void (*prepare)(void), void (*parent)(void),
                  void (*child)(void));
    

    函数功能

    简而言之,该函数在调用**fork()**前执行prepare函数,调用fork()后在子进程执行child函数,在父进程执行parent函数,返回0表示函数执行成功。值得注意的是,无论函数定义在哪里,只有在下一个fork()执行前,该函数才被执行。

    线程进程混合引起的死锁

    对于一个简单的进程与线程混合代码如下:

    代码示例

    代码1

    #include<pthread.h>
    #include<unistd.h>
    #include<stdio.h>
    #include <sys/wait.h>
    
    pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;
    void *doit(void *arg = NULL)
    {
        printf("Begin doit with pid %d\n", getpid());
        //pthread_mutex_lock(&mutex);
        struct timespec t{2,0};
        nanosleep(&t, NULL);
        //pthread_mutex_unlock(&mutex);
        printf("End doit with pid %d\n", getpid());
    }
    
    int main()
    {
        pthread_t t;
        pthread_create(&t, NULL, doit, NULL);
        printf("sub thread created successfully\n");
        struct timespec t2{2,0};
        nanosleep(&t2, NULL);
        if(fork() == 0)
        {
            printf("sub process created successfully\n");
            doit();
        }
        wait(NULL);
        return 0;
    }
    

    执行后的结果为:
    在这里插入图片描述
    从结果来看,没有什么问题,因为并未涉及到临界区,那么下面,我们假设有一个临界区:

    代码2

    #include<pthread.h>
    #include<unistd.h>
    #include<stdio.h>
    #include <sys/wait.h>
    
    pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;
    void *doit(void *arg = NULL)
    {
        printf("Begin doit with pid %d\n", getpid());
        //pthread_mutex_lock(&mutex);
        struct timespec t{2,0};
        nanosleep(&t, NULL);
        //pthread_mutex_unlock(&mutex);
        printf("End doit with pid %d\n", getpid());
    }
    
    int main()
    {
        pthread_t t;
        pthread_create(&t, NULL, doit, NULL);
        printf("sub thread created successfully\n");
        struct timespec t2{2,0};
        nanosleep(&t2, NULL);
        if(fork() == 0)
        {
            printf("sub process created successfully\n");
            doit();
        }
        wait(NULL);
        return 0;
    }
    

    上面就是把那两个注释去掉,运行的结果好像和之前还是一样。下面更近一步,让主函数中等待的时间短一点:

    代码3

    #include<pthread.h>
    #include<unistd.h>
    #include<stdio.h>
    #include <sys/wait.h>
    
    pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;
    void *doit(void *arg = NULL)
    {
        printf("Begin doit with pid %d\n", getpid());
        //pthread_mutex_lock(&mutex);
        struct timespec t{2,0};
        nanosleep(&t, NULL);
        //pthread_mutex_unlock(&mutex);
        printf("End doit with pid %d\n", getpid());
    }
    
    int main()
    {
        pthread_t t;
        pthread_create(&t, NULL, doit, NULL);
        printf("sub thread created successfully\n");
        //更改的是这个地方
        struct timespec t2{1,0};
        nanosleep(&t2, NULL);
        if(fork() == 0)
        {
            printf("sub process created successfully\n");
            doit();
        }
        wait(NULL);
        return 0;
    }
    

    结果如下:
    在这里插入图片描述
    输出中并未见到第二个"end doit",这说明代码中有死锁的现象,那么现在分析一下为什么会有死锁的现象。代码2和3中的区别就是等待时间的长短,其中在代码2中,主线程和子线程等待时间一样,主线程等待完毕,子线程执行完毕,代码运行正常,但是安全吗?这个是不安全的,操作系统的异步性不能总是保证二者同时执行完毕或者子线程先于主线程执行完毕。
    在代码3中,我们缩短了主线程的等待时间,这种情况下,子线程是处于加锁的状态,创建子进程时,这个状态被复制到子进程中,子进程调用这个函数,重复加锁,因此产生死锁。

    解决方案

    既然调用的时候可能会产生锁状态复制的情况,那么创建子进程之前解一下锁就行,反正有锁解锁,无锁解一下也不会出错。因此结合pthread_fork函数改正后的代码为:

    代码4

    #include<pthread.h>
    #include<unistd.h>
    #include<stdio.h>
    #include <sys/wait.h>
    
    pthread_mutex_t  mutex = PTHREAD_MUTEX_INITIALIZER;
    void *doit(void *arg)
    {
        printf("Begin doit with pid %d\n", getpid());
        pthread_mutex_lock(&mutex);
        struct timespec t={2,0};
        nanosleep(&t, NULL);
        pthread_mutex_unlock(&mutex);
        printf("End doit with pid %d\n", getpid());
    }
    void *prepare(void *arg )
    {
        pthread_mutex_unlock(&mutex);
    }
    void *parent(void *arg)
    {
        pthread_mutex_lock(&mutex);
    }
    int main()
    {
        pthread_t t;
        pthread_atfork(prepare, parent, NULL);
        pthread_create(&t, NULL, doit, NULL);
        printf("sub thread created successfully\n");
        struct timespec t2={1,0};
        nanosleep(&t2, NULL);
        if(fork() == 0)
        {
            printf("sub process created successfully\n");
            doit(NULL);
        }
        wait(NULL);
        return 0;
    }
    

    代码运行正常
    在这里插入图片描述

    展开全文
  • 答案:子进程创建出来时只有一个线程,就是调用fork()函数的那个线程。也就是说,父进程在复制子进程的时候,只会复制当前线程的执行状态,其它线程不会复制。(https://www.cnblogs.com/wenxuanh/p/12573966.html)...

    首先来看一个问题,在linux系统中,我们都知道fork会产生一个调用进程的复制,创建出一个新的进程,那么如果父进程有多个线程会不会复制父进程的多个线程呢?
    答案:子进程创建出来时只有一个线程,就是调用fork()函数的那个线程。也就是说,父进程在复制子进程的时候,只会复制当前线程的执行状态,其它线程不会复制。(https://www.cnblogs.com/wenxuanh/p/12573966.html)

    在linux下,经常会调用fork来产生一个子进程,如果出现既要考虑多线程,又要考虑多进程的情况,这个时候就要引入pthread_atfork函数了。
    在这里插入图片描述

    一个在多线程程序里fork造成死锁的例子:

    父进程在创建了一个线程,并在线程函数doit中对mutex加锁(假设此时还未运行到解锁代码处);
    父进程创建一个子进程,在子进程中调用doit,由于子进程会复制父进程的内存,所以这时候mutex处于锁的状态;
    父进程在复制子进程的时候,只会复制当前线程的执行状态,其它线程不会复制。因此子进程会处于死锁的状态(阻塞在doit的加锁代码处)。
    deadlock_test.cc

    // 一个在多线程程序里fork造成死锁的例子
    // 一个输出示例:
    /*
    pid = 19445 Entering main ...
    pid = 19445 begin doit ...
    pid = 19447 begin doit ...
    pid = 19445 end doit ...
    pid = 19445 Exiting main ...
    */
    #include <stdio.h>
    #include <time.h>
    #include <pthread.h>
    #include <unistd.h>
    
    
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    void* doit(void* arg)
    {
    	printf("pid = %d begin doit ...\n",static_cast<int>(getpid()));
    	pthread_mutex_lock(&mutex);
    	struct timespec ts = {2, 0};
    	nanosleep(&ts, NULL);
    	pthread_mutex_unlock(&mutex);
    	printf("pid = %d end doit ...\n",static_cast<int>(getpid()));
    
    	return NULL;
    }
    
    int main(void)
    {
    	printf("pid = %d Entering main ...\n", static_cast<int>(getpid()));
    	pthread_t tid;
    	pthread_create(&tid, NULL, doit, NULL);
    	struct timespec ts = {1, 0};
    	nanosleep(&ts, NULL);
    	if (fork() == 0)
    	{
    		doit(NULL);
    	}
    	pthread_join(tid, NULL);
    	printf("pid = %d Exiting main ...\n",static_cast<int>(getpid()));
    
    	return 0;
    }
    
    
    ps aux |grep deadlock_test
    

    在这里插入图片描述

    killall deadlock_test
    

    pthread_atfork解决死锁问题

    解决做法的本质上就是在fork执行之前先调用prepare函数解锁,这样确保子进程调用doit时可以成功上锁。
    Pthread_atfork_test.cc

    #include <stdio.h>
    #include <time.h>
    #include <pthread.h>
    #include <unistd.h>
    
    
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    void* doit(void* arg)
    {
    	printf("pid = %d begin doit ...\n",static_cast<int>(getpid()));
    	pthread_mutex_lock(&mutex);
    	struct timespec ts = {2, 0};
    	nanosleep(&ts, NULL);
    	pthread_mutex_unlock(&mutex);
    	printf("pid = %d end doit ...\n",static_cast<int>(getpid()));
    
    	return NULL;
    }
    
    void prepare(void)
    {
    	pthread_mutex_unlock(&mutex);
    }
    
    void parent(void)
    {
    	pthread_mutex_lock(&mutex);
    }
    
    int main(void)
    {
    	pthread_atfork(prepare, parent, NULL);
    	printf("pid = %d Entering main ...\n", static_cast<int>(getpid()));
    	pthread_t tid;
    	pthread_create(&tid, NULL, doit, NULL);
    	struct timespec ts = {1, 0};
    	nanosleep(&ts, NULL);
    	if (fork() == 0)
    	{
    		doit(NULL);
    	}
    	pthread_join(tid, NULL);
    	printf("pid = %d Exiting main ...\n",static_cast<int>(getpid()));
    
    	return 0;
    }
    
    

    在这里插入图片描述

    展开全文
  • 如果一个多线程程序的某个线程调用了fork函数,那么新创建的子进程是否将自动创建和父进程相同数量的线程呢? 答案是不会,子进程只拥有一个执行线程,它是调用fork的那个线程的完整复制。 并且子进程将自动继承父...

    如果一个多线程程序的某个线程调用了fork函数,那么新创建的子进程是否将自动创建和父进程相同数量的线程呢?
    答案是不会,子进程只拥有一个执行线程,它是调用fork的那个线程的完整复制。
    并且子进程将自动继承父进程中互斥锁的状态。
    也就是说,父进程中已经加锁的互斥锁在子进程中也是被锁住的。
    这引来的问题:子进程可能不清楚从父进程中继承而来的互斥锁的具体状态(加锁或者解锁状态未知)。
    这个互斥锁可能被加锁了,但并不是由调用fork函数的那个线程锁住的,而是由其他线程锁住的。若是此种情况,子进程若再次对该互斥锁执行加锁操作就会导致死锁。

    多线程程序中调用fork函数可能导致死锁程序示例:

    #include <pthread.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <wait.h>
    
    pthread_mutex_t mutex;
    
    void* another(void*arg)
    {
        printf("子线程中,互斥锁上锁\n");
        pthread_mutex_lock(&mutex);
        sleep(5);
        pthread_mutex_unlock(&mutex);
    }
    
    int main()
    {
        pthread_mutex_init(&mutex, NULL);
        pthread_t id;
        pthread_create(&id, NULL, another, NULL);
    
        /*确保子线程已经开始运行并获得互斥变量mutex,主线程sleep(1)*/
        sleep(1);
    
        int pid = fork();
        if(pid < 0)
        {
            pthread_join(id, NULL);
            pthread_mutex_destroy(&mutex);
            return 1;
        }
        else if(pid == 0)//子进程
        {
            printf("子进程, 获取锁\n");
            /*
            子进程从父进程继承了互斥锁mutex的状态,
            该互斥锁处于锁住状态,
            这是由父进程中的子线程执行pthread_mutex_lock引起
            因此,下面这句加锁会一直阻塞
            */
            pthread_mutex_lock(&mutex);
            printf("阻塞了。。。。\n");
            pthread_mutex_unlock(&mutex);
            exit(0);
        }
        else
        {
            wait(NULL);
        }
        pthread_join(id, NULL);
        pthread_mutex_destroy(&mutex);
    
        return 0;
    }

    pthread_atfork函数

    pthread提供了一个专门的函数pthread_atfork(),以确保fork调用后父进程和紫禁城都拥有一个清楚地锁状态。该函数的定义如下:

    #include <pthread.h>
    int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));

    该函数将建立3个fork句柄来帮助我们清理互斥锁状态。
    prepare句柄将在fork调用创建出子进程之前被执行,它可以用来锁住所有父进程中的互斥锁。
    parent句柄则是fork调用创建出子进程之后,而fork返回之前,在父进程中被执行。它的作用是释放所有在prepare句柄中被锁住的互斥锁。
    child句柄是fork返回之前,在子进程中被执行。和parent句柄一样,child句柄也是用于释放所有在prepare句柄中被锁住的互斥锁。

    函数成功返回0, 错误返回错误码。

    在上述程序中使用pthread_atfork函数

    在fork调用前加入下列代码:

    void prepare()
    {
        pthread_mutex_lock(&mutex);
    }
    
    void infork()
    {
        pthread_mutex_unlock(&unlock);
    }
    
    pthread_atfork(prepare, infork, infork);
    

    《Linux高性能服务器编程》学习笔记

    展开全文
  • linux之pthread_atfork()函数讲解

    万次阅读 2014-08-08 11:40:41
    linux之pthread_atfork()函数讲解
    linux之pthread_atfork()函数讲解

    #include <pthread.h>
    int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));

    pthread_atfork()在fork()之前调用,当调用fork时,内部创建子进程前在父进程中会调用prepare,内部创建子进程成功后,父进程会调用parent ,子进程会调用child。

    直接看下代码:
    #include <stdio.h>
    #include <time.h>
    #include <pthread.h>
    #include <unistd.h>
    
    
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    void *doit(void *arg)
    {
        printf("pid = %d begin doit ...\n", static_cast<int>(getpid()));
        pthread_mutex_lock(&mutex);
        struct timespec ts = {2, 0};
        nanosleep(&ts, NULL);
        pthread_mutex_unlock(&mutex);
        printf("pid = %d end doit ...\n", static_cast<int>(getpid()));
    
        return NULL;
    }
    
    void prepare(void)
    {
        pthread_mutex_unlock(&mutex);
    }
    
    void parent(void)
    {
        pthread_mutex_lock(&mutex);
    }
    
    int main(void)
    {
        pthread_atfork(prepare, parent, NULL);
        printf("pid = %d Entering main ...\n", static_cast<int>(getpid()));
        pthread_t tid;
        pthread_create(&tid, NULL, doit, NULL);
        struct timespec ts = {1, 0};
        nanosleep(&ts, NULL);
        if (fork() == 0)
        {
            doit(NULL);
        }
        pthread_join(tid, NULL);
        printf("pid = %d Exiting main ...\n", static_cast<int>(getpid()));
    
        return 0;
    }

    同样的流程,但在执行fork() 创建子进程之前,先执行prepare(), 将子线程加锁的mutex 解锁下,然后为了与doit() 配对,在创建子进程成功后,父进程调用parent() 再次加锁,这时父进程的doit() 就可以接着解锁执行下去。而对于子进程来说,由于在fork() 创建子进程之前,mutex已经被解锁,故复制的状态也是解锁的,所以执行doit()就不会死锁了。
    执行结果如下:
    simba@ubuntu:~/Documents/build/debug/bin$ ./deadlock_test2
    pid = 4905 Entering main ...
    pid = 4905 begin doit ...
    pid = 4908 begin doit ...
    pid = 4905 end doit ...
    pid = 4905 Exiting main ...
    simba@ubuntu:~/Documents/build/debug/bin$ pid = 4908 end doit ...
    pid = 4908 Exiting main ...

    simba@ubuntu:~/Documents/build/debug/bin$ 

    可以看到子进程也正常退出了。


    展开全文
  • 使用pthread库实现openssl多线程ssl服务端和客户端,大家参考使用吧
  • 在进行linux系统里开发时,经常会调用linux的系统函数fork来产生一个子进程,如果父子进程都没有用到pthread线程相关函数,则就不存在需要理解pthread_atfork函数的必要。问题是有时候既要考虑多线程,又要考虑多...
  • int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)); 说明:该函数用于注册三个回调函数,后面用户调用fork函数时,才真正执行。调用fork时,内部创建子进程前在父进程中会...
  • pthread_atfork 注册fork的函数 实例 #define _UNIX03_THREADS 1 2 #include <pthread.h> 3 #include <stdio.h> 4 #include <unistd.h> 5 #include <fcntl.h> 6 #include <sys/types.h...
  • 103-atfork 与 fork

    2017-03-18 15:14:10
    linux 提供了一个称之为 pthread_atfork函数,它允许...1. 函数原型int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));上面三个回调函数,分别为 prepare 函数,parent 函数
  • pthread_atfork解读

    千次阅读 2015-02-11 20:09:10
    apue中的对pthread_atfork的使用说明: pthread_atfork(void (*prepare)(void),void (*parent)(void), void(*child)(void)) prepare在父进程fork创建子进程之前调用,这里可以获取父进程定义的所有锁; child ...
  • pthread类知识点: 1、boost::is_same判断两种...函数原型:intpthread_atfork(void(*prepare)(void),void(*parent)(void),void(*child)(void)); 对应头文件:#include<pthread.h> 用法:调用fork时,内...
  • 如果一个多线程程序的某个线程调用了fork()函数,那么新创建的子进程是否将自动创建和父进程相同数量的线程呢? 答案是否定的,正确解释是子进程只拥有一个执行线程,他是调用fork()的那个线程的完整复制。并且子...
  • pthread_atfork()函数使用

    2013-05-09 15:05:02
    if ((err = pthread_atfork(prepare, parent, child)) != 0) err_exit(err, "can't install fork handlers"); err = pthread_create(&tid, NULL, thr_fn, 0); if (err != 0) err_exit(err, "can't ...
  • Linux多线程学习(十)pthread_atfork

    千次阅读 2012-09-11 13:33:57
    pthread_atfork 注册fork的函数 实例  #define _UNIX03_THREADS 1  2 #include  3 #include  4 #include  5 #include  6 #include  7 #include  8 #include  9 #
  • pthread_create()和pthread_atfork()函数使用时应注意的问题  由于 pthread 库不是 Linux 系统默认的库,连接时需要使用库libpthread.a,所以在使用pthread_create()创建线程,以及调用pthread_atfork()...
  • .../voice_demo: hidden symbol `pthread_atfork' in /opt/gcc-linaro-aarch64-linux-gnu-4.9-2014.09_linux/bin/../aarch64-linux-gnu/libc/usr/lib/aarch64-linux-gnu/libpthread_nonshared.a(...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,100
精华内容 440
关键字:

atfork函数