中国建设银行启东市支行网站昆山做网站公司哪家好

当前位置: 首页 > news >正文

中国建设银行启东市支行网站,昆山做网站公司哪家好,做优化送网站,宁波seo在线优化目录 1–并发服务器端 2–进程 2-1–进程的相关概念 2-2–fork()创建进程 2-3–僵尸进程 2-4–wait()和waitpid()销毁僵尸进程 3–信号处理 3-1–signal()函数 3-2–sigaction()函数 3–3–利用信号处理技术消灭僵尸进程 4–基于多任务的并发服务器 5–分割 TCP 的…目录 1–并发服务器端 2–进程 2-1–进程的相关概念 2-2–fork()创建进程 2-3–僵尸进程 2-4–wait()和waitpid()销毁僵尸进程 3–信号处理 3-1–signal()函数 3-2–sigaction()函数 3–3–利用信号处理技术消灭僵尸进程 4–基于多任务的并发服务器 5–分割 TCP 的 I/O 程序 1–并发服务器端 并发服务器端主要有以下三类         ① 多进程服务器通过创建多个进程提供服务         ② 多路复用服务器通过捆绑并统一管理I/O对象提供服务         ③ 多线程服务器通过生成与客户端等量的线程提供服务 2–进程 2-1–进程的相关概念 进程的相关概念         ① 进程的定义如下占用内存空间的正在运行的程序         ② 从操作系统的角度看进程是程序流的基本单位若创建多个进程则操作系统将同时运行         ③ 对于 CPU 而言核的个数与可同时运行的进程数相同若进程数超过核数进程将分时使用 CPU 资源         ④ 无论进程是如何创建的所有进程都会从操作系统中分配到进程 ID其值为大于 2 的整数 2-2–fork()创建进程 #include unistd.h pid_t fork(void);// 成功时返回进程 ID失败时返回 -1 fork() 函数会复制父进程的进程副本给创建的子进程两个进程都将执行 fork() 函数调用后的语句具体执行的内容可根据 fork() 函数的返回值进行区分对于父进程 fork() 函数返回子进程的进程 ID对于子进程 fork() 函数返回 0 // gcc fork.c -o fork // ./fork #include stdio.h #include unistd.hint gval 10; int main(int argc, char *argv[]){pid_t pid;int lval 20;gval, lval 5;pid fork();if(pid 0){ // 对于子进程fork返回0因此执行以下内容gval 2, lval 2;}else{ // 对于父进程执行以下内容gval - 2, lval - 2;}if(pid 0){// 对于子进程复制父进程的进程副本则最初 gval 11, lval 25;// 执行 2 后gval 13, lval 27;printf(Child Proc: [%d, %d] \n, gval, lval);}else{// 对于父进程执行 - 2后gval 9, lval 23;printf(Parent Proc: [%d %d] \n, gval, lval);}return 0; } 2-3–僵尸进程 一般进程完成工作后都应被立即销毁但部分进行由于各种原因导致不能及时销毁就成为了僵尸进程其会占用系统中的重要资源         终止僵尸进程的两种方式① 传递参数给 exit 函数并调用 exit 函数② main 函数中执行 return 语句并返回值         向 exit 函数传递的参数值和 main 函数 return 语句的返回值都会传递给操作系统而操作系统不会立即销毁子进程直到把这些返回值传递给父进程这种不会被立即销毁的子进程就是僵尸进程         此外操作系统不会主动将返回值传递给父进程只有父进程主动发起请求时操作系统才会将子进程的返回值传递给父进程因此如果父进程未主动要求获得子进程的结束状态值操作系统就不会销毁子进程子进程就一直处于僵尸进程状态 // gcc zombie.c -o zombie // ./zombie #include stdio.h #include unistd.hint main(int argc, char *argv[]){pid_t pid fork();if(pid 0){puts(Hi, I am a child process);}else{// 父进程终止时子进程也会被同时销毁// 本案例通过延缓父进程的终止时间来让子进程进入僵尸进程状态printf(Child Process ID: %d \n, pid);sleep(30);}if(pid 0){puts(End child process);}else{puts(End parent process);}return 0; } 通过 ps au 可以观测到在父进程睡眠的时间里子进程成为了僵尸进程Z状态 2-4–wait()和waitpid()销毁僵尸进程 为了销毁僵尸子进程父进程必须主动请求获取子进程的返回值         父进程调用 wait() 函数 和 waitpid() 函数可以主动获取子进程的返回值 #include sys/wait.hpid_t wait(int* statloc); // 成功时返回终止的子进程 ID, 失败时返回 -1; // 子进程的返回值会保存到 statloc 所指的内存空间// WIFEXITED() 子进程正常终止时返回 true // WEXITSTATUS() 返回子进程的返回值 父进程调用 wait() 函数时如果没有已终止的子进程则父进程的程序将会阻塞直至有子进程终止来返回值 // gcc wait.c -o wait // ./wait #include stdio.h #include stdlib.h #include unistd.h #include sys/wait.hint main(int argc, char* argv[]){int status;pid_t pid fork();if(pid 0){return 3; // 第一个子进程返回3}else{printf(Child PID: %d \n, pid); // 第一个子进程的 IDpid fork(); // 创建第二个子进程if(pid 0){exit(7); // 第二个子进程返回7}else{printf(Child PID : %d \n, pid); // 第二个子进程的 IDwait(status); // 主动请求获取子进程的返回值if(WIFEXITED(status)){printf(Chile send one: %d \n, WEXITSTATUS(status));}wait(status); // 主动请求获取子进程的返回值if(WIFEXITED(status)){printf(Child send two: %d \n, WEXITSTATUS(status));}sleep(30); // 这时候父进程选择睡眠子进程也不会成为僵尸进程}}return 0; } wait() 函数会引起程序阻塞而 waitpid() 函数不会引起阻塞 #include sys/wait.hpid_t waitpid(pid_t pid, int* statloc, int options); // 成功时返回终止的子进程的ID(或0)失败时返回-1 // pid 表示等待终止的目标子进程的 ID传递 -1 时与 wait() 相同即可以等待任意子进程终止 // statloc 存放子进程返回结果的地址空间 // options 设置为 WNOHANG 时即使没有终止的子进程父进程也不会进入阻塞状态而是返回 0 并结束函数 // gcc waitpid.c -o waitpid // ./waitpid #include stdio.h #include unistd.h #include sys/wait.hint main(int argc, char *argv[]){int status;pid_t pid fork();if(pid 0){sleep(15);return 24;}else{// 没有终止的子进程时返回0则一直循环调用waitpid()// 直到有终止的子进程来跳出循环while(!waitpid(-1, status, WNOHANG)){sleep(3);puts(sleep 3sec.);}if(WIFEXITED(status)){printf(Child send %d \n, WEXITSTATUS(status));}return 0;} } 3–信号处理 上述父进程调用 wait() 函数会阻塞而调用 waitpid() 函数也必须不断调用因为不知道子进程何时终止这也同样会影响父进程的工作效率         通过信号处理机制可以解决上述问题信号表示在特定事件发生时由操作系统向进程发送通知的消息         因此可以通过注册信号当子进程终止时让操作系统将子进程终止的消息发送给父进程这时候父进程才请求获取子进程的返回值 3-1–signal()函数 #include signal.h void (*signal(int signo, void (*func)(int)))(int);// 第一个参数 signo 表示特殊情况信息 // 第二个参数表示特殊情况发生后要调用的函数的地址值指针// 常见特殊情况 // 1. SIGALRM 表示已到调用 alarm 函数注册的时间 // 2. SIGINT 表示遇到了 CTRLC 的情况 // 3. SIGCHLD 表示遇到了子进程终止的情况#include unistd.h unsigned int alarm(unsigned int seconds); // 返回 0 或以秒为单位的距离 SIGALRM 信号发生的所剩时间即还剩下多长时间就会发生 SIGALRM 信号时间 // 经过 seconds 秒后会发生 SIGALRM 信号事件 发生信号事件时将会唤醒由于调用 sleep 函数而进入阻塞状态的进程即即使还没到 sleep 函数规定的事件也会被强制唤醒而进程一旦唤醒后就不会再进入睡眠状态 // gcc signal.c -o signal // ./signal#include stdio.h #include unistd.h #include signal.hvoid timeout(int sig){if(sig SIGALRM){puts(Time out!);}alarm(2); }void keycontrol(int sig){if(sig SIGINT){puts(CTRLC pressed);} }int main(int argc, char argv[]){int i;signal(SIGALRM, timeout);signal(SIGINT, keycontrol);alarm(2);for(i 0; i 3; i){puts(wait…);sleep(100); // 不会真的睡眠 100s因为alarm函数会产生SIGALRM信号事件从而唤醒进程}return 0; } 3-2–sigaction()函数 sigaction() 函数的功能类似于 signal() 函数但 sigaction() 更稳定因为 signal() 函数在不同操作系统中可能存在区别但 sigaction() 在不同 UNIX 系统中完全相同 #include signal.hint sigaction(int signo, const struct sigaction act, struct sigaction* oldact); // 成功时返回0失败时返回 -1 // signo 用于传递信号信息 // act 对应于 signo 的信号处理函数 // oldact 获取之前注册的信号处理函数的指针不用时传递0struct sigaction{void (sa_handler)(int); // 信号处理函数的指针sigset_t sa_mask; // 初始化为0int sa_flags; // 初始化为0 } // gcc sigaction.c -o sigaction // ./sigaction#include stdio.h #include unistd.h #include signal.hvoid timeout(int sig){if(sig SIGALRM){puts(Time out!);}alarm(2); }int main(int argc, char argv[]){int i;struct sigaction act;act.sa_handler timeout;sigemptyset(act.sa_mask); // 调用sigemptyset()将sa_mask的所有位初始化为0act.sa_flags 0; // sa_flags也初始化为0sigaction(SIGALRM, act, 0);alarm(2);for(int i 0; i 3; i){puts(wait…);sleep(100);}return 0; }3–3–利用信号处理技术消灭僵尸进程 // gcc remove_zombie.c -o remove_zombie // ./remove_zombie#include stdio.h #include stdlib.h #include unistd.h #include signal.h #include sys/wait.hvoid read_childproc(int sig){int status;pid_t id waitpid(-1, status, WNOHANG); // 等待任意子线程结束if(WIFEXITED(status)){ // 判断子线程是否正常终止printf(Remove proc id: %d \n, id);printf(Child send: %d \n, WEXITSTATUS(status)); // 打印子线程的返回值} }int main(int argc, char *argv[]){pid_t pid;struct sigaction act;act.sa_handler read_childproc; // 设置信号处理函数sigemptyset(act.sa_mask);act.sa_flags 0;sigaction(SIGCHLD, act, 0); // 调用sigaction()当遇到子线程结束的信号时调用信号处理函数pid fork();if(pid 0){ // 子线程执行区域puts(Hi! Im child process);sleep(10);return 12;}else{printf(Child proc id: %d \n, pid);pid fork();if(pid 0){ // 另一个子线程执行区域puts(Hi! Im child process);sleep(10);return 24;}else{int i;printf(Child proc id: %d \n, pid);for(int i 0; i 5; i){puts(wait…);sleep(5);}}}return 0; } 4–基于多任务的并发服务器 每当有客户端请求服务时回声服务器端都创建子进程以提供服务         使用 fork() 创建进程时子进程会复制父进程拥有的所有资源因此无需额外传递文件描述符 // gcc echo_mpserv.c -o echo_mpserv // ./echo_mpserv 9190#include stdio.h #include stdlib.h #include string.h #include unistd.h #include signal.h #include sys/wait.h #include arpa/inet.h #include sys/socket.h#define BUF_SIZE 30void error_handling(char message){fputs(message, stderr);fputc(\n, stderr);exit(1); }void read_childproc(int sig){__pid_t pid;int status;pid waitpid(-1, status, WNOHANG);printf(remove proc id: %d \n, pid); }int main(int argc, char argv[]){int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;__pid_t pid;struct sigaction act; // 信号socklen_t adr_sz;int str_len, state;char buf[BUF_SIZE];if(argc ! 2){printf(Usage : %s port\n, argv[0]);exit(1);}// 防止僵尸进程act.sa_handler read_childproc; //设置信号处理函数sigemptyset(act.sa_mask);act.sa_flags 0;state sigaction(SIGCHLD, act, 0);serv_sock socket(PF_INET, SOCK_STREAM, 0); // 创建 tcp socketmemset(serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family AF_INET;serv_adr.sin_addr.s_addr htonl(INADDR_ANY);serv_adr.sin_port htons(atoi(argv[1]));if(bind(serv_sock, (struct sockaddr) serv_adr, sizeof(serv_adr)) -1){error_handling(bind() error); } if(listen(serv_sock, 5) -1){error_handling(listen() error);}while(1){adr_sz sizeof(clnt_adr);clnt_sock accept(serv_sock, (struct sockaddr)clnt_adr, adr_sz);if(clnt_sock -1){continue;}else{puts(new client connected…);}// 每当有客户端请求服务时clnt_sock 不为 -1// 因此会执行 fork() 函数创建子进程并由子进程向客户端提供服务pid fork(); if(pid -1){close(clnt_sock);continue;}if(pid 0){ // 子进程运行区域close(serv_sock); // 将复制过来的父进程中的服务器文件描述符销毁while((str_len read(clnt_sock, buf, BUF_SIZE)) ! 0){write(clnt_sock, buf, str_len);}close(clnt_sock);puts(client disconnected…);return 0;}else{ // 父进程运行区域// 因为客户端的文件描述符已经复制到子进程中// 由子进程处理客户端的内容因此父进程需要销毁客户端的文件描述符close(clnt_sock); }}close(serv_sock);return 0; } 5–分割 TCP 的 I/O 程序 客户端通过 fork() 创建子进程将 I/O 分割客户端的父进程负责接收数据额外创建的子进程负责发送数据         分割后不同进程分别负责输入和输出因此客户端是否从服务器端接收完数据都可以进行传输 // gcc echo_mpclient.c -o echo_mpclient // ./echo_mpclient 127.0.0.1 9190#include stdio.h #include stdlib.h #include string.h #include unistd.h #include arpa/inet.h #include sys/socket.h#define BUF_SIZE 30void error_handling(char *message){fputs(message, stderr);fputc(\n, stderr);exit(1); }void read_routine(int sock, char buf){while(1){int str_len read(sock, buf, BUF_SIZE);if(str_len 0) return;buf[str_len] 0;printf(Message from server: %s, buf);} }void write_routine(int sock, char buf){while(1){fgets(buf, BUF_SIZE, stdin);if(!strcmp(buf, q\n) || !strcmp(buf, Q\n)){shutdown(sock, SHUT_WR); // 调用 shutdown 函数向服务器端传递 EOFreturn;}write(sock, buf, strlen(buf));} }int main(int argc, char argv[]){int sock;pid_t pid;char buf[BUF_SIZE];struct sockaddr_in serv_adr;if(argc ! 3){printf(Usage : %s IP port\n, argv[0]);exit(1);}sock socket(PF_INET, SOCK_STREAM, 0); // 创建 tcp socketmemset(serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family AF_INET;serv_adr.sin_addr.s_addr inet_addr(argv[1]);serv_adr.sin_port htons(atoi(argv[2]));if(connect(sock, (struct sockaddr)serv_adr, sizeof(serv_adr)) -1){error_handling(connect() error!);}pid fork(); // 创建子进程实现 I/O 分离if(pid 0){ // 子进程写数据到服务器端write_routine(sock, buf);}else{ // 父进程从服务器端中读取数据read_routine(sock, buf);}close(sock);return 0; }