C/C++ 基于Linux的高并发后台服务器-经验小结

1、多线程多进程模式

进程是操作系统资源分配最小的调度单位,意味着进程被暂停后,其下所有线程都会失去调度资源的权限。要充分利用系统资源,最好的形式是多线程多进程模式。
我们最好将一个整体功能,分散到多个进程当中,从而实现资源利用率最大化。否则就只能多个线程在一个进程内进行竞争,没办法充分利用系统资源。

2、进程创建

在linux中,开启进程一般通过exec系列函数或者fork函数来完成。即使是exec函数,也会要使用到fork函数。
所以开启进程,fork函数是无法绕开的。而fork函数会对线程造成影响,所以我们一定要先定好进程结构,然后再开启线程。原因:首先,由于线程无法被复制,所以在子进程中,一些线程会消失(没有被复制过来;其次,如果程序逻辑依赖多线程模式的时候,fork可能在子进程中破坏掉这种模式,进而使得程序出现无法预料的问题。所以一定要先准备好进程结构,再去使用线程!!!

3、进程入口函数的实现

 a)使用无属性的指针参数和固定参数的进程入口函数来实现
 b)使用面向对象的参数和统一的进程入口函数来实现
 c)使用模板函数来实现

这三种方式都可以实现,但是方便程度和安全性不一样。第一种方式技术上最简单,但是类型在转换的时候,可能出现问题。而且可以传入的参数数量是固定的,以后其他项目很难复用此代码。第二种方式比第一种好了不少。参数不是固定的,可移植性强了很多。但是这种方式需要专门写一个参数封装和解析的代码。这种解析代码的复用性会比较差。因为每个进程的任务不一样,参数也不一样,参数的含义也可能大相径庭。第三种方式难度最大,但是使用起来最方便,可以移植性最强。参数可以随时修改,函数也可以是类的成员函数。此外参数无需解析,直接原样转发到目标函数。实现起来也不需要太多代码,stl里面准备好了很多工具,可以直接使用。就是模板编程不太好理解。

*3c 模板函数和模板类

std::bind用于给一个可调用对象绑定参数。可调用对象包括函数对象(仿函数)、函数指针、函数引用、成员函数指针和数据成员指针。
std::forward<T>(u)有两个参数:T 与 u。当T为左值引用类型时,u将被转换为T类型的左值,否则u将被转换为T类型右值。如此定义std::forward是为了在使用右值引用参数的函数模板中解决参数的完美转发问题。
std::move是无条件的转为右值引用,而std::forward是有条件的转为右值引用,更准确的说叫做Perfect forwarding(完美转发),而	std::forward里面蕴含着的条件则是Reference Collapsing(引用折叠)。

对于一个函数:虚函数特性和模板函数特性不能同时存在,但是一个模板类可以有虚函数

4、多进程通信为什么用本地套接字通信(最方便最快速)而不用其他通信方式?

1.文件通信依赖磁盘速度(大量读写容易造成磁盘击穿),且慢于网络传输
2.管道在多线程环境下不太方便(可能会出现多个线程往一个管道内写入数据导致数据错误),而且管道为单向的。(除非是一对一管道通信,否则不建议使用管道通信)
3、信号量的信息容量过小(只能用于通知信号状态变化,大量的日志数据无法通过信号量传递),但是传输速度很快,也不会出现跨进程/线程的的问题
4、内存共享需要反复加锁同步,否则可能会出现问题(加锁需要极力避免,否则会出现卡顿)
5、消息函数(sendmsg、recvmsg)需要创建时确定收发方 (但是也有优势:可以收发大量数据,且不像管道同时有数据插入的情况以及不需要上锁)
6、“网络套接字”通信也可以,但需要额外的IP和端口(指定IP和端口随时都可以连上),但因为需要占用双方的一个IP和端口(占用资源),高并发可能造成挤占效应

*5、关于守护进程/正常进程被杀其子进程的去留?

1、守护进程若设置了prctl(PR_SET_PDEATHSIG,SIGHUP);或者prctl(PR_SET_PDEATHSIG, SIGTERM)这个函数的作用是,当父进程挂掉后,会发送SIGHUP或者SIGTERM信号给子进程。做了测试程序,果然,这次关掉B脚本后,A就退出了。后来又在A的代码中加了个信号处理函数,处理了SIGTERM和SIGHUP,发现kill掉B脚本的时候,A确实接收到了信号。
2、正常情况下(默认),一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。由于孤儿进程会被init进程给收养,所以孤儿进程不会对系统造成危害。
3、一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。一个进程如果只复制fork子进程而不负责对子进程进行wait()或是waitpid()调用来释放其所占有资源的话,那么就会产生很多的僵死进程,如果要消灭系统中大量的僵死进程,只需要将其父进程杀死,此时所有的僵死进程就会变成孤儿进程,从而被init所收养,这样init就会释放所有的僵死进程所占有的资源,从而结束僵死进程。
4、一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。

6、使用“本地套接字”通信的优势

1、无需IP和端口,不影响服务器对外资源(虚拟本地文件地址,内核文件映射出来的,不占IP和端口,可以放心使用),无挤占效应
2、信息无需加锁,可以多线程并发写(还可以采用epoll多线程并发读)
3、数据传输量巨大,传输速率高(纯内存读写),没有经过磁盘读写
4、本地模式采用“本地套接字”通信,集群模式(日志服务器程序和客户端程序在两台不同的物理机器上)采用“网络套接字”通信
5、使用本地套接字通信后,若后期想改为网络套接字通信,工作量很小,基本上可以完全沿用所有逻辑,只在套接字创建和客户端创建时做小修改(把原来的文件和地址改成网络套接字通信需要的IP和端口即可)
6、A和B同时发100
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值