做个网站要多少钱苏州企业网站推广
- 作者: 五速梦信息网
- 时间: 2026年04月18日 10:03
当前位置: 首页 > news >正文
做个网站要多少钱,苏州企业网站推广,微信公众平台官网登录入口网页版,建设婚纱摄影网站的重要性具有代表性的并发服务器端实现模型和方法#xff1a; 多进程服务器#xff1a;通过创建多个进程提供服务。✔ 多路复用服务器#xff1a;通过捆绑并统一管理I/O对象提供服务。 多线程服务器#xff1a;通过生成与客户端等量的线程提供服务。 目录
- 进程的概念及应用 1.… 具有代表性的并发服务器端实现模型和方法 多进程服务器通过创建多个进程提供服务。✔ 多路复用服务器通过捆绑并统一管理I/O对象提供服务。 多线程服务器通过生成与客户端等量的线程提供服务。 目录
- 进程的概念及应用 1.1 什么是进程 1.2 创建进程 1.2.1 进程ID 1.2.2 fork函数创建进程 1.2.3 僵尸进程为什么要进行进程销毁 1.2.4 wait函数销毁进程阻塞 1.2.5 waitpid函数销毁进程无阻塞 1.3 进程间的通信 1.3.1 通过管道PIPE实现进程间的通信
- 信号处理 2.1 signal函数旧版本只适用于旧版本UNIX系统 2.2 sigaction函数新版本所有UNIX操作系统都一样更稳定
- 实现简单的多进程服务端 3.1 代码 3.2 fork函数复制文件操作符 3.3 多进程可服务器端的缺点
- 分割TCP的I/O程序实现多进程客户端的一种模型 1. 进程的概念及应用 1.1 什么是进程 进程占用内存空间的正在运行的程序。 从操作系统的角度看进程是程序流的基本单位。若创建多个进程则操作系统将同时运行。有时一个程序运行过程中也会产生多个进程多进程服务器端就是其中的代表。 CPU核的个数与进程数 拥有2个运算设备的CPU称作双核CPU拥有4个运算器的称作四核CPU。也就是说1个CPU可能包含有多个运算设备(核)核的个数与可同时运行的进程数相同。如果进程数超过了核那么进程将分时使用CPU资源但因为CPU运算很快所以我们会觉得所有进程都同时在运行。 1.2 创建进程 1.2.1 进程ID 每个进程都会从操作系统中分配到一个ID这个ID就是“进程ID”进程ID的值为大于2的整数因为1要分配给操作系统启动后的首个进程协助操作系统的进程因此用户无法得到ID为1的进程。 Linux中查看所有进程的命令: ps au 1.2.2 fork函数创建进程 #includeunistd.hpid_t fork(); 成功返回进程ID失败返回-1 fork函数 将创建基于当前运行的调用fork函数的进程的副本内存空间内容完全相同且互相独立。此进程为父进程其副本为子进程。两个进程都将执行fork函数调用后的语句(准确来说就是从这个fork函数的返回值开始)之后的程序流中通过fork函数的返回值来区分当前执行的是子进程还是父进程。fork函数只会复制属于进程的资源而非操作系统的资源。 父进程fork函数返回子进程ID 子进程fork函数返回0 如图 从复制发生点开始父进程的所有变量的值在复制发生点处时是什么值子进程也就会是什么值之后两个进程之间的变量值互不影响如图所示最终结果 父进程gval11,lval26 子进程gval12,lval25 1.2.3 僵尸进程为什么要进行进程销毁 如果在子进程创建和执行了销毁后父进程没有主动要求获取子进程的结束状态值那么子进程就会成为僵尸进程此时的进程只是一个空壳只保留了一些pid退出状态运行时间等数据而其内存空间已经在return和exit的时候释放了这就是僵尸进程。 僵尸进程产生的原因 子进程通过两种方式来终止一是传递参数并调用exit函数二是main函数找那个执行return语句并返回值。这两种方式返回的值都会传递给操作系统但操作系统不会销毁子进程而是直到把这些值传递给产生该子进程的父进程之后才会进行销毁。在销毁之前这期间状态下的进程就是僵尸进程。所以也可得知如果要终止子进程那么就必须向父进程传递exit参数值或return语句的返回值。 那么子进程如何向父进程传递exit参数值或return语句的返回值来终止自己 答通过父进程主动发起请求来调用。如果父进程没有主动要求获取子进程的结束状态值那么操作系统将一直保存子进程。 为什么要销毁僵尸进程 答僵尸进程虽然不占用内存空间但因其信息还在会占用进程号而系统所能拥有的进程号是有限的所以如果有大量僵尸进程出现的话则会因为进程号不够用而导致不能创建新的进程。 1.2.4 wait函数销毁进程阻塞 #includesys/wait.hpid_t wait(int* statloc); //statloc指向的内存空间存放有子进程的exit参数值或return语句的返回值等其它信息 成功返回终止的子进程ID失败返回-1 statloc指向的内存空间不仅仅存放有子进程的exit参数值或return语句的返回值还有其它信息需要通过如下宏进行分离 WIFEXITED(int statue); 子进程正常终止返回1真。 WEXITSTATUS(int statue); 返回子进程的返回值。 所以使用wait函数的标准流程 int status;wait(status);if(WIFEXITED(status)) //如果正常终止 {std::cout子进程正常终止!std::endl;std::cout子进程返回值:WEXITSTATUS(status)std::endl; } 注意调用wait函数时如果没有子进程要终止那么程序将阻塞住直到有任意一个子进程终止。 1.2.5 waitpid函数销毁进程无阻塞 #includesys/wait.hpid_t waitpid( pid_t pid, //如果为-1那么和wait函数一样等待任意一个子进程终止 int* statloc, //同wait函数一样 int options //传递头文件中声明的常量WNOHANG表示即使没有子进程终止//也不会阻塞程序执行只是返回0并退出函数 ); 成功返回终止的子进程ID(或0)失败返回-1 注意返回0的情况是没有子进程终止。 1.3 进程间的通信 进程间的通信就是让两个进程间可以交换数据。 1.3.1 通过管道PIPE实现进程间的通信 管道与套接字一样是属于操作系统的而非进程资源所以在执行fork时不会被复制。 创建管道的函数 #includeunistd.h int pipe(int filedes[2]); //filedes[0]:通过管道接收数据时使用的文件描述符即管道出口 //filedes[1]:通过管道传输数据时使用的文件描述符即管道入口 成功返回0失败返回-1 父进程调用该函数创建管道时会同时获取对应出入口的文件描述符此时父进程可以读写同一管道。 但此时子进程如何获取对应出入口的文件描述符呢 答使用fork函数让子进程获取对应管道出入口的文件描述符。 int fds[2]; pipe(fds); pid_t pidfork(); if(pid0) //子进程 {write(fds[1],str,sizeof(str)); } else //父进程 {read(fds[0],buf,sizeof(buf)); } 此时父进程通过fork创建了一个子进程子进程获得了pipe函数返回的在fds里的两个文件描述符分别是管道的出口和入口这样的话子进程就可以通过管道入口写入数据父进程就可以通过管道出口读取数据。 两进程间管道的双向传输模型 代码 #includeiostream #includeunistd.hint main() {int fds[2];char buf[1024]{who are you};char str[1024]{Thank you for your message};pipe(fds);pid_t pidfork();if(pid0){write(fds[1],buf,sizeof(buf));sleep(2);//(1)char readbuf[1024];read(fds[0],readbuf,sizeof(readbuf));std::cout父进程传来的信息:readbufstd::endl;}else{char readbuf[1024];read(fds[0],readbuf,sizeof(readbuf));std::cout子进程传来的信息:readbufstd::endl;write(fds[1],str,sizeof(str));sleep(3);//(2)}return 0; } 调用sleep的原因 1注释掉会产生如下结果 子进程读取到的父进程的信息是自己write函数写进去的消息然后父进程会因为调用read函数没有读取到数据而阻塞住。这是因为向管道传输数据时先读的进程会把数据取走。当数据进入管道时此数据就成为了无主数据哪个进程先read那么哪个进程就能先取走。 2父进程睡眠3s是为了让父进程慢于子进程结束因为如果父进程先结束那么当前的命令提示符窗口就会结束然后当子进程读取到父进程发来的信息时就会再次弹出一个命令提示符窗口。如图 由以上问题可知当只有一个管道时要进行两个进程间的双向通信就需要控制好运行的先后顺序这很复杂甚至因为系统的不同不大可能实现所以我们该怎么进行双向通信 答创建2个管道。2个管道分别负责不同的数据流。 修改代码 #includeiostream #includeunistd.hint main() {int fds_write[2];int fds_read[2];char buf[1024]{who are you};char str[1024]{Thank you for your message};pipe(fds_write);pipe(fds_read);pid_t pidfork();if(pid0){write(fds_write[1],buf,sizeof(buf)); //子进程向父进程写管道入口char readbuf[1024];read(fds_read[0],readbuf,sizeof(readbuf)); //子进程从父进程读管道出口std::cout父进程传来的信息:readbufstd::endl;}else{char readbuf[1024];read(fds_write[0],readbuf,sizeof(readbuf)); //父进程就从子进程读管道出口std::cout子进程传来的信息:readbufstd::endl;write(fds_read[1],str,sizeof(str)); //父进程向子进程写管道入口//sleep(3);//写不写都可以}return 0; }
- 信号处理
将信号处理函数注册给操作系统调用然后将信号与信号处理函数绑定接着当触发信号时操作系统会调用响应的信号处理函数。这种形式有点类似于Qt的connect函数
2.1 signal函数旧版本只适用于旧版本UNIX系统
#includesignal.hvoid (*signal(
int signo,
void (func)(int)
))(int); 为了在产生信号时调用返回之前注册的函数指针 signo参数是要调用的特殊情况信息也就是信号。 常量含义SIGALRM 当alarm函数调用的时间到了后就调用信号处理函数。 #includeunistd.h unsigned int alarm(unsigned int seconds); //返回0或以秒为单位的距SIGALARM信号发生所剩时间 注意 1.如果seconds为0那么就取消之前注册的信号。 2.如果没有指定该信号对应的处理函数那么当alarm函数接收后将终止进程。 SIGINT输出CTRLC就调用信号处理函数。SIGCHLD子进程终止就调用信号处理函数。 func参数是一个int型参数返回值为void型的函数指针也就是信号处理函数。其中的int参数会传入注册时的signo参数。 注意当信号发生时会唤醒由于调用sleep函数而进入阻塞状态的进程。因为操作系统无法调用进入睡眠状态的进程的函数所以只能先唤醒这个进程再调用。 2.2 sigaction函数新版本所有UNIX操作系统都一样更稳定 #includesignal.hint sigaction( int signo, //与signal函数相同传递信号信息 const struct sigaction act, //对应于第一个参数的信号处理函数信号处理器信息 struct sigaction* oldact //通过此参数获取之前注册的信号处理函数指针不需要则传0 ); 成功返回0失败返回-1struct sigaction { void (*sa_handler)(int); //保存信号处理函数的指针 sigset_t sa_mast; //用于指定信号相关的选项和特性可以用sigemptyset(sa_mast)函数来初始化为0。 int sa_flags; //用于指定信号相关的选项和特性目前初始化为0即可 } 这个函数还有很多可以深究的地方目前用到的已经可以写一个多进程服务器端了后续的后面补上。 - 实现简单的多进程服务端 3.1 代码 思路 1.父进程调用accpet函数循环接收连接请求然后每个子进程就是一个服务器端处理一个客户端的连接请求。 2.此时获取的套接字文件描述符创建并传递给子进程。 3.子进程提供服务。 #includeiostream #includesys/socket.h #includeunistd.h #includesignal.h #includesys/wait.h #includearpa/inet.h #includecstring #define MAX_SIZE 1024void childprocesshandle(int sig) {std::coutherestd::endl;if(sigSIGCHLD){int statue;int childPidwaitpid(-1,statue,WNOHANG);if(childPid0){std::cout没有要结束的子进程std::endl;return;}if(WIFEXITED(statue)) //子进程正常返回{std::cout结束的子进程ID:childPidstd::endl;std::cout子进程返回的值:WEXITSTATUS(statue)std::endl;}else{std::cout子进程非正常退出std::endl;}} }int main() {int socketfdsocket(PF_INET,SOCK_STREAM,IPPROTO_TCP);if(socketfd-1){std::coutsocket fail!std::endl;return 0;}//这里是复习前面所学的多种套接字选项所写int sendbufsize;socklen_t sendlentsizeof(sendbufsize);getsockopt(socketfd,SOL_SOCKET,SO_SNDBUF,(void)sendbufsize,sendlent);std::cout输出缓冲区大小:sendbufsizestd::endl;int rcvbufsize;socklen_t rcvlentsizeof(rcvbufsize);getsockopt(socketfd,SOL_SOCKET,SO_RCVBUF,(void)rcvbufsize,rcvlent);std::cout输入缓冲区大小:rcvbufsizestd::endl;int NoTimewaittrue;socklen_t NoTimelensizeof(NoTimewait);setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,(const void)NoTimewait,NoTimelen);bool Checkfalse;socklen_t timewaitsizesizeof(Check);getsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,(void)Check,timewaitsize);std::coutTime-wait状态:Checkstd::endl;sockaddr_in sockAddr;memset(sockAddr,0,sizeof(sockAddr));sockAddr.sin_familyAF_INET;sockAddr.sin_addr.s_addrhtonl(INADDR_ANY);sockAddr.sin_porthtons(9130); //固定端口if(-1bind(socketfd,(sockaddr)sockAddr,sizeof(sockAddr))){std::coutbind fail!std::endl;}if(-1listen(socketfd,5)){std::coutlisten fail!std::endl;}struct sigaction sigstruct;sigemptyset(sigstruct.sa_mask);sigstruct.sa_handlerchildprocesshandle;sigstruct.sa_flags0;if(-1sigaction(SIGCHLD,sigstruct,0)){std::coutsigaction fail!std::endl;}while(1){sockaddr_in clientAddr;memset(clientAddr,0,sizeof(clientAddr));socklen_t clientAddrLensizeof(clientAddr);int clientfdaccept(socketfd,(sockaddr)clientAddr,clientAddrLen);if(clientfd-1){continue;}pid_t pidfork(); //1if(pid-1){std::coutfork fail!std::endl;continue;}else if(pid0){close(socketfd);std::cout客户端的IP地址:inet_ntoa(clientAddr.sin_addr)std::endl;char readbuf[MAX_SIZE];int bufsize;while((bufsizeread(clientfd,readbuf,MAX_SIZE))!0){std::cout客户端发来的信息:readbufstd::endl;write(clientfd,readbuf,bufsize);}close(clientfd);std::couthere1std::endl;return 12;}elseclose(clientfd);}close(socketfd); } 3.2 fork函数复制文件操作符 在代码中的1处执行fork函数创建子进程的时候子进程会复制父进程的所有资源所以那么套接字是否复制了 答套接字是属于操作系统的严格意义上来说没有复制只是进程会拥有相应套接字的文件描述符。 如图所示当父进程和子进程都有一个文件描述符指向操作系统的套接字的时候只有当2个文件描述符都终止后才能完全销毁套接字。如果是如图的状态 套接字不会被销毁所以在调用fork函数后要将无关的套接字文件描述符关掉。 3.3 多进程服务器端的缺点 创建进程需要付出极大的代价大量的运算和内存空间由于每个进程都拥有独立的内存空间所以在相互间的数据交换也要求采用相对复杂的方法(IPC属于相对复杂的通信方法如管道这种方式)。所以如果每有一个客户端就创建一个进程这是很耗费资源的。
- 分割TCP的I/O程序实现多进程客户端的一种模型 其实就是将客户端的I/O分离开来父进程负责读子进程负责写。如图 为什么要这样做 答以回声服务器端和客户端为例回声客户端在发送完数据后只能等待服务器端那边将数据传回来之后才能再次发送而将I/O分离开来则无需等待可多次发送如图左边是没分割I/O的回声客户端右边是分割的。 综上I/O分割有如下优点1. 程序实现更简单 2.可以提高频繁交换数据的程序性能。
- 上一篇: 做个网站需要哪些东西宣化网站制作公司
- 下一篇: 做公司企业网站标准尺寸网页访问自动跳转中
相关文章
-
做个网站需要哪些东西宣化网站制作公司
做个网站需要哪些东西宣化网站制作公司
- 技术栈
- 2026年04月18日
-
做个网站需要多少钱漫画网站建设教程
做个网站需要多少钱漫画网站建设教程
- 技术栈
- 2026年04月18日
-
做个网站需要多少钱.100个简单的手工小制作
做个网站需要多少钱.100个简单的手工小制作
- 技术栈
- 2026年04月18日
-
做公司企业网站标准尺寸网页访问自动跳转中
做公司企业网站标准尺寸网页访问自动跳转中
- 技术栈
- 2026年04月18日
-
做公司网站别人能看到吗6做网页的app
做公司网站别人能看到吗6做网页的app
- 技术栈
- 2026年04月18日
-
做公司网站成本wordpress 所有函数
做公司网站成本wordpress 所有函数
- 技术栈
- 2026年04月18日
