- 論壇徽章:
- 0
|
大家好,本人被下面這個問題困擾了一段時間,最近似乎找到了答案。
這里和大家分享一下,可能對有相同困惑的同學有點幫助,同時也請各位幫忙看看錯漏的地方。
1================問題:
在使用pthread庫創(chuàng)建兩個線程時clone()被調(diào)用了兩次,可以用strace 看到:
int
main()
{
...
err=pthread_create(&tid, NULL, job, NULL);
err=pthread_create(&tid1, NULL, job, NULL);
...
} |
strace:
clone(Thread is running.
child_stack=0xb7efb4b4, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0xb7efbbd8, {entry_number:6, base_addr:0xb7efbb90, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}, child_tidptr=0xb7efbbd = 5104
clone(child_stack=0xb76fa4b4, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0xb76fabd8, {entry_number:6, base_addr:0xb76fab90, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}, child_tidptr=0xb76fabd = 5105
|
大家都知道clone()可以產(chǎn)生一個所謂的“輕量級進程”,也就是有獨立的task_struct的,獨立調(diào)度的東西。
再看看flag:CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID
這里對這些flag就不多說了對于我這個問題,最為重要的是CLONE_VM(共享內(nèi)存描述符mm_struct和所有的頁表)。
這點可以在copy_mm()里面看得到(在clone()中被調(diào)用):
static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
{
...
if (clone_flags & CLONE_VM) {
atomic_inc(&oldmm->mm_users);
mm = oldmm;
goto good_mm;
}
...
} |
這樣我這里兩個pthread和原來的main進程就共享同一個mm_struct了,如圖1:
part1.jpg (774.69 KB, 下載次數(shù): 64)
下載附件
2008-11-09 15:08 上傳
pthread線程之間的棧必定是獨立的,不然不可能被獨立調(diào)度,我的問題就在這里,3個task_struct 共用一個mm_struct,那么他們的棧應該怎么辦?難道在一個棧里面?
2======================解答:
問題主要在兩個函數(shù)上面,一個是clone()系統(tǒng)調(diào)用,一個是pthread_create()
man clone,看他的參數(shù)。參數(shù)列表中最為顯眼的就是第二個參數(shù)void *child_stack,man里面是這樣形容它的:
The child_stack argument specifies the location of the stack used by the child process. Since the child
and calling process may share memory, it is not possible for the child process to execute in the same
stack as the calling process.
調(diào)用clone()的時候是要自己提供子task的?臻g的,這個系統(tǒng)調(diào)用實在是太特別了。
在看看pthread的庫是怎樣給這個參數(shù)賦值的:
函數(shù)pthread_handle_create() (manager.c里面) 被調(diào)用來創(chuàng)建新線程,在這個函數(shù)里面調(diào)用了pthread_allocate_stack()
來分配棧空間:
static int pthread_allocate_stack(const pthread_attr_t *attr,
pthread_descr default_new_thread,
int pagesize,
char ** out_new_thread,
char ** out_new_thread_bottom,
char ** out_guardaddr,
size_t * out_guardsize,
size_t * out_stacksize)
{
...
map_addr = mmap(NULL, stacksize + guardsize,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (map_addr == MAP_FAILED)
/* No more memory available. */
return -1;
...
} |
可以看到pthread庫是通過調(diào)用mmap()來為新的線程創(chuàng)建?臻g的,可以在細看一下它的參數(shù),它使用了flag
MAP_ANONYMOUS和MAP_PRIVATE,而且fd參數(shù)中用了-1,這樣調(diào)用的結果是在進程空間中創(chuàng)建一個匿名的
線性區(qū),這樣就有了棧空間,而且這個空間也在原來的mm_struct里面,如圖2:
part2.jpg (774.69 KB, 下載次數(shù): 60)
下載附件
2008-11-09 15:08 上傳
光有空間不行,還要看看內(nèi)核是怎樣使用,我們來到copy_thread(),它在copy_process()中被調(diào)用(它們都在clone()里面被調(diào)用的):
static struct task_struct *copy_process(unsigned long clone_flags,
unsigned long stack_start,
struct pt_regs *regs,
unsigned long stack_size,
int __user *child_tidptr,
struct pid *pid)
{
...
retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
...
} |
int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
unsigned long unused,
struct task_struct * p, struct pt_regs * regs)
{
struct pt_regs * childregs;
struct task_struct *tsk;
int err;
childregs = task_pt_regs(p);
*childregs = *regs;
childregs->ax = 0;
childregs->sp = sp;
...
} |
這里可以清楚的看到,內(nèi)核為這個用戶線程初始化他未來的sp寄存器值,就是剛才mmap()返回的那個地址。所以結果
如圖3:
part3.jpg (901.92 KB, 下載次數(shù): 82)
下載附件
2008-11-09 15:08 上傳
附上pthread庫,有興趣的同學可以研究一下,好像還比較復雜。
[ 本帖最后由 pennyliang 于 2008-11-9 15:08 編輯 ] |
|