第十二篇:并发回射服务器的具体实现及其中僵尸子进程的清理( 上 )

前言

本文将分为两个部分,第一部分具体实现一对并发回射服务器/客户程序( 看过前面那篇文章的这部分可不看 重复了 );第二部分为服务器添加僵尸子进程自动清理机制。

那么服务器具体怎么实现并发?怎么会有僵尸进程?僵尸进程又是什么?如何处理这些僵尸进程 ... 本文将为你一一解惑。

回射并发服务器

功能:接收用户发送过来的数据后再发送回用户,且能同时处理多个用户请求。

大体思路:每当收到用户请求,服务器就fork一个子进程,让子进程去处理客户请求。

实现代码

 1 #include    "unp.h"
 2
 3 int
 4 main(int argc, char **argv)
 5 {
 6     int                    listenfd, connfd;
 7     pid_t                childpid;
 8     socklen_t            clilen;
 9     struct sockaddr_in    cliaddr, servaddr;
10
11     listenfd = Socket(AF_INET, SOCK_STREAM, 0);
12
13     bzero(&servaddr, sizeof(servaddr));
14     servaddr.sin_family      = AF_INET;
15     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
16     servaddr.sin_port        = htons(SERV_PORT);
17
18     Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
19
20     Listen(listenfd, LISTENQ);
21
22     for ( ; ; ) {
23         clilen = sizeof(cliaddr);
24         connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
25
26         /*
27          * 子进程代码
28         */
29         if ( (childpid = Fork()) == 0) {
30             // 子进程启动后首先关闭监听套接字
31             Close(listenfd);
32             // 回射处理
33             str_echo(connfd);
34             // 退出子进程
35             exit(0);
36         }
37         // 关闭连接套接字
38         Close(connfd);
39     }
40 }
 1 #include    "unp.h"
 2
 3 void
 4 str_echo(int sockfd)
 5 {
 6     ssize_t        n;
 7     char        buf[MAXLINE];
 8
 9 again:
10     while ( (n = read(sockfd, buf, MAXLINE)) > 0)
11         Writen(sockfd, buf, n);
12
13     if (n < 0 && errno == EINTR)
14         goto again;
15     else if (n < 0)
16         err_sys("str_echo: read error");
17 }

回射并发客户端

功能:向服务器发送数据,接收服务器反射回的信息并打印。

大体思路:略。

实现代码

 1 #include    "unp.h"
 2
 3 int
 4 main(int argc, char **argv)
 5 {
 6     int                    sockfd;
 7     struct sockaddr_in    servaddr;
 8
 9     if (argc != 2)
10         err_quit("usage: tcpcli <IPaddress>");
11
12     sockfd = Socket(AF_INET, SOCK_STREAM, 0);
13
14     bzero(&servaddr, sizeof(servaddr));
15     servaddr.sin_family = AF_INET;
16     servaddr.sin_port = htons(SERV_PORT);
17     Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
18
19     Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
20
21     str_cli(stdin, sockfd); // 与服务器通信
22
23     exit(0);
24 }
 1 #include    "unp.h"
 2
 3 void
 4 str_echo(int sockfd)
 5 {
 6     ssize_t        n;
 7     char        buf[MAXLINE];
 8
 9 again:
10     while ( (n = read(sockfd, buf, MAXLINE)) > 0)
11         Writen(sockfd, buf, n);
12
13     if (n < 0 && errno == EINTR)
14         goto again;
15     else if (n < 0)
16         err_sys("str_echo: read error");
17 }

运行测试

1. 在一个终端以超级用户权限启动服务器

2. 在另几个终端打开客户端并输入IP地址参数127.0.0.1

3. 执行回射测试,运行情况如下( 几个客户终端都运行正常 ):

问题发现

看似这个并发回射程序已经做好了。但,很遗憾,不是的。我们接下来的观察将会带我们进入本文真正的主题:僵尸子进程。

首先退出这几个客户终端。按照我们最初的设想,这几个子进程应该已经结束了。但事实真是这样吗?我们在终端执行以下命令进行查看:

图中,STAT显示表示进程状态,Z+表示进程正处在“ 僵尸 ”状态。通过比对其PID与PPID不难发现,服务器中处理客户端的进程并没有“ 死去 ”,而是变成了“ 僵尸 ”。

什么是僵尸进程

僵尸进程就是已经撤销但依然占着资源的进程。

为什么要有僵尸进程

设置僵死状态的目的是为了维护子进程的信息。父进程有时需要获取到一些已经撤销的子进程的信息。

如何处理僵尸进程

对于本例中的僵尸进程,我们当然要将其清理掉并释放其占用的资源。

时间: 2024-10-13 00:31:36

第十二篇:并发回射服务器的具体实现及其中僵尸子进程的清理( 上 )的相关文章

