面经问题整理1

CDN

CDN:内容分配网络,任何内容都可以通过CDN进行加速;
CDN概念:CDN是在现有internet上增加的新的一种网络架构,好处是用户可以在最近的节点上访问到所需要的内容,加快了网站响应的速度。

那么是如何做到的呢:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-snoCKiWF-1585662254322)(en-resource://database/826:1)]
上面是架构图

下面来阐述过程:

  • 请求过程;当用户想要访问一个静态文件如音乐,这个静态文件的域名是cdn.qqmusic.com
  1. 首先,需要知道无CDN情况的的流程
    a.首先查询浏览器的DNS缓存,看是否有映射的IP关系;没有就查询操作系统的DNS缓存,(操作系统的window在system32\drivers\etc\hosts下,linux在\etc\hosts下);没有就访问本地的域名服务器(接入互联网的提供商);没有就访问根域名服务器,从根域名服务器得到顶级域名服务器的地址,然后访问顶级域名服务器,从顶级域名服务器得到域名对应的Name Server的域名服务器地址,这个Name Server就存着这个域名对应的IP地址映射表,就可以返回IP地址了。

    如果有CDN,先访问本地域名服务器(如果已经记录了CNAME更快),会访问到域名注册的服务器,这时候DNS解析服务器就把这个域名的解析设置了CNAME,返回到local后发现有CNAME,就把请求转发给CDN负载均衡服务器,服务器根据用户的所在地分配最佳的CDN节点给用户,用户根据解析的IP去访问CDN节点,CDN节点判断缓存(二级缓存)中是否有数据,如果没有就向实际IP地址提交访问请求,然后CDN节点一方面返回用户数据,另一方面在本地存储。

CDN网络一般分为中心节点和边缘节点;
中心节点的功能就是负载均衡、监控边缘节点健康状况、根据边缘节点状况重定向请求,与主站同步更新。
边缘节点负责保存着缓存的内容,有二级缓存,当边缘节点未命中内容时,就会向中心层请求。

CDN如果单节点负载过大,那么中心节点会进行调整,

IO机制

阻塞式IO和非阻塞式IO (见unix内容)

IO多路复用

  • select o(n)
int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval *timeout);

fd_set可以理解为一个集合,这个集合中存放的是文件描述符(file descriptor)
fd0 fd1
readset 1 0 …

关键用于判断IO是否已经完成即数据可读写的标志就在 readset writeset里。调用select时会把fd_set集合都放到内核态中。

select相对于同步阻塞模型的好处是,他可以简单实现多个socket的IO请求同时处理,用户可以注册多个socket,然后不断调用select来读取被激活的socket。而同步阻塞模型就只能通过多线程(不能用多进程)完成,多线程同步上比较难实现。

问题
1.fd_set集合如果很大那么复制到内核态开销很大,而且频繁调用的话
2.每次在内核态中都要遍历fd_set,如果fd_set很大,遍历的过程开销大
3. 为了减少数据拷贝带来的性能损坏,内核对被监控的fd_set集合大小做了限制1024

  • poll o(n)
    和select最大的区别是取消了fd_set大小的限制。
    基于链表来存储的.

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

typedef struct pollfd {
        int fd;                         // 需要被检测或选择的文件描述符
        short events;                   // 对文件描述符fd上感兴趣的事件
        short revents;                  // 文件描述符fd上当前实际发生的事件} pollfd_t;

使用了pollfd的结构,使得文件描述符集合能够大于1024

  • epoll o(1)
    特点:回调 哈希表 只拷贝一次fd,之后每次epoll_wait不拷贝。
    采用事件通知的方式,当fd就绪,系统注册的回调函数就会被调用,将就绪的fd放到readylist里面。

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

epoll_create 创建一个epoll句柄,size表示要监听的描述符数量
epoll_ctl 注册要监听的事件类型
epoll_wait 函数等待事件就绪

获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了

水平触发LT: 当epoll_wait检测到fd就绪并通知应用程序时,应用程序可以不马上处理该事件,等下一次epoll_wait的时候再次通知该事件

边缘触发ET:当epoll_wait检测到某描述符事件就绪并通知应用程序时,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次通知此事件。
边缘触发优点体现在:当系统中有大量不需要读写的就绪文件描述符的时候,每次调用epoll_wait就都会返回,这样效率会很低;但是必须要注意一次读取就要把所有数据都读出来。

对select缺点的解决

  1. 多次复制fd_set问题。epoll只会在调用epoll_ctl函数时,即注册新的事件到epoll对象里面的时候才会复制fd,就不会每次轮询都要复制。
    2.遍历fd_set问题。 epoll在调用epoll_ctl的时候给每个fd设置了回调函数 当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表。
    epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd(利用schedule_timeout()实现睡一会,判断一会的效果。

  2. 相对于select来说,epoll没有描述符个数限制,使用一个文件描述符管理多个描述符,将用户关心的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

如何追踪运行期间进程的堆栈 gdb的原理

依赖于系统函数ptrace,就是能观察寄存器和内存的内容。
断点的功能是通过内核信号实现的,以x86为例,内核向某个地址打入断点,实际上就是往该地址写入断点指令INT 3,即0xCC。到目标程序运行到该断点处时,产生SIGTRAP信号,该信号被GDB捕获,GDB查找断点列表来确定是否命中断点。继续执行的时候则会把保存的指令重新放回并执行.

gdb维护一个断点链表,如果gdb捕获到断点的信号,那么就说明有中断。
b + 行号 next step continue watch print

gdb是怎么工作的,和内核什么地方有关系?
ptrace的系统调用来实现的,使得父进程能够观察子进程的执行,检测和改变其核心映像和寄存器。

下面我们来看ptrace函数中request参数的一些主要选项:PTRACE_TRACEME: 表示本进程将被其父进程跟踪,交付给这个进程的所有信号,即使信号是忽略处理的(除SIGKILL之外),都将使其停止,父进程将通过wait()获知这一情况。这是什么意思呢?我们可以结合到gdb上来看。如果在gdb中run一个程序,首先gdb会fork一个子进程,然后该子进程调用ptrace系统调用,参数就是PTRACE_TRACEME,然后调用一个exec执行程序。基本过程是这样,细节上可能会有出入。需要注意的是,这个选项PTRACE_TRACEME是由子进程调用的而不是父进程!

trace系统调从名字上看是用于进程跟踪的,它提供了父进程可以观察和控制其子进程执行的能力,并允许父进程检查和替换子进程的内核镜像(包括寄存器)的值。其基本原理是:
在被调试程序和gdb之间建立跟踪关系,当使用了ptrace跟踪后,所有发送给被跟踪的子进程的信号(除了SIGKILL),都会被转发给父进程,而子进程则会被阻塞,这时子进程的状态就会被系统标注为TASK_TRACED。而父进程收到信号后,就可以对停止下来的子进程进行检查和修改,然后让子进程继续运行。

单步跟踪是在每一步执行完一条指令之后就触发一个SIGTRAP信号,让GDB运行

fork 父进程得到的是子进程pid 子进程得到0,。
复制一个虚拟地址空间出来,要修改的时候采用写时复制技术。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章