版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、并发服务器目录n服务器分类技术n进程与线程n多进程服务器n多线程服务器并发服务器服务器分类n按连接类型分类n面向连接的服务器(如tcp)n面向无连接的服务器(如udp)n按处理方式分类n迭代服务器n并发服务器迭代服务器 vs. 并发服务器绑定地址监听连接接收连接处理连接断开连接接收请求处理请求返回响应绑定地址监听连接接收连接创建子进程关闭连接套接字处理连接关闭连接套接字终止子进程关闭监听套接字服务器主进程服务器子进程TCP迭代服务器TCP并发服务器“进程”基本概念n进程定义了一个计算的基本单元,可以认为是一个程序的一次运行。它是一个动态实体,是独立的任务。它拥有独立的地址空间、执行堆栈、文件描
2、述符等。n每个进程拥有独立的地址空间,进程间正常情况下,互不影响,一个进程的崩溃不会造成其他进程的崩溃。n当进程间共享某一资源时,需注意两个问题:同步问题和通信问题。创建进程#include #include pid_t fork(void)返回:父进程中返回子进程的进程返回:父进程中返回子进程的进程ID, 子进程返回子进程返回0, -1出错出错nfork后,子进程和父进程继续执行后,子进程和父进程继续执行fork()函数后的指令。()函数后的指令。子进程是父进程的副本。子进程拥有父进程的数据空间、子进程是父进程的副本。子进程拥有父进程的数据空间、堆栈的副本。但父、子进程并不共享这些存储空间部
3、分。堆栈的副本。但父、子进程并不共享这些存储空间部分。如果代码段是只读的,则父子进程共享代码段。如果父子如果代码段是只读的,则父子进程共享代码段。如果父子进程同时对同一文件描述字操作,而又没有任何形式的同进程同时对同一文件描述字操作,而又没有任何形式的同步,则会出现混乱的状况;步,则会出现混乱的状况;n父进程中调用父进程中调用fork之前打开的所有描述字在函数之前打开的所有描述字在函数fork返回返回之后子进程会得到一个副本。之后子进程会得到一个副本。fork后,父子进程均需要将后,父子进程均需要将自己不使用的描述字关闭。自己不使用的描述字关闭。创建进程(cont.)#include #inc
4、lude pid_t vfork(void);n该系统调用基本上与该系统调用基本上与fork相同,在相同,在BSD3.0中开始出现,主中开始出现,主要为了解决要为了解决fork昂贵的开销。昂贵的开销。n两者的基本区别在于当使用两者的基本区别在于当使用vfork()创建新进程时,父进程创建新进程时,父进程将被暂时阻塞,而子进程则可以借用父进程的地址空间,直将被暂时阻塞,而子进程则可以借用父进程的地址空间,直到子进程退出,至此父进程才继续执行。到子进程退出,至此父进程才继续执行。终止进程终止进程n进程的终止存在两个可能:进程的终止存在两个可能:n父进程先于子进程终止(父进程先于子进程终止(init
5、进程领养)进程领养)n子进程先于主进程终止子进程先于主进程终止n对于后者,系统内核为子进程保留一定的状态对于后者,系统内核为子进程保留一定的状态信息:进程信息:进程ID、终止状态、终止状态、CPU时间等;当时间等;当父进程调用父进程调用wait或或waitpid函数时,获取这些函数时,获取这些信息;(什么叫信息;(什么叫“僵尸进程僵尸进程”?)?)n当子进程正常或异常终止时,系统内核向其父当子进程正常或异常终止时,系统内核向其父进程发送进程发送SIGCHLD信号;缺省情况下,父进信号;缺省情况下,父进程忽略该信号,或者提供一个该信号发生时即程忽略该信号,或者提供一个该信号发生时即被调用的函数。
6、被调用的函数。终止进程(续)终止进程(续)#include void exit(int status);n本函数终止调用进程。关闭所有子进程打开的描述本函数终止调用进程。关闭所有子进程打开的描述符,向父进程发送符,向父进程发送SIGCHLD信号,并返回状态。信号,并返回状态。获取子进程终止信息获取子进程终止信息#include #include pid_t wait(int *stat_loc); 返回:终止子进程的返回:终止子进程的ID成功;成功;-1出错;出错;stat_loc存储存储子进程的终止状态(一个整数);子进程的终止状态(一个整数);n如果没有终止的子进程,但是有一个或多个正在执
7、如果没有终止的子进程,但是有一个或多个正在执行的子进程,则该函数将堵塞,直到有一个子进程行的子进程,则该函数将堵塞,直到有一个子进程终止或者终止或者wait被信号中断时,被信号中断时,wait返回。返回。n当调用该系统调用时,如果有一个子进程已经终止,当调用该系统调用时,如果有一个子进程已经终止,则该系统调用立即返回,并释放子进程所有资源。则该系统调用立即返回,并释放子进程所有资源。获取子进程终止信息获取子进程终止信息使用wait()函数可能会出现一个问题SIGCHLD服务器父进程服务器父进程服务器子进程服务器子进程服务器子进程服务器子进程服务器子进程服务器子进程客户客户FINFINFINSI
8、GCHLDSIGCHLD由于由于linux信号不排队,在信号不排队,在SIGCHLD信号同时到来后,信信号同时到来后,信号处理程序中调用了号处理程序中调用了wait函数,其只执行一次,这样将留函数,其只执行一次,这样将留下下2个僵尸进程。可以使用个僵尸进程。可以使用waitpid函数解决这个问题。函数解决这个问题。获取子进程终止信息获取子进程终止信息pid_t waitpid(pid_t pid, int *stat_loc, int options); 返回:终止子进程的返回:终止子进程的ID成功;成功;-1出错;出错;stat_loc存储存储子进程的终止状态;子进程的终止状态;n当当pid
9、=-1,option=0时,该函数等同于时,该函数等同于wait,否则,否则由参数由参数pid和和option共同决定函数行为,其中共同决定函数行为,其中pid参参数意义如下:数意义如下:n-1:要求知道任何一个子进程的返回状态(等待第一:要求知道任何一个子进程的返回状态(等待第一个终止的子进程);个终止的子进程);n0:要求知道进程号为:要求知道进程号为pid的子进程的状态;的子进程的状态;n-1:要求知道进程号为要求知道进程号为pid的绝对值的子进程的终止的绝对值的子进程的终止状态状态 nOptions最常用的选项是最常用的选项是WNOHANG,它通它通知内核在没有已终止进程时不要堵塞。知
10、内核在没有已终止进程时不要堵塞。获取子进程终止信息(获取子进程终止信息(cont.)n调用调用wait或或waitpid函数时,正常情况下,函数时,正常情况下,可能会有以下几种情况:可能会有以下几种情况:n阻塞(如果其所有子进程都还在运行);阻塞(如果其所有子进程都还在运行);n获得子进程的终止状态并立即返回(如果获得子进程的终止状态并立即返回(如果一个子进程已终止,正等待父进程存取其一个子进程已终止,正等待父进程存取其终止状态);终止状态);n出错立即返回(如果它没有任何子进程)出错立即返回(如果它没有任何子进程)多进程并发服务器状态图服务器客户connect()函数listenfd客户/服
11、务器状态图(调用accept函数时)连接请求多进程并发服务器状态图(cont.)服务器服务器客户客户connect()函数listenfd客户/服务器状态图(调用accept函数后)connfd连接建立多进程并发服务器状态图(cont.)服务器(父进程)服务器(父进程)客户客户connect()函数函数listenfd客户/服务器状态图(调用fork函数后)connfd连接建立连接建立服务器(子进程)listenfdconnfdfork()函数多进程并发服务器状态图(cont.)服务器(父进程)服务器(父进程)客户客户connect()函数函数listenfd客户/服务器状态图(父进程关闭连接
12、套接字,子进程关闭监听套接字)连接建立服务器(子进程)服务器(子进程)connfd多进程并发服务器实例n该实例包括服务器程序和客户程序,具体功能如下:该实例包括服务器程序和客户程序,具体功能如下:n服务器等待接收客户的连接请求,一旦连接成功则显服务器等待接收客户的连接请求,一旦连接成功则显示客户地址,接着接收客户端的名称并显示;然后接示客户地址,接着接收客户端的名称并显示;然后接收来自该客户的字符串,每当收到一个字符串时,显收来自该客户的字符串,每当收到一个字符串时,显示该字符串,并将字符串按照恺撒密码的加密方式示该字符串,并将字符串按照恺撒密码的加密方式(K=3)进行加密,再将加密后的字符发
13、回客户端;)进行加密,再将加密后的字符发回客户端;之后,继续等待接收该客户的信息,直到客户关闭连之后,继续等待接收该客户的信息,直到客户关闭连接。要求服务器具有同时处理多个客户请求的能力。接。要求服务器具有同时处理多个客户请求的能力。n客户首先与相应的服务器建立连接;接着接收用户输客户首先与相应的服务器建立连接;接着接收用户输入的客户端名称,并将其发送给服务器;然后继续接入的客户端名称,并将其发送给服务器;然后继续接收用户输入的字符串,再将字符串发送给服务器,同收用户输入的字符串,再将字符串发送给服务器,同时接收服务器发回的加密后的字符串并显示。之后,时接收服务器发回的加密后的字符串并显示。之
14、后,继续等待用户输入字符串,直到用户输入继续等待用户输入字符串,直到用户输入Ctrl+D,客,客户关闭连接并退出。户关闭连接并退出。 多进程服务器的问题多进程服务器的问题 虽然多进程并发服务器模式很多年来都使用得虽然多进程并发服务器模式很多年来都使用得很好,但使用很好,但使用fork生成子进程存在一些问题。生成子进程存在一些问题。n首先,首先,fork占用大量的资源,内存映像要从父进程占用大量的资源,内存映像要从父进程拷贝到子进程,所有描述符要在子进程中复制等。拷贝到子进程,所有描述符要在子进程中复制等。虽然当前采用写时拷贝(虽然当前采用写时拷贝(copy-on-write)技术,)技术,将真
15、正的拷贝推迟到子进程有写操作时,但将真正的拷贝推迟到子进程有写操作时,但fork仍仍然需要占用大量资源。然需要占用大量资源。n其次,其次,fork子进程后,需要用进程间通信(子进程后,需要用进程间通信(IPC)在父子进程间传递信息。由于子进程从一开始就有在父子进程间传递信息。由于子进程从一开始就有父进程数据空间及所有描述符的拷贝,所以父进程数据空间及所有描述符的拷贝,所以fork之之前的信息容易传递,但是从子进程返回信息给父进前的信息容易传递,但是从子进程返回信息给父进程就需要做很多工作。程就需要做很多工作。 “线程线程”基本概念基本概念n线程是进程内的独立执行实体和调度单元,又称为线程是进程
16、内的独立执行实体和调度单元,又称为“轻量级轻量级”进程(进程(lightwight process);创建线程比进程快);创建线程比进程快10100倍。一个进程内的所有线程共享相同的内存空间、倍。一个进程内的所有线程共享相同的内存空间、全局变量等信息(这种机制又带来了同步问题)。而且它们全局变量等信息(这种机制又带来了同步问题)。而且它们还共享以下信息:还共享以下信息: 共享信息共享信息 私有信息私有信息n进程指令进程指令 线程线程IDn大多数数据大多数数据 寄存器集合(包括程序计数器和栈指针)寄存器集合(包括程序计数器和栈指针)n打开的文件描述字打开的文件描述字栈(用于存放局部变量)栈(用于
17、存放局部变量)n信号处理程序和信号处置信号处理程序和信号处置errorn当前工作目录当前工作目录信号掩码信号掩码n用户用户ID和组和组ID优先级优先级线程调用函数(1)#include int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*func)(void *), void *arg); 返回:成功时为返回:成功时为0;出错时为正的;出错时为正的Exxx值值n当一个程序开始运行时,系统会创建一个初始线程或主线程当一个程序开始运行时,系统会创建一个初始线程或主线程的单个线程。额外线程由上述函数创建;的单个
18、线程。额外线程由上述函数创建;n新线程由线程新线程由线程id标识:标识:tid,新线程的属性,新线程的属性attr包括:优先级、包括:优先级、初始栈大小、是否应该是守护线程等等。线程的执行函数和初始栈大小、是否应该是守护线程等等。线程的执行函数和调用参数分别是:调用参数分别是:func和和arg;n由于线程的执行函数的参数和返回值类型均为由于线程的执行函数的参数和返回值类型均为void *,因此,因此可传递和返回指向任何类型的指针;可传递和返回指向任何类型的指针;n常见的返回错误值:常见的返回错误值:EAGAIN:超过了系统线程数目的限制。:超过了系统线程数目的限制。ENOMEN:没有足够的内
19、存产生新的线程。:没有足够的内存产生新的线程。EINVAL:无效的属性:无效的属性attr值。值。线程函数调用(线程函数调用(2)#inlcude int pthread_join(pthread_t tid, void *status); 返回:成功时为返回:成功时为0;出错时为正的;出错时为正的Exxx值,不值,不设置设置errorn该函数类似与该函数类似与waitpid函数,但必须指定等函数,但必须指定等待线程的待线程的ID,该函数不能等待任意一个线程,该函数不能等待任意一个线程结束(如结束(如wait););n被等待线程必须是当前进程的成员,并且不被等待线程必须是当前进程的成员,并且不
20、是是分离的线程分离的线程和和守护线程守护线程。pthread_t pthread_self(void); 返回:调用线程的线程返回:调用线程的线程id;线程函数调用(3)#include int pthread_detach(pthread_t tid) 返回:成功时为返回:成功时为0;出错时为正;出错时为正Exxx值;值;n线程或者是可汇合的(线程或者是可汇合的(joinable)(默认),或者)(默认),或者是脱离的(是脱离的(detached)。当可汇合的线程终止时,)。当可汇合的线程终止时,其线程其线程id和退出状态将保留,直到另外一个线程调用和退出状态将保留,直到另外一个线程调用pt
21、hread_join。脱离的线程则像守护进程,当它终。脱离的线程则像守护进程,当它终止时,释放所有资源,我们不能等待它终止。止时,释放所有资源,我们不能等待它终止。n该函数将指定的线程变为脱离的。该函数将指定的线程变为脱离的。 pthread_detach(pthread_self()();线程函数调用(线程函数调用(4)#include void pthread_exit(void *status); 无返回值;无返回值;n如果线程为可汇合的,将保留线程如果线程为可汇合的,将保留线程id和退出状态供和退出状态供pthread_join()函数调用()函数调用;n指针指针status:指向线程
22、的退出状态。不能指向一个局部:指向线程的退出状态。不能指向一个局部变量,因为线程终止时其所有的局部变量将被撤销;变量,因为线程终止时其所有的局部变量将被撤销;n还有其他两种方法可使线程终止还有其他两种方法可使线程终止n启动线程的函数(启动线程的函数(pthread_create的第的第3个参数)个参数)返回。其返回值便是线程的终止状态;返回。其返回值便是线程的终止状态;n如果进程的如果进程的main函数返回,或者当前进程中,任一函数返回,或者当前进程中,任一线程调用了线程调用了exit()函数,将终止该进程中所有线()函数,将终止该进程中所有线程。程。给新线程传递参数n由于同一个进程内的所有线
23、程共享内存和变量,因此在传递参数时需作特殊处理,下面参考如下几种方法:n传递参数的普通方法n通过指针传递参数n通过分配arg的空间来传递参数传递参数的普通方法n由于线程创建函数只允许传递一个参数,因此当需要传递多个数据时,应首先将这些数据由于线程创建函数只允许传递一个参数,因此当需要传递多个数据时,应首先将这些数据封装在一个结构中。封装在一个结构中。void *start_routine(void *arg);struct ARG int connfd;int other;int main() ARG arg;While(1) if(connfd = accept(sockfd,NULL,NU
24、LL)= -1) arg.connfd = connfd; pthread_create(&tid, NULL, start_routine, (void *)&arg); void *start_routine(void *arg) ARG info;info.connfd = (ARG *)arg) - connfd;info.other = (ARG *)arg) - other;/这种方法有问题,对一个客户可以工作,但多个客户则可能出现问题。这种方法有问题,对一个客户可以工作,但多个客户则可能出现问题。 通过指针传递参数n这种方法首先将要传递的数据转换成通用指针类型,然
25、后传递给新线程,这种方法首先将要传递的数据转换成通用指针类型,然后传递给新线程,新线程再将其还原成原数据类型:新线程再将其还原成原数据类型:void *start_routine(void *arg);int main(void) int connfd;pthread_create(&tid, NULL, start_routine, (void *)connfd);void *start_routine(void *arg) int connfd;connfd =(int ) arg;n这种方法虽然简单,但却有这种方法虽然简单,但却有。如:要求。如:要求arg的类型必须能的类型必须能
26、被正确地转换成通用指针类型,而且可传递的参数只有一个。被正确地转换成通用指针类型,而且可传递的参数只有一个。通过分配arg的空间来传递n主线程首先为每个新线程分配存储主线程首先为每个新线程分配存储arg的空间,再将的空间,再将arg传递给新线程传递给新线程使用,新线程使用完后要释放该空间。使用,新线程使用完后要释放该空间。void *start_routine(void *arg);int main(void) ARG * arg;int connfd; loop if(connfd = accept(sockfd,NULL,NULL)= -1) arg = new ARG;arg - con
27、nfd = connfd;pthread_create(&tid, NULL, start_routine, (void *)arg);线程安全问题n线程安全问题是一个非常复杂的问题。简单地说,就是多个线程在操作共享数据时出现的混乱情况,这种情况可能导致不可预测的后果。n解决线程安全问题的方法主要有两种:一是使用线程安全函数:posix定义的以”_r”结尾的函数,二是使用线程专用数据(TSD)。线程专用数据:TSDn从上例可以看出,在多线程环境中,应避免使用静态变量。在linux环境中,用线程专用数据TSD取代静态变量。它类似于全局数据,只不过它是线程私有的,是以线程为界限的。TSD是
28、定义线程私有全局数据的唯一方法。n每个TSD由进程内唯一的关键字(key)来标志,用这个关键字,线程可以存取线程私有的数据。pthread_key_create函数#include int pthread_key_create(pthread_key_t *key, void (* destructor)(void *value); 返回值:正常执行后返回返回值:正常执行后返回0,否则返回错误码,否则返回错误码n该函数在进程内部分配一个标志该函数在进程内部分配一个标志TSD的关键字,关的关键字,关键字是进程内部唯一的,所有线程在创建时关键字键字是进程内部唯一的,所有线程在创建时关键字值是值是NULL。nkey指向创建的关键字;指向创建的关键字;destructor是一个可选的是一个可选的析构函数,用于析构函数,用于每个每个线程终止线程终止时调用该析
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年度个人房屋买卖合同电子档案管理服务协议3篇
- 二零二五年度海外投资合作出海合同示范文本4篇
- 2025年外研版七年级化学下册月考试卷含答案
- 二零二五年度建筑工程脚手架安装与维护合同2篇
- 2025年华东师大版六年级数学下册阶段测试试卷含答案
- 2025年上外版九年级地理下册阶段测试试卷含答案
- 基于2025年度物流趋势的绿色物流合作协议3篇
- 二零二五年智能安防系统设计与集成合同详尽条款2篇
- 项目管理案例研究-洞察分析
- 2025年北师大版二年级数学上册阶段测试试卷
- 2023年上海英语高考卷及答案完整版
- 西北农林科技大学高等数学期末考试试卷(含答案)
- 金红叶纸业简介-2 -纸品及产品知识
- 《连锁经营管理》课程教学大纲
- 《毕淑敏文集》电子书
- 颈椎JOA评分 表格
- 员工岗位能力评价标准
- 定量分析方法-课件
- 朱曦编著设计形态知识点
- 110kV变电站工程预算1
- 某系统安全安全保护设施设计实施方案
评论
0/150
提交评论