📄 manager.c
字号:
char * guardaddr; size_t stacksize, guardsize; if (attr != NULL && attr->__stackaddr_set) {#ifdef _STACK_GROWS_UP /* The user provided a stack. */ new_thread = (pthread_descr) attr->__stackaddr; new_thread_bottom = (char *) (new_thread + 1); guardaddr = attr->__stackaddr + attr->__stacksize; guardsize = 0;#else /* The user provided a stack. For now we interpret the supplied address as 1 + the highest addr. in the stack segment. If a separate register stack is needed, we place it at the low end of the segment, relying on the associated stacksize to determine the low end of the segment. This differs from many (but not all) other pthreads implementations. The intent is that on machines with a single stack growing toward higher addresses, stackaddr would be the lowest address in the stack segment, so that it is consistently close to the initial sp value. */ new_thread = (pthread_descr) ((long)(attr->__stackaddr) & -sizeof(void *)) - 1; new_thread_bottom = (char *) attr->__stackaddr - attr->__stacksize; guardaddr = new_thread_bottom; guardsize = 0;#endif#ifndef THREAD_SELF __pthread_nonstandard_stacks = 1;#endif /* Clear the thread data structure. */ memset (new_thread, '\0', sizeof (*new_thread)); } else {#ifdef NEED_SEPARATE_REGISTER_STACK size_t granularity = 2 * pagesize; /* Try to make stacksize/2 a multiple of pagesize */#else size_t granularity = pagesize;#endif void *map_addr; /* Allocate space for stack and thread descriptor at default address */#if FLOATING_STACKS if (attr != NULL) { guardsize = page_roundup (attr->__guardsize, granularity); stacksize = __pthread_max_stacksize - guardsize; stacksize = MIN (stacksize, page_roundup (attr->__stacksize, granularity)); } else { guardsize = granularity; stacksize = __pthread_max_stacksize - guardsize; } 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;# ifdef NEED_SEPARATE_REGISTER_STACK guardaddr = map_addr + stacksize / 2; if (guardsize > 0) mprotect (guardaddr, guardsize, PROT_NONE); new_thread_bottom = (char *) map_addr; new_thread = ((pthread_descr) (new_thread_bottom + stacksize + guardsize)) - 1;# elif _STACK_GROWS_DOWN guardaddr = map_addr; if (guardsize > 0) mprotect (guardaddr, guardsize, PROT_NONE); new_thread_bottom = (char *) map_addr + guardsize; new_thread = ((pthread_descr) (new_thread_bottom + stacksize)) - 1;# elif _STACK_GROWS_UP guardaddr = map_addr + stacksize; if (guardsize > 0) mprotect (guardaddr, guardsize, PROT_NONE); new_thread = (pthread_descr) map_addr; new_thread_bottom = (char *) (new_thread + 1);# else# error You must define a stack direction# endif /* Stack direction */#else /* !FLOATING_STACKS */ void *res_addr; if (attr != NULL) { guardsize = page_roundup (attr->__guardsize, granularity); stacksize = STACK_SIZE - guardsize; stacksize = MIN (stacksize, page_roundup (attr->__stacksize, granularity)); } else { guardsize = granularity; stacksize = STACK_SIZE - granularity; }# ifdef NEED_SEPARATE_REGISTER_STACK new_thread = default_new_thread; new_thread_bottom = (char *) (new_thread + 1) - stacksize - guardsize; /* Includes guard area, unlike the normal case. Use the bottom end of the segment as backing store for the register stack. Needed on IA64. In this case, we also map the entire stack at once. According to David Mosberger, that's cheaper. It also avoids the risk of intermittent failures due to other mappings in the same region. The cost is that we might be able to map slightly fewer stacks. */ /* First the main stack: */ map_addr = (caddr_t)((char *)(new_thread + 1) - stacksize / 2); res_addr = mmap(map_addr, stacksize / 2, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (res_addr != map_addr) { /* Bad luck, this segment is already mapped. */ if (res_addr != MAP_FAILED) munmap(res_addr, stacksize / 2); return -1; } /* Then the register stack: */ map_addr = (caddr_t)new_thread_bottom; res_addr = mmap(map_addr, stacksize/2, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (res_addr != map_addr) { if (res_addr != MAP_FAILED) munmap(res_addr, stacksize / 2); munmap((caddr_t)((char *)(new_thread + 1) - stacksize/2), stacksize/2); return -1; } guardaddr = new_thread_bottom + stacksize/2; /* We leave the guard area in the middle unmapped. */# else /* !NEED_SEPARATE_REGISTER_STACK */# ifdef _STACK_GROWS_DOWN new_thread = default_new_thread; new_thread_bottom = (char *) (new_thread + 1) - stacksize; map_addr = new_thread_bottom - guardsize; res_addr = mmap(map_addr, stacksize + guardsize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (res_addr != map_addr) { /* Bad luck, this segment is already mapped. */ if (res_addr != MAP_FAILED) munmap (res_addr, stacksize + guardsize); return -1; } /* We manage to get a stack. Protect the guard area pages if necessary. */ guardaddr = map_addr; if (guardsize > 0) mprotect (guardaddr, guardsize, PROT_NONE);# else /* The thread description goes at the bottom of this area, and * the stack starts directly above it. */ new_thread = (pthread_descr)((unsigned long)default_new_thread &~ (STACK_SIZE - 1)); map_addr = mmap(new_thread, stacksize + guardsize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (map_addr == MAP_FAILED) return -1; new_thread_bottom = map_addr + sizeof(*new_thread); guardaddr = map_addr + stacksize; if (guardsize > 0) mprotect (guardaddr, guardsize, PROT_NONE);# endif /* stack direction */# endif /* !NEED_SEPARATE_REGISTER_STACK */#endif /* !FLOATING_STACKS */ } *out_new_thread = new_thread; *out_new_thread_bottom = new_thread_bottom; *out_guardaddr = guardaddr; *out_guardsize = guardsize; return 0;}static int pthread_handle_create(pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void *), void *arg, sigset_t * mask, int father_pid, int report_events, td_thr_events_t *event_maskp){ size_t sseg; int pid; pthread_descr new_thread; char * new_thread_bottom; pthread_t new_thread_id; char *guardaddr = NULL; size_t guardsize = 0; int pagesize = __getpagesize(); /* First check whether we have to change the policy and if yes, whether we can do this. Normally this should be done by examining the return value of the __sched_setscheduler call in pthread_start_thread but this is hard to implement. FIXME */ if (attr != NULL && attr->__schedpolicy != SCHED_OTHER && geteuid () != 0) return EPERM; /* Find a free segment for the thread, and allocate a stack if needed */ for (sseg = 2; ; sseg++) { if (sseg >= PTHREAD_THREADS_MAX) return EAGAIN; if (__pthread_handles[sseg].h_descr != NULL) continue; if (pthread_allocate_stack(attr, thread_segment(sseg), pagesize, &new_thread, &new_thread_bottom, &guardaddr, &guardsize) == 0) break; } __pthread_handles_num++; /* Allocate new thread identifier */ pthread_threads_counter += PTHREAD_THREADS_MAX; new_thread_id = sseg + pthread_threads_counter; /* Initialize the thread descriptor. Elements which have to be initialized to zero already have this value. */ new_thread->p_tid = new_thread_id; new_thread->p_lock = &(__pthread_handles[sseg].h_lock); new_thread->p_cancelstate = PTHREAD_CANCEL_ENABLE; new_thread->p_canceltype = PTHREAD_CANCEL_DEFERRED; new_thread->p_reentp = &new_thread->p_reent; _REENT_INIT_PTR(new_thread->p_reentp); new_thread->p_h_errnop = &new_thread->p_h_errno; new_thread->p_resp = &new_thread->p_res; new_thread->p_guardaddr = guardaddr; new_thread->p_guardsize = guardsize; new_thread->p_header.data.self = new_thread; new_thread->p_nr = sseg; new_thread->p_inheritsched = attr ? attr->__inheritsched : 0; /* Initialize the thread handle */ __pthread_init_lock(&__pthread_handles[sseg].h_lock); __pthread_handles[sseg].h_descr = new_thread; __pthread_handles[sseg].h_bottom = new_thread_bottom; /* Determine scheduling parameters for the thread */ new_thread->p_start_args.schedpolicy = -1; if (attr != NULL) { new_thread->p_detached = attr->__detachstate; new_thread->p_userstack = attr->__stackaddr_set; switch(attr->__inheritsched) { case PTHREAD_EXPLICIT_SCHED: new_thread->p_start_args.schedpolicy = attr->__schedpolicy; memcpy (&new_thread->p_start_args.schedparam, &attr->__schedparam, sizeof (struct sched_param)); break; case PTHREAD_INHERIT_SCHED: new_thread->p_start_args.schedpolicy = __sched_getscheduler(father_pid); __sched_getparam(father_pid, &new_thread->p_start_args.schedparam); break; } new_thread->p_priority = new_thread->p_start_args.schedparam.__sched_priority; } /* Finish setting up arguments to pthread_start_thread */ new_thread->p_start_args.start_routine = start_routine; new_thread->p_start_args.arg = arg; new_thread->p_start_args.mask = *mask; /* Make the new thread ID available already now. If any of the later functions fail we return an error value and the caller must not use the stored thread ID. */ *thread = new_thread_id; /* Raise priority of thread manager if needed */ __pthread_manager_adjust_prio(new_thread->p_priority); /* Do the cloning. We have to use two different functions depending on whether we are debugging or not. */ pid = 0; /* Note that the thread never can have PID zero. */ if (report_events) { /* See whether the TD_CREATE event bit is set in any of the masks. */ int idx = __td_eventword (TD_CREATE); uint32_t mask = __td_eventmask (TD_CREATE); if ((mask & (__pthread_threads_events.event_bits[idx] | event_maskp->event_bits[idx])) != 0) { /* Lock the mutex the child will use now so that it will stop. */ __pthread_lock(new_thread->p_lock, NULL); /* We have to report this event. */#ifdef NEED_SEPARATE_REGISTER_STACK /* Perhaps this version should be used on all platforms. But this requires that __clone2 be uniformly supported everywhere. And there is some argument for changing the __clone2 interface to pass sp and bsp instead, making it more IA64 specific, but allowing stacks to grow outward from each other, to get less paging and fewer mmaps. */ pid = __clone2(pthread_start_thread_event, (void **)new_thread_bottom, (char *)new_thread - new_thread_bottom, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | __pthread_sig_cancel, new_thread);#elif _STACK_GROWS_UP pid = __clone(pthread_start_thread_event, (void **) new_thread_bottom, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | __pthread_sig_cancel, new_thread);#else pid = __clone(pthread_start_thread_event, (void **) new_thread, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | __pthread_sig_cancel, new_thread);#endif if (pid != -1) { /* Now fill in the information about the new thread in the newly created thread's data structure. We cannot let the new thread do this since we don't know whether it was already scheduled when we send the event. */ new_thread->p_eventbuf.eventdata = new_thread;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -