Unix系统并发服务进程的控制
2009-11-16史云辉于峰张云成
史云辉 于 峰 张云成
[摘 要]Unix系统的fork函数为并发服务提供可能性,通过辅助控制完善fork函数的使用,可以提高应用系统的稳定性。
[关键词]Unix;并发;fork
一、引言
Unix并发服务器的实现方式有多种:有动态创建服务器进程的方式(TongLink/Easy);有启动固定数量服务进程的方式;还有两者结合,预先启动固定数量的服务进程,然后根据应用过程中的请求情况,动态增加或减少服务进程的方式(Tuxedo)来满足客户需求。以上方式各有自己的特点,以适应不同类型的客户服务请求。但无论采取何种并发服务器形式,Unix内核创建新进程的方法只有一个,即调用fork函数。本文针对fork机制从派生数量及运行时间上进行控制,使之完善,以提高应用系统服务程序的稳定性,更好地为客户服务。
二、fork函数应用简述
对于客户端的请求,一般服务器都使用循环处理方式,并且阻塞在接收客户端请求操作上。当请求到来时,父进程调用fork函数产生一子进程,然后父进程继续接收客户端请求;子进程完成对客户端请求的处理,处理完成后结束子进程。
fork函数在并发服务系统应用的典型流程如下:
while(1){
接收客户端请求:
if ((pid=fork())<0){
错误处理;
else if (pid==0){
子进程处理:
Exit(0);
}
}
该方式虽然实现简单,但是存在以下问题:
1.并发数量问题:并发数量决定了应用系统并发服务的处理能力。Unix系统为每个用户分配的资源是有限制的,当大量的并发请求到达时,可能达到用户进程数量上限,导致产生“系统资源不能临时获得”的错误发生,影响系统的稳定性。综合考虑服务器的其他须处理作业情况,应当为该服务器定制可派生进程的上限,在确保系统稳定运行的前提下,为客户端提供及时、正确的响应。运行时间问题:服务进程可能因某些异常情况产生超时,并且自身不能恢复,应提供一种机制能够监测到处于超时状态的进程,然后将其安全退出,释放资源。
三、fork函数的完善
完善fork机制的设计思想及处理流程:
1.首先在服务程序启动时,应该告诉它最大的进程并发数量和服务最长的超时时间。通过读取配置文件实现,由于父子进程都要使用这两个参数,所以应将其放到共享内存中并辅以信号量进行互斥访问控制。
2.在父进程调用fork函数之前,应检查共享内存记录的处于工作状态的子进程数量是否已经到达预定义的上限:如已达到,则返回相应的错误信
息给客户程序;如未达到,则计数器加一后调用fork函数。实际上,在遍历处于工作状态的子进程时,也要检查其工作时间是否已经超过超时时限;如果超时,则kill该子进程。
3.进入子进程,第一件事情将该进程ID和开始运行时间登记在共享内存中,供父进程检查超时以及杀掉该进程使用。该步操作也可以放在父进程中,调用fork之后进行。放在子进程处理的好处是节省父进程的时间,使之尽快返回,准备处理下一客户请求。
4.子进程退出前的最后一件事情:释放资源,即将共享内存中处于工作状态进程数的计数器减一。
5.应提供查看服务进程状态的功能,通过检索共享内存数据实现。
四、具体技术实现
1.数据结构定义
①进程控制结构定义:置于共享内存的进程状态信息
structs_forlctrl{
pid_tpid;
longnum;
time_tstime;
};
structs_fork_ctrl shm[MaxProcNum+1];
2.结构数组说明:
结构数组的元素个数:允许的最大进程数加一,其中第—个元素父进程使用,其他元素记录子进程信息。
第一个元素shin[0]字段说明:
shm[0].pid:记录父进程ID,供查看使用
shm[0】.num:并发进程上限(配置文件读入)
shin[0].stime:超时时间(配置文件读入)
其他元素字段说明:
shrn[1—MaxProcNum].pid:子进程ID,超时后,可以根据该值Kill子进程
shm[1—MaxProcNum].num:子进程执行次数累计,用于统计分析
shm[1—MaxProcNum].stime:子进程派生时间,纪录该进程运行开始时间,与系统当前时间比较,可知该进程是否处于超时状态
2.函数原型定义
按照设计思想,定义一组实现函数,用于在派生进程前后使用,以提高应用程序的稳定性:
1.in fort_init(char*program,int MaxProcNum,intReleaseTime):初始化函数。按最大进程数加一分配共享内存空间,并设定初试值。第—个参数程序名用来获取IPC资源ID。
2.intforkprealloc():进程预分配函数。遍历共享内存进程结构,如果找到空闲项,则预分配之,进程数加—;如果发现超时进程,则杀掉该进程,并使用该进程位置预分配。该函数属于原子操作,必须使用互斥防问机制(如信号量)控制。
3. int fork_alloc():进程信息分配函数。登记新派生的子进程ID及其启动时间。
4. void fork_free():进程信息释放函数。删除进程登记信息,供父进程继续分配使用。
5.int for_info():进程信息显示函数。显示共享内存登记的进程状状况。
五、完善后的fork调用流程
fork_init(argv[0],MaxProcNum,ReleaseTime):
while(1){
接收客户端请求;
if (fork_prealloc()<0){
并发服务已达处理上限,暂不接受请求;
continue;
}
if ((pid==fork())<0){
错误处理:
else if(pid==0){
fork—alloc();
子进程处理:
fork_free();
exit(0);
}
}(编辑/刘佳)