(1)return
(2)exit
(3)_exit和_Exit
(4)进程的最后一个线程使用return
(5)进程的最后一个线程使用phread_exit
2、一个异常进程结束:
(1)abort
(2)signal
(3)cancellation request ,最后一个线程被取消
3、当一个进程结束时,它的父进程需要知道它是如何结束的,于是需要得到子进程的termination status,parent process可以通过wait 和waitpid两个函数得到。
(1)当parent process 先于child process结束
init process(ID is 1)接手它的child process,kernel搜索所有的进程,看看结束的进程是否是正在运行的进程的父进程,如果是,则将它们的父进程改为init process。
(2)子进程先于父进程结束
kernle保留了所有将要结束的进程的信息,如:process ID,termnation status,a mount of CPU time,这样的话,parent process可以通过wait和waitpid得到termination status。
(3)当一个进程的父进程变成init process时,发生了什么?
无论何时child process结束,init调用wait函数得到termination status。init进程总是可以获得子进程的终止状态。
4、wait和waitpid
当调用这两个函数时,可能发生三种情况:
(1)block——如果它的子进程全部都在运行。
(2)立即返回child process的termination status——如果有子进程终止。
(3)返回error——如果没有子进程。
# include <sys/wait.h>
pid_t wait (int *statloc);
pid_t waitpid (pid_t pid, int *statloc, int options);
两者的区别
(1)wait可能阻塞caller直到一个子进程终止,而waitpid则可以通过选项不会阻塞。
(2)waitpid并不等待第一个终止的进程,它有选项可以控制它所等待的进程。
(3)对于得到的ternimation status我们可以通过四个macos来区分:
1 WIFEXITED(status) //正常终止
2 WIFSIGNALED(status) //异常终止,获取异常信号
3 WIFSTOPPED(status)
4 WIFCONTINUED(status)
(4)waitpid根据pid的不同来获取不同的进程termination status。
1 waitpid可以wait某个特定的process,而wait则是任何的terminated child
2 waitpid是nonblockling //可以使用选项控制为非阻塞
3 waitpid支持WUNTRACED和WCONTINUED //waitpid支持任务控制
备注:当子进程结束的时候,父进程会收到SIGCHLD通知 2.进程可以调用wait/waitpid等待此Signal.为了减少僵尸进程的产生,我们可以采用waitpid而减少wait的使用,虽然僵尸进程可以由init回收
今天看了unix网络编程中的关于wait和waitpid的区别,它采用的验证例子是客户/服务器的连接问题
1.当子进程结束的时候,父进程会收到SIGCHLD通知
2.进程可以调用wait/waitpid等待此Signal
a.当所有子进程都在执行的时候,会block
b.当某个子进程中止,成为zombie的时候,立刻返回
c.如果没有任何子进程,则返回错误
以前曾经学过这两函数的使用,但是没有什么例子可以验证,今天巧遇网络编程,就把这两个函数重新温习一下:
pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid, int *stat_loc, int options);
两个函数的不同在于wait会令调用者阻塞直至某个子进程终止而waitpid则可以通过设置一个选项来设置为非阻塞,另外waitpid并不是等待第一个结束的进程而是等待参数中pid指定的进程。
两个函数中的变量statloc是一个指向int型数据的指针。如果此变量不是NULL,则结束的进程的termination status会被保存在statiloc所指向的内存的区域;如果我们不关心termination status,则可以把statloc置为NULL。
waitpid函数:
如果一个进程有若干个子进程, 那么只要有一个子进程返回, wait就返回. 如果要等待一个指定的子进程, 有两种方法. 第一个种, 早期的UNIX必须调用wait, 然后把返回的pid和期望pid做比较, 如果不是期望的, 把该pid保存起来, 继续调用wait, 直到进程终止. 第二种是使用waitpid.
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *statloc, int options);
函数返回: 若成功则为进程ID, 若出错则为-1.
参数说明:
pid:
pid == -1 等待任一子进程. 这个时候waitpid与wait等效.
pid > 0 等待其ID与pid相等的子进程.
pid == 0 等待其组ID与调用进程的组ID的任一子进程.
pid < -1 等待其组ID等于pid绝对值的任一子进程.
对于waitpid函数, 如果指定的进程或进程组不存在, 或者调用进程没有子进程都会出错.
options:
这个一参数可以让我们进一步控制waitpid的操作, 此参数或者是0, 或者是下列的逐位或常数之一:
WNOHANG: 若由pid指定的子进程并不立即可用, 则waitpid不阻塞, 此时其返回值为0.
WUNTRACED: 若某实现支持作业控制, 则由pid指定的任一子进程状态已暂停, 且其状态自暂停以来还未报告过, 则返回其状态. WIFSTOPPED宏确定返回值是否对应于一个暂停子进程.
下面我们来具体看看wait和waitpid在网络编程中使用的区别:
void sig_chld(int signo)
{
pid_t pid;
int stat;
pid = wait(&stat);
printf("child %d terminated \n",pid);
return;
}
void sig_chld(int signo)
{
pid_t pid;
int stat;
while((pid = waitpid(-1,&stat,WNOHANG))>0)
printf("child %d terminated \n",pid);
return;
}
上面是两段不同的信号处理函数,它们的结果可能相差很多的,下面我们就来看看有哪些区别:
首先我们看看用wait函数产生的效果:
1、下面的输出是在启动客户端和服务器端程序后的ps输出
备注:在客户端程序中,我们连续产生5个服务器连接进程,所以一共6个waitsrv进程
2、下面是在客户端输入ctrl+d后的服务器端输出和ps查询后的结果
我们从上面可以看出利用wait的一个致命的缺点就是只能回收一个子进程,其他的进程由于没有得到回收而变成僵尸进程
下面我们来看看利用waitpid函数的输出结果:
1、启动服务器端和客户端后的ps输出
2、在客户端输入ctrl+d后的服务器端输出和ps的结果
我们可以发现所有服务器的子进程在接受到客户端输入的EOF后,都被回收了!
由此,我们可以发现,为了减少僵尸进程的产生,我们可以采用waitpid而减少wait的使用,虽然僵尸进程可以由init回收
以上的例子程序是采用的unix网络编程中的例子,在此代码就不再详细列出了!
1、这两个方法来自不同的类分别是,sleep来自Thread类,和wait来自Object类。
sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用了b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。
2、最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。一般wait不会加时间限制,因为如果wait线程的运行资源不够,再出来也没用,要等待其他线程调用notify/notifyAll唤醒等待池中的所有线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)可以用时间指定使它自动唤醒过来,如果时间不到只能调用interrupt()强行打断。
Thread.Sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”。
3、使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
synchronized(x){
x.notify()
//或者wait()
}
4、sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常。