嵌入式开发交流网论坛
标题:
「linux后台开发」最新一线互联网大厂面试题总结
[打印本页]
作者:
午夜De凶手
时间:
2020-11-1 14:55
标题:
「linux后台开发」最新一线互联网大厂面试题总结
1、设计高并发系统数据库层面该如何设计? 数据库
锁
有哪些类型?如何实现?
1. 分库分表: 同样量的数据平均存储在不同数据库相同表(或不同表)中,减轻单表压力,如果还是很大,就可以每个库在分多张表,根据hash取值或者其他逻辑判断将数据存储在哪张表中
2. 读写分离: 数据库原本就有主从数据库之分,查询在从服务器,增删改在主服务器,
3. 归档和操作表区分: 建一张归档表,将历史数据放入,需要操作的表数据单独存储
4. 索引啊之类的创建,对于数据量很大,百万级别以上的单表,如果增删改操作不频繁的话, 可以创建bitMap索引,速度要快得多
1. 共享锁:要等第一个人操作完,释放锁,才能操作
2. 更新锁:解决死锁,别人可以读,但不能操作
3. 排他锁:读写都被禁用
4. 意向锁(xlock): 对表中部分数据加锁,查询时,可以跳过
5. 计划锁: 操作时,别的表连接不了这张表,
2、一个线程池正在处理服务如果忽然断电该怎么办?
队列实现持久化储存,下次启动自动载入。
但是实际需要看情况,大体思路是这样。
添加标志位,未处理 0,处理中 1,已处理 2。每次启动的时候,把所有状态为 1 的, 置为 0。或者定时器处理
关键性的应用就给电脑配个 UPS。
3、顺序栈的表示和实现
顺序栈,即栈的顺序存储结构是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针top指示栈顶元素在顺序栈中的位置。通常的习惯做法以top=0表示空栈。一般来说,在初始化设空栈时不应限定栈的最大容量。一个较合理的做法:先为栈分配一个基本容量,然后在应用过程中,当栈的空间不足在进行扩展。
#define STACK_INIT_SIZE 100#define STACKINCREMENT 10typedef struct SqStack{int *base;int *top;int stacksize;}SqStack;int InitStack(SqStack &S){S.base=(int *)malloc(STACK_INIT_SIZE*sizeof(int));if(!S.base)exit(0);S.top=S.base;S.stacksize=STACK_INIT_SIZE;return 0;}int GetTop(SqStack S,int &e){if(S.top==S.base)return 1;e=*(S.top-1);return 0;}int Push(SqStack &S,int e){if(S.top-S.base>=S.stacksize){S.base=(int*)realloc(S.base,(S.stacksize+STACKINCREMENT)*sizeof(int));if(!S.base)return 1;S.top=S.base+S.stacksize;S.stacksize+=STACKINCREMENT;}*S.top=e;S.top++;return 0;}int Pop(SqStack &S,int &e){if(S.top==S.base)return 1;--S.top;e=*S.top;return 0;}int StackEmpty(SqStack S){if(S.top==S.base)return 1;return 0;}int DestroyStack(SqStack &S){free(S.base);S.top=NULL;S.base=S.top;return 0;}
4、 C++实现线程安全的单例模式
懒汉模式:
class singleton{protected: singleton { pthread_mutex_init(&mutex); }private: static singleton* p;public: static pthread_mutex_t mutex; static singleton* initance;}; pthread_mutex_t singleton::mutex;singleton* singleton::p = NULL;singleton* singleton::initance{ if (p == NULL) { pthread_mutex_lock(&mutex); if (p == NULL) p = new singleton; pthread_mutex_unlock(&mutex); } return p;}
5、 使用“反向代理服务器”的优点是什么?
(1)提高访问速度
由于目标主机返回的数据会存在代理服务器的硬盘中,因此下一次客户再访问相同的站 点数据时,会直接从代理服务器的硬盘中读取,起到了缓存的作用,尤其对于热门站点 能明显提高请求速度。
(2)防火墙作用
由于所有的客户机请求都必须通过代理服务器访问远程站点,因此可在代理服务器上设 限,过滤某些不安全信息。
(3)通过代理服务器访问不能访问的目标站点
互联网上有许多开发的代理服务器,客户机可访问受限时,可通过不受限的代理服务器 访问目标站点,通俗说,我们使用的翻墙浏览器就是利用了代理服务器,可直接访问外网。
[attach]53869[/attach]
需要更多面试题或C/C++ Linux高级服务器架构师学习资料后台私信“资料”(包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等)
[attach]53870[/attach]
6、 Epoll在LT和ET模式下的区别以及注意事项
1.ET模式:
因为ET模式只有从unavailable到available才会触发,所以
1)、读事件:需要使用while循环读取完,一般是读到EAGAIN,也可以读到返回值小 于缓冲区大小;
如果应用层读缓冲区满:那就需要应用层自行标记,解决OS不再通知可读的问题
2)、写事件:需要使用while循环写到EAGAIN,也可以写到返回值小于缓冲区大小
如果应用层写缓冲区空(无内容可写):那就需要应用层自行标记,解决OS不再通知 可写的问题。
3).正确的accept,accept 要考虑 2 个问题
(1) 阻塞模式 accept 存在的问题
考虑这种情况:TCP连接被客户端夭折,即在服务器调用accept之前,客户端主动发 送RST终止连接,导致刚刚建立的连接从就绪队列中移出,如果套接口被设置成阻 塞模式,服务器就会一直阻塞在accept调用上,直到其他某个客户建立一个新的连接 为止。但是在此期间,服务器单纯地阻塞在accept调用上,就绪队列中的其他描述符 都得不到处理。
解决办法是把监听套接口设置为非阻塞,当客户在服务器调用accept之前中止某个连 接时,accept调用可以立即返回-1,这时源自Berkeley的实现会在内核中处理该 事 件,并不会将该事件通知给epool,而其他实现把errno设置为ECONNABORTED或者 EPROTO错误,我们应该忽略这两个错误。
(2)ET模式下accept存在的问题
考虑这种情况:多个连接同时到达,服务器的TCP就绪队列瞬间积累多个就绪连接,由 于是边缘触发模式,epoll只会通知一次,accept只处理一个连接,导致TCP就绪队列 中剩下的连接都得不到处理。
解决办法是用while循环抱住accept调用,处理完TCP就绪队列中的所有连接后再退 出循环。如何知道是否处理完就绪队列中的所有连接呢?accept返回-1并且errno 设置为EAGAIN就表示所有连接都处理完。
综合以上两种情况,服务器应该使用非阻塞地accept,accept在ET模式下的正确使用 方式为:
while ((conn_sock = accept(listenfd,(struct sockaddr *) &remote,(size_t *)&addrlen)) > 0) { handle_client(conn_sock);}if (conn_sock == -1) { if (errno != EAGAIN && errno != ECONNABORTED &&errno != EPROTO && errno != EINTR) perror("accept");} 2.LT模式:
因为LT模式只要available就会触发,所以:
1)、读事件:因为一般应用层的逻辑是“来了就能读”,所以一般没有问题,无需while 循环读取到EAGAIN;
如果应用层读缓冲区满:就会经常触发,解决方式如下;
2)、写事件:如果没有内容要写,就会经常触发,解决方式如下。
LT经常触发读写事件的解决办法:修改fd的注册事件,或者把fd移出epollfd。
总结:
LT模式的优点在于:事件循环处理比较简单,无需关注应用层是否有缓冲或缓冲区是 否满,只管上报事件。缺点是:可能经常上报,可能影响性能。
7、线程与进程的区别和联系?线程是否具有相同的堆栈?d是否有独立的堆栈?
进程是死的,只是一些资源的集合,真正的程序执行都是线程来完成的,程序启动的时候操作系统就帮你创建线程。每个线程有自己的堆栈。DLL中有没有独立的堆栈,这个问题不好回答,或者说这个问题本身是否有问题。因为DLL中的代码是被某些线程所执行;只有线程拥有堆栈,如果DLL中的代码是EXE中的线程所调用,那么这个时候是不是说这个DLL没有自己独立的堆栈?如果DLL中的代码是由DLL自己创建的线程所执行,那么是不是说DLL有独立的堆栈?
以上讲的是堆栈,如果对于堆来说,每个DLL有自己的堆,所以如果是从DLL中动态分配的内存,最好是从DLL中刪除,如果你从DLL中分配内存,然后在EXE中,或者另外一个DLL中删除,很有可能导致程序崩溃.
8、画出OSI和TCP/IP协议栈的对应关系.
OSI/RM共分为七层,TCP/IP分为四层。TCP/IP中的网络接口层相当于OSI的物理层和数据链路层,TCP的应用层相当于OSI的应用层、表示层和会话层。其余层次基本对应。见图,其中外围深颜色的是OSI层次,内部白颜色的是TCP层次。
[attach]53871[/attach]
9、分布式服务接口请求的顺序性如何保证?
①首先,一般来说,从业务逻辑上最好设计系统不需要这种顺序的保证,因为一旦引入顺序性保障,会导致系统复杂度的上升,效率会降低,对于热点数据会压力过大等问题。
②操作串行化。
首先使用一致性hash负载均衡策略,将同一个id的请求都分发到同一个机器上面去处理,比如订单可以根据订单id。如果处理的机器上面是多线程处理的,可以引入内存队列去处理,将相同id的请求通过hash到同一个队列当中,一个队列只对应一个处理线程。
③最好能将多个操作合并成一个操作。
10、智能指针是线程安全的吗?哪些地方需要考虑线程安全?
1.智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时++或者--,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2,这样引用计数就乱了,有可能造成资源未释放或者程序崩溃的风险。所以说智能指针中++或--的操作是需要加锁的,也就是说引用计数的操作是线程安全的
2.智能指针的对象存放在堆上,两个线程同时去访问,就会造成线程安全问题.
std::shared_ptr循环引用
struct ListNode{int _data;shared_ptr _prev;shared_ptr _next;~ListNode{ cout
欢迎光临 嵌入式开发交流网论坛 (http://www.dianzixuexi.com/bbs/)
Powered by Discuz! X3.2