socket编程之并发回射服务器3

在socket编程之并发回射服务器一文中,服务器采用多进程的方式实现并发,本文采用多线程的方式实现并发. 多线程相关API: // Compile and link with -pthread int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); int pthread_join(pthread_t thread, void **

第八篇:并发回射服务器的最基本实现思路

前言 一个服务器,通常会在一段时间内接收到多个请求.如果非要等到处理完一个请求再去处理下一个,势必会造成大部分用户的不满( 尤其当有某个请求需要占用大量时间时 ). 如何解决这个问题?让处理这些用户请求的进程并发起来吧! 实现方法 方法一:当客户请求到达,服务器进程创建一个自身副本( 调用fork函数 ).这是网络服务器的经典用法. 方法二:当客户请求到达,服务器进程创建一个自身副本( 调用fork函数 ),然后用本机内的另一个程序替换自身( 调用execve函数 ). 注:fork和execv

Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇)

目录 Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇) 1 Internal Locking Methods 2 Metadata Locking 3 External Locking Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇) 1 Internal Locking Methods 这里介绍Mysql的几种锁,该锁由Mysql自行进行管理,用户不需要处理该锁. Row-Level Locking 对于InnoDB,行锁可以通过SELECT ... FOR UPDAT

[CSAPP笔记][第十二章并发编程]

第十二章 并发编程 如果逻辑控制流在时间上是重叠,那么它们就是并发的(concurrent).这种常见的现象称为并发(concurrency). 硬件异常处理程序,进程和Unix信号处理程序都是大家熟悉的例子. 我们主要将并发看做是一种操作系统内核用来运行多个应用程序的机制. 但是,并发不仅仅局限于内核.它也可以在应用程序中扮演重要的角色. 例如 Unix信号处理程序如何允许应用响应异步事件 例如:用户键入ctrl-c 程序访问虚拟存储器的一个未定义的区域 其他情况 访问慢速I/O设备 当一个应

UNIX网络编程卷1 回射服务器程序 TCP服务器程序设计范式 四个版本

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 这是一个简单的回射服务器程序.它将客户发送的数据读入缓冲区并回射其中内容 下面我会介绍同一个使用 TCP 协议的回射服务器程序的几个不同版本,分别是 fork 版本.select 版本.poll 版本.多线程版本 fork 版本:为每一个客户连接派生(fork) 一个子进程用来处理客户请求 /** * TCP/IPv4 协议相关 * **/ #include "unp.h" in

第十二章 并发编程 学习笔记

第十二章 并发编程 进程是程序级并发,线程是函数级并发. 三种基本的构造并发程序的方法: 进程:每个逻辑控制流是个一个进程,由内核进行调度和维护. I/O多路复用:应用程序在一个进程的上下文中显式地调度他们自己的逻辑流. 线程:运行在单一进程上下文中的逻辑流,由内核进行调度. 12.1 基于进程的并发编程 构造并发程序最简单的方法就是用进程. 使用大家都很熟悉的函数例如: fork exec waitpid 关于在父.子进程间共享状态信息:共享文件表,但不共享用户地址空间. 进程又独立的地址空间

第十二章 并发编程

第十二章 并发编程 三种基本的构造并发程序 进程:每个逻辑控制流是一个进程,由内核进行调度,进程有独立的虚拟地址空间 I/O多路复用:逻辑流被模型化为状态机,所有流共享同一个地址空间 线程:运行在单一进程上下文中的逻辑流,由内核进行调度,共享同一个虚拟地址空间 常用函数: fork exec waitpid 基于I/O多路复用的并发事件驱动服务器 事件驱动程序:将逻辑流模型化为状态机. 状态机: 状态 输入事件 转移 对于状态机的理解,参考EDA课程中学习的状态转换图的画法和状态机. 整体的流程

TCP回射服务器修订版(ubuntu 18.04)

一.需求 把https://www.cnblogs.com/soldierback/p/10673345.html中的TCP回射服务器程序重写成使用select来处理任意个客户的单进程 程序,而不是为每个进程派生一个子进程 二.分析 (1)服务器有单个监听描述符    (2)服务器只维护一个读描述符集:假设服务器是在前台启动的,那么描述符0.1.2将分别被设置为标准输入.标准输出和标准错误输出:可见监听   套接字的第一个可用描述符是3    (3)服务器维护一个名为clients的整型数组,它

Python开发【第二十二篇】:Web框架之Django【进阶】

Python开发[第二十二篇]:Web框架之Django[进阶] 猛击这里:http://www.cnblogs.com/wupeiqi/articles/5246483.html 博客园 首页 新随笔 联系 订阅 管理 随笔-124  文章-127  评论-205 Python之路[第十七篇]:Django[进阶篇 ] Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用 MySQLdb 来连接数据库,并编写数据访问层代码 业务逻