10.1 进程概念及其应用
可以构建按序向第一个客户端到第一百个客户端提供服务的服务器端。当然,第一个客户端不会去抱怨,但是如果每个客户端的服务时间都要0.5s,那第100个客户端就会疯狂抱怨了。
并发服务器端的实现方法
即使有可能延长服务时间,也有必要改进服务器端,使其同时向所有发起请求的客户端提供服务,以提高平均满意度。
向多个客户端提供服务是一种有效利用CPU的方式
接下来讨论同时向多个客户端提供服务的并发服务器端。下面列出的是具有代表性的并发服务器端实现模型和实现方法:
- 多进程服务器:通过创建多个进程提供服务
- 多路复用服务器:通过捆绑并统一管理IO对象提供服务
- 多线程服务器:通过生成与客户端等量的线程提供服务
理解进程
进程:占用内存空间的正在运行的程序。
从操作系统的角度看,进程是程序流的基本单位,若创建多个进程,则操作系统将同时运行。
Tips:
拥有2个运算设备(核)的CPU称作双核CPU,有4个核的叫做4核CPU。一个CPU中可能包含多个核,核的个数与可同时运行的进程数相同。
若进程数超过核数,进程将分时使用CPU资源。但因为CPU运算速度极快,我们会感到所有进程同时运行。
进程ID
无论进程是如何创建的,所有进程都会从操作系统分配到ID,这就称为进程ID(PID),其值为大于2的整数。1要分配给操作系统启动后的(用于写作操作系统)首个进程,因此用户进程无法得到ID值1。
ps au
查看当前运行的所有进程
通过调用fork函数创建进程
创建进程的方法很多,只介绍用于创建多进程服务器端的fork函数。
fork函数将创建调用的进程副本:即并非根据完全不同的程序创建进程,而是复制正在运行的、调用fork函数的进程。
另外,两个进程都将执行fork函数调用后的语句(准确说是在fork函数返回后)。
因为通过同一个进程、复制相同的内存空间,之后的程序流要根据fork函数的返回值加以区分。即利用fork函数的如下特点区分程序执行流程:
- 父进程:fork函数返回子进程ID
- 子进程:fork函数返回0
父进程是指原进程,即调用fork函数的主体,而子进程是通过父进程调用fork函数复制出的进程。
分析:
① 父进程调用fork函数的同时复制出子进程,并分别得到fork函数的返回值。
② 复制前,父进程将全局变量gval增加到11,将局部变量lval的值增加到25,在此状态下完成进程复制。
③ 复制完成后根据fork函数的返回类型区分父子进程。
④ 父进程将lval的值加1,但这并不影响子进程lval的值
⑤ 子进程将gval的值加1,也不会影响父进程gval的值
因为fork函数调用后分成了完全不同的进程,只是二者共享同一代码而已。
#include <stdio.h>
#include <unistd.h>
int gval = 10;
int main(int argc, char *argv[]) {
pid_t pid;
int lval = 20;
gval++, lval += 5;
pid = fork();
if(pid == 0) {
// if child process
gval += 2;
lval += 2