并发介绍
如果逻辑控制流在时间上是重叠的,那么它们就是并发的,可以出现在计算机系统的不同层面上,硬件异常处理程序、进程和Unix信号处理程序都是并发的。并发可以看作是操作系统内核用来运行多个应用程序的机制,但是并发不局限于内核。它也可以在应用程序中扮演角色。并发的主要作用有:访问慢速IO设备;与人交互的程序;通过推迟工作以降低延迟;服务多个网络客户端的请求。并发通常可以有三种,基于进程、基于IO多路复用、基于线程。
基于进程的并发
进程是一个程序运行的实例。每一个进程都有自己独立的地址空间,一般情况下,包括文本区域、数据区域和堆栈。文本区域是存储处理器执行的代码;数据区域存储变量和线程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。程序是一个没有生命的实体,只有处理器赋予程序生命,然后由操作系统去执行它,它才能称作一个实体,称为进程。
一个并发服务器的自然构造方法是,在父进程中接受客户端的连接请求,然后创建一个子进程来为每个新客户端提供服务。在连接接受请求之后,服务器派生一个子进程,这个子进程获取服务器描述符表的完整拷贝,子进程关闭它的拷贝的监听描述符,而父进程关闭它的已连接描述符,否则,将永远不会释放已连接描述符的文件表条目,而且由此引发的存储器泄露将最终消耗尽可用的服务器,使系统崩溃。
对于父、子进程间共享状态信息,进程有一个非常清晰的模型:共享文件表,但是不共享用户地址空间。进程有独立的地址空间既是优点,也是缺点,这样一来,一个进程不可能不小心覆盖另一个进程的虚拟存储器;另一方面,独立的地址空间使得进程之间共享信息变得更加困难。另外,基于进程的设计另一个缺点是,往往运行比较慢,因为进程控制和IPC开销比较高。
Unix C实现基于进程的小型并发服务器
本程序在Unix 环境下用C语言开发的,并且调用了csapp.h的现成头文件。
//本程序是一个基于多进程的并发echo服务器,父进程派生一个子进程来处理每个新的连接请求 #include "csapp.h" //echo程序,用于向客户端发送响应主体 void echo(int connfd) { int n; char buf[MAXLINE]; rio_t rio; rio_readinitb(&rio,connfd); //带缓冲的读取函数 while((n=rio_readlineb(&rio,buf,MAXLINE))>0) { //向连接符写入内容 printf("server received %d bytes \n",n); rio_writen(connfd,buf,n); } } //信号处理函数,用于处理僵死进程,即回收已经终止的进程给系统带来的资源占用 void sigchld_handler(int sig) { //-1代表回收父进程的子进程组,由于unix信号是不排队的,因此必须准备好回收多个僵死子进程的准备 while(waitpid(-1,0,WNOHANG)>0) ; return; } int main(int argc,char **argv) { int listenfd,connfd,port; socklen_t clientlen=sizeof(struct sockaddr_in); struct sockaddr_in clientaddr; if(argc!=2) { fprintf(stderr,"usage :%s <port>\n",argv[0]); exit(0); } //将字符串转化为整型,端口号 port=atoi(argv[1]); //启动信号处理函数监听器 signal(SIGCHLD,sigchld_handler); //启动服务器监听描述符 listenfd=open_listenfd(port); while(1) { //已连接描述符,接受连接 connfd=accept(listenfd,(SA *)&clientaddr,&clientlen); //开辟子进程,用于处理连接请求 if(fork()==0) { //由于子进程共享父进程的所有资源,因此先关闭监听描述符,再调用请求体命令,然后关闭已连接描述符,并且正常终止此进程 close(listenfd); echo(connfd); close(connfd); exit(0); } //关闭父进程的已连接描述符 close(connfd); } }
使用telnet来测试程序:
[email protected]:~$ telnet localhost 8080 Trying 127.0.0.1... Connected to localhost. Escape character is ‘^]‘. hello hello
[email protected]:/home/zzw/doc_main/CProgram/Concurrency# ./echoserverp.o 8080 server received 7 bytes