pthread_atfork解读

本文详细介绍了pthread_atfork函数在多线程环境下的作用,包括其在父进程fork创建子进程前后的调用顺序。通过三个实验,探讨了不使用pthread_atfork时,子进程访问或释放从父进程继承的互斥量对父进程和子进程的影响。实验结果显示,子进程主动释放锁可以达到与pthread_atfork类似的效果,但使用pthread_atfork能更好地管理代码,避免潜在问题。在涉及多线程和fork操作时,需要注意第三方库可能存在的锁管理问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

apue中的对pthread_atfork的使用说明:
pthread_atfork(void (*prepare)(void),void (*parent)(void), void(*child)(void))
prepare在父进程fork创建子进程之前调用,这里可以获取父进程定义的所有锁;
child fork返回之前在子进程环境中调用,在这里unlock prepare获得的锁;
parent fork创建了子进程以后,但在fork返回之前在父进程的进程环境中调用的,在这里对prepare获得的锁进行解锁;

1、使用场景
当父进程有多线程时,子进程继承父进程所有的互斥量、读写锁和条件变量的状态,子进程只存在一个线程,它是由父进程中调用fork的线程的副本构成的。如果父进程中的线程占有锁(任一线程),子进程同样占有这些锁。如果父进程马上调用exec,老的地址空间被丢弃,所以锁的状态无关紧要。否则,则要清除锁的状态。

2、猜想
(1)如果子进程并不访问从父进程继承来的互斥量,不使用pthread_atfork是否会影响父进程,子进程的运行?
(2)如果子进程也需要访问从父进程继承来的locked互斥量,不使用pthread_atfork是否会影响父进程,子进程的运行?
(3)如果子进程主动释放从父进程继承来的互斥量不使用pthread_atfork是否会影响父进程,子进程的运行?
带着这几个疑问,下面进行了几组实验。

3、验证
实验一:子进程并不访问从父进程继承来的互斥量
(1)执行顺序(“|”代表线程的生命周期):
父进程主线程:|                                                          ,lock, unlock           |
父进程子线程:      |    lock, fork,           ,unlock                                |
子进程主线程:                                |                                                   |
(2)结果:
父进程的new thread, 能够正常unlock, 主线程能够正常lock, unlock
不使用 pthread_atfork并不会影响父进程,子进程的运行。
(3)代码:
为了简化实验代码,使用了sleep来控制线程之间的同步。


实验二: 子进程访问从父进程继承来的locked的互斥量

(1)执行顺序(“|”代表线程的生命周期):
父进程主线程:|                                                                                  |
父进程子线程:    |    lock, fork,           unlock, lock, unlock   |
子进程主线程:                              | run,     lock, unlock        |
(2)结果:
父进程中的子线程,能够正常unlock, 但是子进程一直没能够getlock
不使用pthread_atfork并不会影响父进程,但是会影响子进程的运行。
(3)代码:
为了简化实验代码,使用了sleep来控制线程之间的同步。

void * thr_main(void * arg)
{
    printf("parent process: new thread start \n");
    pthread_mutex_lock(&lock);
    printf("parent process: new thread get lock \n");

    pid_t pid;
    if((pid = fork()) < 0)
    {
    	perror("fork");
    	pthread_exit(0);
    }
    else if(pid == 0) // child process
    {
        printf("child process: start\n");
    	printf("child process: unlock and get lock \n");
    	pthread_mutex_unlock(&lock); 
    	pthread_mutex_lock(&lock);    	   	
    	printf("child process: exit\n");
    }
    else // parent process
    {
    	sleep(10);
        pthread_mutex_unlock(&lock);
        printf("parent process: new thread unlock \n");
        printf("parent process: new thread exit \n");

    }
	return ((void*)0);
}


int main(void)
{
    printf("parent process: main thread start \n");
    int err = 0;
    pthread_t tid;
    
    err = pthread_create(&tid, NULL, thr_main, NULL);
    if(err != 0)
    {
        perror("pthread_create");
        exit(1);
        }
	sleep(50);
    printf("parent process: main thread exit \n");
    return 0;
}

实验三: 子进程主动释放从父进程继承来的互斥量

(1)执行顺序(“|”代表线程的生命周期):
父进程主线程:|                                                                                    |
父进程子线程:    |    lock, fork,                                    , unlock   |
子进程主线程:                              | run,  unlock, lock   |
(2)结果:
父子进程都能够正常运行。
(3)代码:
为了简化实验代码,使用了sleep来控制线程之间的同步。

4、进一步思考
既然,子进程中主动释放锁能够起到跟 pthread_atfork一样的效果,相比而言,pthread_atfork有些什么好处呢?
有可能是为了更好的管理代码,使用pthread_atfork,只需要处理自己写的代码;而前者需要做统一的管理,容易出现遗漏;

5、更进一步思考
第三方库(如libc库),如果大量使用了锁(如malloc),但是库并没有使用 pthread_atfork来注册清理函数,则我们需要谨慎在多线程中使用fork。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值