具有代表性的并发服务器端实现模型:
多进程服务器:通过创建多个进程提供服务;
多路复用服务器:通过捆绑并统一管理I/O对象提供服务;
多线程服务器:通过生成与客户端等量的线程提供服务。
1、进程
fork() 函数
#include <unistd.h>
pid_t fork();
功能:
创建一个进程。
返回值:
失败,返回-1; 成功,返回进程ID;
父进程: fork() 函数返回子进程ID;
子进程: fork() 函数返回0。
2、僵尸进程
子进程运行完后,父进程并未对其回收。
验证僵尸进程:
// 在 fork() 后程序会先运行子进程然后父进程。
// 在本程序中,父进程 sleep(30); 在这个过程中子进程未被回收,而成为了僵尸进程。
// 在 这30s 过程中,打开一个新的终端 ps aux 在 sta栏内会看到一个进程的状态为 Z+, 就是此僵尸进程。
(代码: unit_10_zombie.c)
30s后:
3、回收子进程
如果子进程未得到得到回收,就会变成僵尸进程。
利用 wait() 函数
#include <sys/wait.h>
pid_t wait( int *statloc );
功能:
回收终止的进程。
【注】若此时没有进程终止,会阻塞等待,知道有进程终止。
参数:
statloc: (被终止)进程的状态,一般就定义 int 型变量,然后取其地址。
返回值:
成功,返回终止的子进程 ID; 失败,返回 -1。
【补】
WIFEXITED() 函数
子进程正常终止返回“真”(TRUE)。
eg. if( WIFEXITED() )
WEXITSTATUS() 函数
返回子进程的返回值。指的是 return 或者 exit()返回值。
【注】 exit() 别用0,1。
利用 waitpid() 函数
#include <sys.wait.h>
pid_t waitpid( pid_t pid, int *statloc, int options );
功能:
回收终止的进程,且不会阻塞。
参数:
pid:等待终止的目标子进程的ID。
若传递 -1, 则与 wait() 函数相同,可以等待任意子进程终止。
statloc:(被终止)进程的状态,一般就定义 int 型变量,然后取其地址。
options:一般写 WNOHANG,效果:即使没有终止的子进程也不会进入阻塞状态,而是返回0并退出函数。
返回值:
成功,返回终止的子进程ID(或0); 失败,返回 -1。
3、信号处理
信号处理:在特定事件发生时由操作系统向进程发送的消息,然后今天处理。
【补】结构体 sigaction
struct sigaction
{
void (&sa_handler) (int); // 指向信号触发的时候调用的函数。Eg.进程回收函数
sigset_t mask; // 到时候用sigemptyset( &act.sa_mask )初始化
int sa_flags; // 直接写0
}
sigaction() 函数
#include <signal.h>
int sigaction( int signo, const struct sigaction act, struct sigaction oldact );
功能:
利用 sigaction() 函数进行信号处理操作。
参数:
signo: 对应不同的信号状态:
act: 对应于参数 signo 的信号处理信息。(对应结构体如下)
oldact:直接写0。
返回值
成功,返回0; 失败,返回 -1。
eg.实例
// 使用信号回收僵尸进程
struct sigaction act;
act.sa_handler = recycle; // 回收函数 recycle 自己写
act.sa_flags = 0;
sigemptyset( &act.sa_mask );
sigaction( SIGCHLD, &act, NULL );
详情参考 unit_10_remove_zombie.c 利用信号处理技术消灭僵尸进程。
4、基于多任务的并发服务器
此节的案例相当重要。