本来打算三章的,由于在家里有些不可避免的事儿浪费了些时间,还有就是第十章遗忘的确实有点多/(ㄒoㄒ)/,所以就复习了两章。
并发服务器端实现模型和方法,具有代表性的有如下三种:
1、多进程服务器:通过创建多个进程提供服务。
2、多路复用服务器:通过捆绑并统一管理I\O对象提供服务。
3、多线程服务器:通过生成与客户端等量的线程提供服务。
父进程返回子进程ID
子进程返回0
有关僵尸进程:
长生僵尸进程的原因:子进程先于父进程结束,并且父进程没有收到关于子进程的信息,那么该子进程就会变成僵尸进程。
只有父进程主动发起请求时,操作系统才会传递该值,若父进程未主动要求获得子进程的结束状态值,操作系统将一直保存着,并让子进程长时间处于僵尸进程状态。
因此我们若想解决僵尸进程,就需要将子进程的信息传递给父进程。
另外如果父进程先于子进程销毁了,子进程就会变成孤儿进程。然后被init收留。
那接下来就是如何主动的向父进程说明子进程的一些结束状态值等信息
目前学会了两种方法:
1、利用wait函数
调用wait函数时如果已有子进程终止,那么子进程终止时传递的返回值将保存在statloc所指的内存空间里。
函数参数指向的单元中还包含其他信息,需要通过以下两个宏进行分离
WIFEXITED 子进程正常终止时返回真 WIFEXITED(status);
WEIXTSTATUS 返回子进程的返回值 WEXITSTATUS(status);
此函数有一个很严重的缺点:
在调用wait函数时,如果没有已经终止的子进程,那么程序将阻塞直到有子进程终止。
2、使用waitpid函数
(这个可以防止wait函数的阻塞问题哦)
第一个参数若传递-1,则将会等待任意子进程终止,跟wait一样。
第三个参数传递sys/wait.h中声明的常量WNOHANG,解决阻塞问题。即即使没有终止的子进程,也会返回0并退出。
ps.题外话:JAVA为了保持平台移植性,有着独立与操作系统的方式提供进程和线程的方法。
关于信号处理:
这是我感觉挺重要的一个知识点,因为自我感觉它真的很有用🤦
下面是信号注册函数,是一个返回值类型为函数指针的函数。好像有点绕口🤦(关于函数指针啥的欢迎访问我的github---->https://github.com/oin625/Tcp-ip/blob/master/Notes/%E5%85%B3%E4%BA%8E%E6%8C%87%E9%92%88%E5%87%BD%E6%95%B0%E5%92%8C%E5%87%BD%E6%95%B0%E6%8C%87%E9%92%88.md)
void (*signal(int signo, void (*func)(int)))(int);
其中第一个参数为特殊情况信息,第二个参数是再第一个参数的情况下,将要调用的函数的地址值。
对于第一个参数,学了如下三个特殊情况:
1、SIGALRM 已到通过调用alarm函数注册的时间
2、SIGINT 输入了CRTL+C
3、SIGCHLD 子进程终止
unsigned int alarm(unsigned int seconds);
关于alarm需要注意的有:
1、若向alarm中传递0,那么之前对SIGALRM信号的预约将取消。
2、该函数预约信号后,并没有指定该信号对应的处理函数,则(通过调用signal函数)终止进程,不做任何处理。
发生信号时,将会唤醒由于调用sleep函数而进入阻塞状态的进程。
signal函数再UNIX系列的不同操作系统中可能存在区别,接下来有一个更稳定的信号注册处理函数,sigaction函数。
给出sigaction结构体信息
struct sigaction
{
void (*sa_handler)(int); //保存信号处理函数的指针值(地址值)
sigset_t sa_mask; //0即可 清零过程:sigemptyset(&act.sa_mask);
int sa_flags; //同上
}act;
利用示例更好理解:
struct sigaction act;
act.sa_handler=timeout;
sigemptyset(&act.sa_mask);
sa_flags = 0;
sigaction(SIGALRM, &act, 0);
上面的都很理解了,那就可以很自然的会使用信号处理技术来消除僵尸进程了。
很简单,先注册进程销毁的信号处理函数,有关信号信息是SIGCHLD,然后再对应的函数中调用waitpid函数即可。
使用fork可以实现多进程的并发服务器,并不难。其中可以再加上信号处理技术以此消除僵尸。
其中会涉及到一个问题,就是在调用fork的时候,我们同样fork了一份文件描述符出来。
这就导致了父进程拥有了服务器套接字和客户端套接字,子进程也同样拥有着服务器套接字和客户端套接字。
而套接字需要全部关闭才行,所以需要将无关的套接字描述符全都关掉,即在父进程中要立马关掉客户端的套接字,在子进程中,要立关掉服务器端套接字。
第十章完。。。。。。。。。
-----------------------------------------分割线---------------------------------------------------------
使用管道可以实现两个进程之间的信息传递。
管道和套接字一样属于操作系统。
管道很简单,这个的话感觉一张图就可以解决了。
由于找不到图我只能手拍了(lll¬ω¬)
图上很明显fds[1]是用来write的,fds[2]是用来read的。
还有就是一个管道是做不到双向通信的,想要双向通信我们需要创建两个管道才可。