方式一
1.File--NewProject--next,取个名字,意味着你的文件存放地。
2.Next--Next--勾选Web Application--Finish。
3.Edit Configurations--添加--tomcat--local--找到WEB容器所地
4.OK,这样就构建成功了,可以开发你的项目。
那么包怎么加载勒
回到主界面,File--Project Structure(Ctrl+Shift+Alt+s(快捷方式))---Libraries--添加符合点击--Java-找到你的包所在地。
方式二
2.在新建好的project中new Module---> create module from scratch--->next--->取个名字--->next--->create source directory---> next--->勾选Web Application--->选2.5(在右边)--->finish 即可
鉴于上边的原因,我们需要在子进程调用exit后在父进成中调用wait或waipid
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int &statloc);
pid_t waitpid(pid_t pid,int *statloc, int options);
Both return:process ID if OK,-1 on error
它们被父进程调用以获取子进程结束信息、清除zombie。当父进成调用这两个函数时
a 阻塞(如果它的子进程还在运行)
b 立即返回子进程结束信息(如果一个子进程已经结束并等待父进程获取信息)
c 返回错误(如果不存在子进程)
两个函数的不同在于wait会令调用者阻塞直至某个子进程终止而waitpid则可以通过设置一个选项来设置为非阻塞,另外waitpid并不是等待第一个结束的进程而是等待参数中pid指定的进程。
两个函数中的变量statloc是一个指向int型数据的指针。如果此变量不是NULL,则结束的进程的termination status会被保存在statiloc所指向的内存的区域;如果我们不关心termination status,则可以把statloc置为NULL。
传统的实现中这两个函数返回的整数中特定的比特位被赋予了特定的含义。POSIX.1指定了一些包含在头文件<sys/wait.h> 宏来查看这些termination status
Macro Description
WIFEXITED(status) 如果status是由一个正常结束的进程产生的则值为真, 此时我们可以继续使用宏WEXITSTATUS(status)来 获取exit或_exit的参数
WIFSIGNALED(status) 如果status是由一个异常结束(接受到一个信号)的进 程产生的则值为真,此时使用宏WTERMSIG(status)来 获取信号数。
WIFSTOPPED(status) 如果status是由一个接受到信号目前终止的进程产生的 则值为真,此时可以继续调用宏WSTOPSIG(status)来 查看是哪个信号导致进程终止。
waitpid的option常量
WNOHANG waitpid将不阻塞如果指定的pid并未结束
WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状态不予以理会。
waitpid中pid的含义依据其具体值而变
pid==-1 等待任何一个子进程,此时waitpid的作用与wait相同
pid >0 等待进程ID与pid值相同的子进程
pid==0 等待与调用者进程组ID相同的任意子进程
pid<-1 等待进程组ID与pid绝对值相等的任意子进程
waitpid提供了wait所没有的三个特性:
1 waitpid使我们可以等待指定的进程
2 waitpid提供了一个无阻塞的wait
3 waitpid支持工作控制
具体可以查看APUE page202
===========================================================================
进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait 就会收集这个子进程的信息, 并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
wait(等待子进程中断或结束)
相关函数 waitpid,fork
表头文件
#include<sys/types.h>
#include<sys/wait.h>
定义函数 pid_t wait (int * status);
函数说明
wait()会暂时停止目前进程的执行,直到有信号来到或子进程结
束。如果在调用wait()时子进程已经结束,则wait()会立即返
回子进程结束状态值。子进程的结束状态值会由参数status 返回,
而子进程的进程识别码也会一快返回。如果不在意结束状态值,则
参数status 可以设成NULL。子进程的结束状态值请参考waitpid()。
返回值
如果执行成功则返回子进程识别码(PID),如果有错误发生则返回
-1。失败原因存于errno 中。
附加说明
范例 一
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
pid_t pid;
int status,i;
if(fork()= =0){
printf(“This is the child process .pid =%d\n”,getpid());
exit(5);
}else{
sleep(1);
printf(“This is the parent process ,wait for child...\n”;
pid=wait(&status);
i=WEXITSTATUS(status);
printf(“child
int __pthread_create_2_1(pthread_t *thread, const pthread_attr_t *attr,
void * (*start_routine)(void *), void *arg)
// thread 为pthread_t,实则是pthread指针。
// typedef struct pthread *pthread_t; ->unsigned long int
// attr 创建线程时的附加属性
// start_routine 执行的线程函数
// arg参数首地址指针
{
pthread_descr self = thread_self();
// 向管理线程的请求
struct pthread_request request;
// 在初始化时 全局 int __pthread_manager_request = -1;
if (__pthread_manager_request < 0) {
// 首先Linuxthreads需要有__pthread_manager_thread,这个线程是个很特殊的线程
// 初始化Linuxthreads系统线程
if (__pthread_initialize_manager() < 0) return EAGAIN;
}
request.req_thread = self;
// 请求类型 CREATE ,并将新线程(轻量进程)的参数都打包到request结构中。
request.req_kind = REQ_CREATE;
request.req_args.create.attr = attr;
request.req_args.create.fn = start_routine;
request.req_args.create.arg = arg;
//sigprocmask用于改变进程的当前阻塞信号集
sigprocmask(SIG_SETMASK, (const sigset_t *) NULL,
&request.req_args.create.mask);
// 向__pthread_manager_request写入request请求。 request请求将被_pthread_manager_thread读取并处理。
__libc_write(__pthread_manager_request, (char *) &request, sizeof(request));
// 挂起一下
suspend(self);
if (THREAD_GETMEM(self, p_retcode) == 0)
*thread = (pthread_t) THREAD_GETMEM(self, p_retval);
return THREAD_GETMEM(self, p_retcode);
}
#if defined HAVE_ELF && defined PIC && defined DO_VERSIONING
default_symbol_version (__pthread_create_2_1, pthread_create, GLIBC_2.1);
int __pthread_create_2_0(pthread_t *thread, const pthread_attr_t *attr,
void * (*start_routine)(void *), void *arg)
{
/* The ATTR attribute is not really of type `pthread_attr_t *'. It has
the old size and access to the new members might crash the program.
We convert the struct now. */
pthread_attr_t new_attr;
if (attr != NULL)
{
size_t ps = __getpagesize ();
memcpy (&new_attr, attr,
(size_t) &(((pthread_attr_t*)NULL)->__guardsize));
new_attr.__guardsize = ps;
new_attr.__stackaddr_set = 0;
new_attr.__stackaddr = NULL;
new_attr.__stacksize = STACK_SIZE - ps;
attr = &new_attr;
}
return __pthread_create_2_1 (thread, attr, start_routine, arg);
}
symbol_version (__pthread_create_2_0, pthread_create, GLIBC_2.0); // 由此可知pthread_create入口函数为__pthread_create_2_1
#else
strong_alias (__pthread_create_2_1, pthread_create)
#endif
由上面的函数可知,pthread_create对线程的创建实际上是向pthread_manager_thread发送个request请求。
下面我们就看pthread_manager_thread是怎样初始化的:
首先要进行基本系统的初始化工作:
即填充__pthread_initial_thread(算是模板吧?)其他属性:
/* Descriptor of the initial thread */
struct _pthread_descr_struct __pthread_initial_thread = {
&__pthread_initial_thread, /* pthread_descr p_nextlive */ // 将进程中所有用户线程串在了一起
&__pthread_initial_thread, /* pthread_descr p_prevlive */
NULL, /* pthread_descr p_nextwaiting */
NULL, /* pthread_descr p_nextlock */
PTHREAD_THREADS_MAX, /* pthread_t p_tid */
0, /* int p_pid */
0, /* int p_priority */
&__pthread_handles[0].h_lock, /* struct _pthread_fastlock * p_lock */
0, /* int p_signal */
NULL, /* sigjmp_buf * p_signal_buf */
NULL, /* sigjmp_buf * p_cancel_buf */
0, /* char p_terminated */
0, /* char p_detached */
0, /* char p_exited */
NULL, /* void * p_retval */
0, /* int p_retval */
NULL, /* pthread_descr p_joining */
NULL, /* struct _pthread_cleanup_buffer * p_cleanup */
0, /* char p_cancelstate */
0, /* char p_canceltype */
0, /* char p_canceled */
NULL, /* int *p_errnop */
0, /* int p_errno */
NULL, /* int *p_h_errnop */
0, /* int p_h_errno */
NULL, /* char * p_in_sighandler */
0, /* char p_sigwaiting */
PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */
{NULL}, /* void ** p_specific[PTHREAD_KEY_1STLEVEL_SIZE] */
{NULL}, /* void * p_libc_specific[_LIBC_TSD_KEY_N] */
0, /* int p_userstack */
NULL, /* void * p_guardaddr */
0, /* size_t p_guardsize */
&__pthread_initial_thread, /* pthread_descr p_self */
0, /* Always index 0 */
0, /* int p_report_events */
{{{0, }}, 0, NULL}, /* td_eventbuf_t p_eventbuf */
ATOMIC_INITIALIZER, /* struct pthread_atomic p_resume_count */
0, /* char p_woken_by_cancel */
NULL /* struct pthread_extricate_if *p_extricate */
};
static void pthread_initialize(void)
{
struct sigaction sa;
sigset_t mask;
struct rlimit limit;
int max_stack;
//__pthread_initial_thread_bos: Limit between the stack of the initial thread (above) and the
//stacks of other threads (below). Aligned on a STACK_SIZE boundary.
/* 如果初始化工作已完成,则退出 */
if (__pthread_initial_thread_bos != NULL) return;
#ifdef TEST_FOR_COMPARE_AND_SWAP
/* Test if compare-and-swap is available */
__pthread_has_cas = compare_and_swap_is_available();
#endif
/* For the initial stack, reserve at least STACK_SIZE bytes of stack
below the current stack address, and align that on a
STACK_SIZE boundary. */
__pthread_initial_thread_bos =
(char *)(((long)CURRENT_STACK_FRAME. - 2 * STACK_SIZE) & ~(STACK_SIZE - 1));
/* 对initial_thread.pid赋值:主进程pid */
__pthread_initial_thread.p_pid = __getpid();
/* If we have special thread_self processing, initialize that for the
main thread now. */
#ifdef INIT_THREAD_SELF
INIT_THREAD_SELF(&__pthread_initial_thread, 0);
#endif
/* 共享主进程errno/h_errno.*/
__pthread_initial_thread.p_errnop = &_errno;
__pthread_initial_thread.p_h_errnop = &_h_errno;
/* Play with the stack size limit to make sure that no stack ever grows
beyond STACK_SIZE minus two pages (one page for the thread descriptor
immediately beyond, and one page to act as a guard page). */
getrlimit(RLIMIT_STACK, &limit); // 获取STACK limit
max_stack = STACK_SIZE - 2 * __getpagesize();
if (limit.rlim_cur > max_stack) {
limit.rlim_cur = max_stack;
setrlimit(RLIMIT_STACK, &limit);
}
#ifdef __SIGRTMIN
/* Initialize real-time signals. */
init_rtsigs ();
#endif
/* Setup signal handlers for the initial thread.
Since signal handlers are shared between threads, these settings
will be inherited by all other threads. */
// 设置initial thread信号处理函数 其他线程都将继承
#ifndef __i386__
sa.sa_handler = pthread_handle_sigrestart;
#else
sa.sa_handler = (__sighandler_t) pthread_handle_sigrestart;
#endif
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
__sigaction(__pthread_sig_restart, &sa, NULL);
#ifndef __i386__
sa.sa_handler = pthread_handle_sigcancel;
#else
sa.sa_handler = (__sighandler_t) pthread_handle_sigcancel;
#endif
sa.sa_flags = 0;
__sigaction(__pthread_sig_cancel, &sa, NULL);
if (__pthread_sig_debug > 0) {
sa.sa_handler = pthread_handle_sigdebug;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
__sigaction(__pthread_sig_debug, &sa, NULL);
}
/* Initially, block __pthread_sig_restart. Will be unblocked on demand. */
sigemptyset(&mask);
sigaddset(&mask, __pthread_sig_restart);
sigprocmask(SIG_BLOCK, &mask, NULL);
/* Register an exit function to kill all other threads. */
/* Do it early so that user-registered atexit functions are called
before pthread_exit_process. */
__on_exit(pthread_exit_process, NULL);
}
// 下面是__pthread_manager_thread 的初始化
/* Descriptor of the manager thread; none of this is used but the error
variables, the p_pid and p_priority fields,
and the address for identification. */
struct _pthread_descr_struct __pthread_manager_thread = {
NULL, /* pthread_descr p_nextlive */ // 这两个值为空!
NULL, /* pthread_descr p_prevlive */
NULL, /* pthread_descr p_nextwaiting */
NULL, /* pthread_descr p_nextlock */
0, /* int p_tid */
0, /* int p_pid */
0, /* int p_priority */
&__pthread_handles[1].h_lock, /* struct _pthread_fastlock * p_lock */
0, /* int p_signal */
NULL, /* sigjmp_buf * p_signal_buf */
NULL, /* sigjmp_buf * p_cancel_buf */
0, /* char p_terminated */
0, /* char p_detached */
0, /* char p_exited */
NULL, /* void * p_retval */
0, /* int p_retval */
NULL, /* pthread_descr p_joining */
NULL, /* struct _pthread_cleanup_buffer * p_cleanup */
0, /* char p_cancelstate */
0, /* char p_canceltype */
0, /* char p_canceled */
&__pthread_manager_thread.p_errno, /* int *p_errnop */
0, /* int p_errno */
NULL, /* int *p_h_errnop */
0, /* int p_h_errno */
NULL, /* char * p_in_sighandler */
0, /* char p_sigwaiting */
PTHREAD_START_ARGS_INITIALIZER, /* struct pthread_start_args p_start_args */
{NULL}, /* void ** p_specific[PTHREAD_KEY_1STLEVEL_SIZE] */
{NULL}, /* void * p_libc_specific[_LIBC_TSD_KEY_N] */
0, /* int p_userstack */
NULL, /* void * p_guardaddr */
0, /* size_t p_guardsize */
&__pthread_manager_thread, /* pthread_descr p_self */
1, /* Always index 1 */
0, /* int p_report_events */
{{{0, }}, 0, NULL}, /* td_eventbuf_t p_eventbuf */
ATOMIC_INITIALIZER, /* struct pthread_atomic p_resume_count */
0, /* char p_woken_by_cancel */
NULL /* struct pthread_extricate_if *p_extricate */
};
int __pthread_initialize_manager(void)
{
// 管理线程与其他线程之间的通信是通过管道完成的。
/*在一个进程空间内,管理线程与其他线程之间通过一对"管理管道(manager_pipe[2])"来通讯,
该管道在创建管理线程之前创建,在成功启动了管理线程之后,管理管道的读端和写端分别赋给两个全局变量
__pthread_manager_reader和__pthread_manager_request,之后,每个用户线程都通过
__pthread_manager_request向管理线程发请求,但管理线程本身并没有直接使用__pthread_manager_reader,
管道的读端(manager_pipe[0])是作为__clone()的参数之一传给管理线程的,
管理线程的工作主要就是监听管道读端,并对从中取出的请求作出反应。*/
int manager_pipe[2];
int pid;
struct pthread_request request;
/* 如