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来控制线程之间的同步。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
;
}
来自CODE的代码片
snippet_file_0.c
实验三:子进程主动释放从父进程继承来的互斥量
(1)执行顺序(“|”代表线程的生命周期):
父进程主线程:| |
父进程子线程: | lock, fork, , unlock |
子进程主线程: | run, unlock, lock |
(2)结果:
父子进程都能够正常运行。
(3)代码:
为了简化实验代码,使用了sleep来控制线程之间的同步。
4、进一步思考
既然,子进程中主动释放锁能够起到跟pthread_atfork一样的效果,相比而言,pthread_atfork有些什么好处呢?
有可能是为了更好的管理代码,使用pthread_atfork,只需要处理自己写的代码;而前者需要做统一的管理,容易出现遗漏;
5、更进一步思考
第三方库(如libc库),如果大量使用了锁(如malloc),但是库并没有使用pthread_atfork来注册清理函数,则我们需要谨慎在多线程中使用fork。