-
文件传输项目中,秒传的思路?
首先秒传,给我们主观感觉是很大的一个文件比如几个 G,几秒钟的时间就可以上 传完成。其实, 该文件并没有上传到服务器,是别人在服务器已经上传了该文件,只需 要给你一个访问的连接, 所以速度很快。如果之前服务器没有,那就不可能实现秒传。 当然判断文件是否在服务端已存在, 不是靠文件名而是该文件对应的 md5 的值,值一样,说明文件内容一样。所以秒传发送了 md5 值, 服务端比较发现已存在,然后给该 用户一个访问的接口连接就行了,不需要真正上传。 (md5可以将任何一个二进制的数据通过数学算法,转换成一个一定长度(128位)的二进制数据 ; 特点:1、结果是唯一的,不可能重复 2、不可逆 ; 作用: 1、通常用来加密密码 2、通常用来验证一个数据的完整性)
-
sendfile 零拷贝方法原理
sendfile 函数调用只需要两步就可完成文件的发送: 1.sendfile 函数通过 DAM 模块将文件数据从磁盘空间拷贝到内核缓冲区; 2.文件数据并不需要从内核缓冲区复制到 socket 缓冲区,而是将记录数据位置和长度的描述符 加入到 socket 缓冲区,DAM 模块直接将数据从内核缓冲区传递给协议引擎; 虽然数据实际上仍然由磁盘复制到内存,再由内存复制到发送设备,但是,从操作系统的角度来看, 这就是“零拷贝”,因为内核空间不存在冗余数据。应用“零拷贝”特性, 除了避免复制之外, 还有更少的上下文切换,更少的 CPU cache 污染以及没有 CPU 必 要计算校验和。
-
send 和 recv 底层做法
1、send 是利用建立好的 tcp连接进行 发送数据 的系统调用, recv 是利用建立好的 tcp 连接进行 接收数据 的系统调用 2、send 负责将要发送的数据写入对应 套接字文件描述符 的 发送缓冲区, send 成功并不能说明数据发送到了对端,它的返回值是 实际写入发送缓冲区的字节数, 什么时候发送给对端 由底层协议完成。如果缓冲区满则有可能阻塞 send; send 在在内核中最终通过 __sock_sendmsg,将数据写入相应的缓冲区; 3、recv 是从 文件描述符对应的 接收缓冲区 读取数据,读取多少由 缓冲区当前数据量 和 应用程序期望的读取的字节数决定,取小者。 recv 在内核中最终通过__sock_recvmsg, 从缓冲区读到数据,再拷贝到用户空间。
-
实模式和保护模式
1、 实模式:以 8086 为代表,寻址方式为 段寄存器<<4 + 偏移量= 物理地址(线性地址), 该模式下对内存的访问没有任何的保护和控制,任意程序都可以访问整个内存空间。为了把这种模式 和后来出现的保护模式区别,则称其为实地址模式。 2、保护模式:以 80386 为代表,段寄存器得到的不是段基地址,而是段描述符表的下标, 及当前是处于内核态还是用户态,有权限的控制。通过查 段描述符表可以得到 “段的基地址”, 然后 ,段的基地址+ 偏移量 =线性地址;此处得到的线性地址并不是直接作用于物理地址, 而是通过分页的方式,将线性地址再映射到物理页面得到物理地址, 在这一步会有读写等等权限的控制在里面。
-
页面置换算法有那些?
1、页面置换算法: 地址映射过程中,当 CUP 需要访问的页面不在内存中,则发生 缺页中断!当缺页中断发生后, 内存中没有空闲的页面,则必须选择一个页面将其移出 内存(如果选择的页面上的数据没有被修改, 则直接用新页覆盖,如果修选择的页面上 的数据被修改了,则必须将其置换到虚拟空间上), 为访问的页面腾出空间。选择移出 页面的算法称为页置换算法。 2、设计页面置换算法的目标: 1.降低随后发生缺页中断的次数或者概率。选择的页面在随后相当长时间不会被访问到, 最好是再也不会被访问; 2.应该选择一个没有修改过的页面,这样,替换时就无需将被替换的页面内容写回到磁盘, 从而进一步加快缺页中断的响应时间。 3、常用的页面置换算法有: 1.OPT(最佳置换算法) 2.FIFO(先进先出置换算法) 3.第二次机会算法(Second Chance) 4.NRU(最近未使用算法) 5.LRU(最近最少使用置换算法) 6.时钟(CLOCK)置换算法 7.工作集算法 8.工作集时钟算法
-
LRU 的实现策略?
1、LRU 全称: Least Recently Used ,即就是“最近最少使用”。 2、LRU 应用在缓存机制中进行选择被替换的元素的一种策略。简而言之,就是选择一个最近 最少使用的元素淘汰,在这个元素原有的位置添加新的元素。一般的实现,都是采用对存储在 内存的元素采用 'age bits’ 来标记该元素从上次访问到现在为止的时长, 从而在每次用 LRU 淘汰时,淘汰这些最长时间未被访问的元素。
-
服务器可能产生那些瓶颈?
1、以下列几种可能产生瓶颈的方面 : 1.磁盘I/O 瓶颈; 2.内存不足; 3.cpu 出现瓶颈,持续 100% ; 4.应用程序:有些程序在开发过程中,不注意运行效率,代码比较粗暴, 导致服务器一旦运行该应用,大量资源被消耗; 5.数据库使用出现瓶颈 ; 6.多媒体服务通常需要很高的带宽,这也容易引起瓶; 磁盘 io 瓶颈,解决办法如下: 1) 对于经常读取的数据,尽量可以缓存在内存中以减少对 IO 设备进行访问 的次数; 2) 如果是单个存储磁盘自身速度慢,可以更换更高存储速度的磁盘 ; 3) 使用磁盘阵列,它可以在多个磁盘上同时读写,极大的提高了吞吐量 ; 4) 采用分布式存储 ; 内存不足,解决方法如下: 1)是否有内存泄露; 2)是否需要选择合理的内存管理方法;
-
Linux 文件系统
1、Linux 操作系统的文件子系统采用的是索引式文件系统 Ext2(目前到了 ext4), 将磁盘格式化成 Ext2 文件系统格式的时候,会划分出三个区域: superblock 、inode 、 block。 2、 superblock:记录文件系统的详细信息以及 inode&block 的总量、使用量和剩余量。 3、inode:记录文件的属性信息以及文件的权限信息。 4、 block: 存储文件的真实数据。
-
Linux 内核的文件 cache 管理机制
1、文件 cache 是文件数据在内存中的副本,这里涉及两方面的内容: 内存的管理和文件系统,文件 cache 就将两者联系起来; 2、 在 linux 系统中,某个程序需要读取文件中的数据时,操作系统先在内存中分配一 些空间, 然后再将数据从磁盘读入到这块空间,应用程序实际从这块内存空间读数据。 当程序向文件中 写入数据时也是先将数据写到内存,再从内存更新到磁盘上。文件的 cache 管理就指的是对 这些由 操作系统分配,并用来存储文件数据的内存 的管理。 3、文件 cache 管理的好坏由两个标准衡量,一是命中率,一是有效 cache 的比率。 文件 cache 的实现分两层,一是pagecache,一是buffercache。每一个pagecache包含若干 buffcache, 内存管理系统负责维护 每项 page cache 的分配和回收。同时再使用 memory map 方式访问时负责建立映射。还有一个需要考虑的问题是文件 cache 的预读和替换。
-
Epoll 内核实现中用到那些数据结构?
epoll 功能的实现分了三个系统调用完成: 1、epoll_create:创建内核事件表用于存放描述符和关注的事件; 主要数据结构有: struct eventpoll; 其中有两个重要的成员 struct rb_root rbr; 和 struct list_head rdllist; 前者是一棵红黑树,也就是内核事件表,后者是就绪事件存放的队列。 2、epoll_ctl : 为红黑树添加 ep_insert、移除 ep_remove、修改 ep_modify 节点的操作; 每个节点就是一个描述符和事件的结构体,数据结构如:struct epitem; 3、epoll_wait : 负责收集就绪事件,注意是收集,所以它并没有太多的事情做,效率自然也高, 那数据是怎么就绪昵?第二步在添加事件和描述符时,注册了回调函数, 当关注的描述符上有事件就绪,就将该描述符和事件放到就绪队列, 也就是第一步创建的 rdllist 中。
-
Epoll 的 et 模式和 lt 模式区别,及内核实现上的不同?
1、LT 模式下:描述符上事件就绪后,如果没有把数据处理完成,或者没有处理, 下一次 epoll 会继续提醒应用程序,直到把数据读完; 2、ET 模式下:描述符上事件就绪后,如果没有把数据处理完成,或者没有处理, 下一 次 epoll 不会提醒应用层序,所以要求应用程序在收到一次提醒时, 必须当下将所有数 据处理完成 3、 内核实现: epoll_wait 每次将收集到的就绪事件和描述符返回给应用程序,过程是这样 : 当检测到 rdlist 不为空时,就说明有事件就绪,使用 ep_collect_ready_items 方法 将就绪队列 rdlist 中的数据挪到 txlist 中,此时,rdlist 为空;再通过 ep_send_events 方法将就绪事件返回给应用程序,同时又会掉用 ep_reinject_items 方法将一些有设置 EPOLLLT 的事件的描述符又放回到 rdlist,这样下一轮,epoll_wait 会发现有就绪事件, 但会再次检查是否有数据,有就返回,没有继续阻塞。但设置了 EPOLLET 事件,相应的描述符在 ep_reinject_items 方法中不会被放回 rdlist,只有等设备驱动程序检查到有 事件产生 才会再次将事件放到 rdlist。所以,如果 ET 模式数据没有读完不会放回 rdlist, 那么也就不会再去检查是否有数据,提醒应用程序了,直到下次设备驱动程序检查到了 事件才会放入 rdlist。
-
主机字节序列转网络字节序列?
由于网络中不同主机,字节序列可能不同,所以在传输端口号的时候,规定统一将转为大端, 也就是把大端作为网络字节序列。 有四个方法可以使用: htons 主机字节序列转网络字节序列 短整形 ; htonl 主机字节序列转网络字节序列 长整形 ; ntohs 网络字节序列转主机字节序列 短整形; ntohl 网络字节序列转主机字节序列 长整形 ;
-
fork复制过程:
1、分配pid
2、分配进程描述符(pcb),同时分配好内核栈
3、复制进程实体,即:打开的文件、文件目录、信号信息、进程地址空间等
4、用父进程内核栈上存放的现场信息,初始化为子进程的现场信息,并将eax置 0
5、将父进程时间片分子进程一半,设置进程状态为就绪
-
文件描述符对服务器的作用
在 Linux 中,一切皆文件。打开磁盘上的普通文件,管道文件,或者建立一个套接字连接, 都需要用到一个文件描述符来标识,对于服务器来说,剩余文件描述符的多少也可以理解为 能和客户端建立的连接数。
-
写入管道的数据会乱序吗?
管道是个流式的,依次写入,依次顺序读取。数据不会乱序, 但多线程中如果两个线程同时写入,数据可能会交替在一起。
-
管道为什么是半双工的?
1、由于 Linux 一个命令只能完成一个功能,所以一个复杂的任务需要好几个进程协同完成, 第一个进程的处理结果需要交给第二个进程,然后依次交给第三个,等等,就像流水线完成某个商品 的生产一样,这个过程只需要数据单向往下传输,所以设计的时候做成了半双工。 2、当然也可以使用管道实现全双工通讯,需要使用两个管道,比较麻烦。如果需要使用全双工通信, 建议使用双向管道,即socketpair,它虽然名字叫双向管道,可并不是管道,而是两个打开的套接字。
-
Linux 进程核心 转储文件的调试coredump
一般调试程序常用的就是ddb工具,有时候程序太大,运行时突然崩掉,不方便跟踪,此时可以使用 core 文件进行定位运行时的错误;但如果是逻辑上的错误,运行结果不正确,程序也没有崩溃是 不能使用 core 文件进行定位的。 1、设置 core 文件大小,默认为 0 ,即程序运行出错不生成 core 文件,也就无法调试;开启命令为 ulimit -c size, size 为设置 core 文件的大小, 当 core 文件超过该值时, 不生成 core 文件;也可以设置为 ulimit -c unlimited 不限制大小。 2、运行要调试的程序,程序错误崩溃后,自动生成 core 文件,如 core.5463,后缀为进程 pid 3、假设程序名称叫 main ,则执行 gdb main core.5463 4、gdb bt 可以看到函数调用栈信息,即可看到出错位置
-
生产者消费者模型
1、生产者/消费者模型描述的是有一块缓冲区作为仓库,生产者可将产品放入仓库, 消费者可以从仓库中取出产品。 2、 生产者/消费者模型关注的是以下几个点 1.生产者生产的时候消费者不能消费 2.消费者消费的时候生产者不能生产 3.缓冲区空时消费者不能消费 4.缓冲区满时生产者不能生产 3、优点: 1.解耦。因为多了一个缓冲区,所以生产者和消费者并不直接相互调用,这一点很容易想到, 这样生产者和消费者的代码发生变化,都不会对对方产生影响,这样其实就把生产者和消费者 之间的强耦合解开,变为了生产者和缓冲区/消费者和缓冲区之间 的弱耦合 2.通过平衡生产者和消费者的处理能力来提高整体处理数据的速度,这是生产者/消费者模型最重要 的一个优点。如果消费者直接从生产者这里拿数据,如果生产者生 产的速度很慢,但消费者 消费的速度很快,那消费者就得占用CPU的时间片白白等在那边。有了生产者/消费者模型, 生产者和消费者就是两个独立的并发体,生产者把生产出来的数据往缓冲区一丢就好了, 不必管消费者;消费者也是,从缓冲区去拿数据就 好了,也不必管生产者,缓冲区满了 就不生产,缓冲区空了就不消费,使生产者/消费 者的处理能力达到一个动态的平衡 。
-
线程安全
1、线程安全即就是在多线程运行的时候,不论线程的调度顺序怎样,最终的结果都是一样的、正确的。 那么就说这些线程是安全的。 2、要保证线程安全需要做到: 1.对线程同步,保证同一时刻只有一个线程访问临界资源。 2.在多线程中使用线程安全的函数。
-
如何避免死锁
1、死锁的概念:多个进程或线程访问一组静态资源时,出现永久阻塞的问题。 2、死锁的原因: 1.系统资源不足; 2.程序运行推进的顺序不当; 3.资源分配不当。 3、产生死锁的条件: 1.互斥; 2.请求与保持; 3.不可抢占; 4.循环等待; 避免死锁就是要打破这四个条件中的某一个即可。如某个进程申请多个资源,只要有一个资源 不满足暂时就不要分配任何资源,等所有资源能满足时一起分配。
-
fcntl方法可以做什么?
int fcntl(int fd,int cmd,...) //参数 fd 代表欲设置的文件描述符 1、复制文件描述符 2、设置文件描述符标志位 3、设置文件描述符对应的文件状态标志,如阻塞非阻塞等 4、设置与异步i/o有关的属性信息 5、设置文件锁
-
Linux 调试,程序断点实现思路?
gdb中attach 到已经存在的进程进行在线调试,能获取当前栈的所有变量值。 gdb attach pid Linux 提供了系统调用 ptrace() ,它可以提供一个进程 跟踪控制另一个进程的方法, 并可以检查和改变 被跟踪进程 内存和寄存器的数据,它可以用来实现断点调试的功能。 原理是 attach 在正在运行的进程使其停止,然后读取进程的指令寄存器内容所指向的指令, 备份后替换成目标指令,在使其继续执行,此时被追踪进程就会执行我们替换的指令, 运行完成注入的指令后,再恢复原进程的指令寄存器的值,从而达到改变运行原程序逻辑的目的。
-
程序,进程,线程的区别?
1、程序:存储在磁盘上的二进制可执行文件。 2、进程:是一个正在运行的程序,是系统进行资源分配的基本单位。它是动态的; 而程序是一个静态的文件,进程是程序的一次实例化,一个程序可执行多次并生成多个进程。 3、线程:是进程内部的一条执行路径,是系统进行调度的基本单位。在操作系统中将线程的实现分成了 三类:用户级线程 内核级线程 混合方式级线程。 4、在Linux中线程的实现如下: Linux实现线程的机制非常独特,从内核的角度来说,它并没有线程这个概念。Linux把所有的线程 都当进程来实现。内核并没有准备特别的调度算法或是定义特别的数据结构来表示线程。 相反线程仅仅被视为一个与其他进程共享某些资源的进程。每个线程都拥有唯一隶属于自己 的task_struct,所以在内核中,它看起来就像一个普通的进程,只是该进程和其他一些进程 共享某些资源,如地址空间;
-
多线程的不足之处
引用多线程后,系统的执行路径变成了多条,并且这多条执行路径在并发运行。 1.程序的执行具有一定的不确定性,每次执行结果可能都会不同, 因为程序交替执行的顺序和时机不同了。 2.使得某些资源的访问出现了竞争,问题变得困难,需要资源进行同步, 这会使得程序的可靠性和稳定性降低。