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.h>
int 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.h>
int 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.h>
pid_t wait(int* statloc);
// 成功时返回终止的子进程 ID, 失败时返回 -1;
// 子进程的返回值会保存到 statloc 所指的内存空间
// WIFEXITED() 子进程正常终止时返回 true
// WEXITSTATUS() 返回子进程的返回值
父进程调用 wait() 函数时,如果没有已终止的子进程,则父进程的程序将会阻塞,直至有子进程终止来返回值;