四、基于多任务的并发服务器

具有代表性的并发服务器端实现模型和发:
1、多进程服务器:通过创建多个进程提供服务 。
2、多路复用服务器:通过捆绑并统一管理I/O对象提供服务 。
3、多线程服务器:通过生成与客户端等量的线程提供服务 。
一、进程概念及应用
进程:“占用内存空间的正在运行的程序” 。
从操作系统的角度看,进程是程序流的基本单位,若创建多个进程,则操作系统将同时运行 。有时一个程序运行过程中也会产生对个进程 。接下来要创建的多进程服务器就是其中的代表 。编写服务器端前,先了解一下通过程序创建进程的方法 。
1、进程ID
无论进程是如何创建的,所有进程都会从操作系统分配到ID,此ID称为“进程ID”,其值是大于2的整数,1要分配给操作系统启动后的首个进程 。
通过命令ps au 查看进程信息

四、基于多任务的并发服务器

文章插图
2、通过调用fork函数创建进程
创建进程的方法有很多,下面介绍用于创建多进程服务器端的fork函数
#include /* Clone the calling process, creating an exact copy.Return -1 for errors, 0 to the new process,and the process ID of the new process to the old process.*/extern __pid_t fork (void) __THROWNL;
成功时返回进程ID,失败时返回-1 。for函数将创建调用的进程副本,复制正在运行的,调用fork函数的进程 。两个进程都将执行fork函数调用后的语句 。但由于通过同一个进程、复制相同的内存空间,之后的程序流要根据fork函数的返回值加以区分 。
父进程:fork函数返回子进程ID
子进程:fork函数返回0
以时间代码为例
【四、基于多任务的并发服务器】 1 #include2 #include 34 int gval = 10; 5 int main(int argc, char *argv[]) { 6pid_t pid; 7int lval = 20; 8gval ++, lval += 5; 9 10pid = fork();11if(pid == 0)12gval += 2, lval += 2;13else14gval -= 2, lval -= 2;15if(pid == 0)16printf("Child Proc: [%d, %d] \n",gval, lval);17else18printf("Parent Proc: [%d, %d] \n",gval, lval);19return 0;20 }
View Code
运行结果如下:
两个进程将独立运行第十行之后的代码 。第十行之前gval和lval的值分别是11和25 。
二、进程和僵尸进程
文件操作中,关闭文件和打开文件同等重要 。同样,在进程中,销毁进程和创建进程也同等重要,如果为认真对待进程销毁,将有可能变成僵尸进程 。
1、僵尸进程
进程完成工作后(执行完main函数中的程序后)应被销毁,但有时这些进程将变成僵尸进程,占用系统中的重要资源,这中状态下的进程称作“僵尸进程”,这也是给系统带来负担的原因之一 。
2、产生僵尸进程的原因
利用如下两个示例展示调用fork函数产生子进程的终止方法:
①传递参数并调用exit函数
②main函数中执行语句并返回值
想exit函数传递的参数值和main函数的语句返回的值都会传递给操作系统,而操作系统不会销毁子进程,直到把这些值传递给产生该子进程的父进程 。处在这种状态下的进程就是僵尸进程 。此僵尸进程该怎么被销毁呢?
“应该向创建子进程的父进程传递子进程的exit参数值或语句的返回值 。”
操作系统不会主动把这些值传递给父进程,只有父进程主动发起请求(调用函数)时,操作系统才会传递该值 。如果父进程未主动要求获取子进程的介绍状态值,操作系统将一直保持,并让子进程长时间处于僵尸进程状态 。
下面代码将创建僵尸进程:
1 #include2 #include 34 int main(int agrc, char* argv[]) { 5pid_t pid = fork(); 6if(pid == 0) { 7puts("Hi, I am a child process"); 8} else { 9printf("Child Process ID :%d \n", pid);10sleep(30);11}12if(pid == 0)13puts("End child process");14else15puts("End parent process");16return 0;17 }