当前位置: 首页 > news >正文

【烈日炎炎战后端】操作系统(1.1万字)

操作系统

        • 1. 讲一下并发和并行?
        • 2. 同步、异步、阻塞、非阻塞
        • 3. BIO,NIO,AIO,多路复用IO?
        • 4. 讲一下线程和进程的区别和联系?
        • 4. 讲一下线程状态并且解释一下?
        • 5. 讲一下进程间通讯方式?
        • 6. 进程的调度算法有哪些?
        • 7. 线程同步的四种方式
        • [<死锁专题>]
          • 1. 什么是死锁?
          • 2. 死锁产生的必要条件?
          • 3. 解决死锁的基本方法?

1. 讲一下并发和并行?

并行是指两个或者多个事件在同一时刻发生:

img

并发是指两个或多个事件在同一时间线内间隔发生:

img

单核 cpu 下,线程实际还是串行执行的。操作系统中有一个组件叫做任务调度器,将 cpu 的时间片(windows下时间片最小约为 15 毫秒)分给不同的程序使用,只是由于 cpu 在线程间(时间片很短)的切换非常快,人类感觉是同时运行的 。总结为一句话就是: 微观串行,宏观并行 。

并行在多处理器系统中存在,而并发可以在单处理器和多处理器系统中都存在,并发能够在单处理器系统中存在是因为并发是并行的假象,并行要求程序能够同时执行多个操作,而并发只是要求程序假装同时执行多个操作(每个小时间片执行一个操作,多个操作快速切换执行)。

link

同一时间/间隔:其实应该这么断句,同一时间/ 间隔发生。同一时间是一起的,间隔发生是一起的。同一时间的意思就是同一条时间段上,间隔发生的意思就是,在这同一个时间段上,有不同的事件间隔发生。

补充解释:

并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。

并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是从宏观来看,二者都是一起执行的。

并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行。

并发(concurrent)是同一时间应对(dealing with)多件事情的能力

并行(parallel)是同一时间动手做(doing)多件事情的能力

2. 同步、异步、阻塞、非阻塞

https://blog.csdn.net/historyasamirror/article/details/5778378

同步 & 异步:同步与异步是针对多个事件(线程/进程)来说的。

同步:如果事件A需要等待事件B的完成才能完成,这种串行执行机制可以说是同步的,这是一种可靠的任务序列,要么都成功,要么都失败。
异步:如果事件B的执行不需要依赖事件A的完成结果,这种并行的执行机制可以说是异步的。事件B不确定事件A是否真正完成,所以是不可靠的任务序列。
区别:同步异步可以理解为多个事件的执行方式和执行时机如何,是串行等待还是并行执行。同步中依赖事件等待被依赖事件的完成,然后触发自身开始执行,异步中依赖事件不需要等待被依赖事件,可以和被依赖事件并行执行,被依赖事件执行完成后,可以通过回调、通知等方式告知依赖事件。

阻塞 & 非阻塞:阻塞与非阻塞是针对单一事件(线程/进程)来说的。

阻塞:如果一个事件在发起一个调用之后,在调用结果返回之前,该事件会被一直挂起,处于等待状态。
非阻塞:如果一个事件在发起调用以后,无论该调用当前是否得到结果,都会立刻返回,不会阻塞当前事件。
区别:阻塞与非阻塞可以理解为单个事件在发起其他调用以后,自身的状态如何,是苦苦等待还是继续干自己的事情。非阻塞虽然能提高CPU利用率,但是也带来了系统线程切换的成本,需要在CPU执行时间和系统切换成本之间好好估量一下。

同步/异步/阻塞/非阻塞 IO 的区别?

https://www.zhihu.com/question/19732473

同步(Synchronous):当一个同步调用发出后,调用者要一直等待返回结果。通知后,才能进行后续的执行。

异步( Asynchronous):当一个异步过程调用发出后,调用者不能立刻得到返回结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。

阻塞( Blocking ):是指调用结果返回前,当前线程会被挂起,即阻塞。

非阻塞( Nonblocking):是指即使调用结果没返回,也不会阻塞当前线程。

3. BIO,NIO,AIO,多路复用IO?

原文链接:https://blog.csdn.net/historyasamirror/article/details/5778378

1. 什么是IO?

在计算机系统中I/O就是输入(Input)和输出(Output)的意思,只要具有输入输出类型的交互系统都可以认为是I/O系统。

2. 如何理解IO过程?

对于一个network IO (这里我们以read举例),它会涉及到两个系统对象,一个是调用这个IO的进程(process )(or thread),另一个就是系统内核(kernel)。当一个read操作发生时,它会经历两个阶段:

  1. 进程调用系统内核并等待数据准备 (Waiting for the data to be ready)
  2. 系统内核准备好数据,将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)
    记住这两点很重要,因为这些IO Model的区别就是在两个阶段上各有不同的情况。
  1. **BIO (Blocking I/O): 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。**当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据。对于network io来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候kernel就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。所以,blocking IO的特点就是在IO执行的两个阶段都被block了。

同步阻塞I/O模型.png-31.4kB

  1. NIO (New I/O): NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 SocketServerSocket 相对应的 SocketChannelServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i0Hf8J2J-1596674706973)(http://static.zybuluo.com/rainybowe/0agaz3rfsrlgxr5b5jtt4k1c/%E6%9C%AA%E5%91%BD%E5%90%8D%E6%96%87%E4%BB%B6%20(1)].png)

  1. AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。查阅网上相关资料,我发现就目前来说 AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,不过又放弃了。
未命名文件 (2).png-33.2kB
  1. **IO多路复用(IO multiplexing)IO multiplexing这个词可能有点陌生,但是如果我说select,epoll,大概就都能明白了。有些地方也称这种IO方式为event driven IO。我们都知道,select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。**它的基本原理就是select/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。

    当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。
    这个图和blocking IO的图其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个system call (select 和 recvfrom),而blocking IO只调用了一个system call (recvfrom)。但是,用select的优势在于它可以同时处理多个connection。(多说一句。所以,如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。
    在IO multiplexing Model中,实际中,对于每一个socket,一般都设置成为non-blocking,但是,如下图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。

I/O复用模型详解(网络总结)

附区别对比1:

2

附区别对比2:

img

4. 讲一下线程和进程的区别和联系?

link1,link2

进程

程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至 CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程(例如记事本、画图、浏览器等),也有的程序只能启动一个实例进程(例如网易云音乐、360 安全卫士等)

线程

一个进程之内可以分为一到多个线程。一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给 CPU 执行Java 中,线程作为最小调度单位,进程作为资源分配的最小单位。 在 windows 中进程是不活动的,只是作为线程的容器。

  1. 操作系统中的一个“执行中的程序”就是进程,它是操作系统资源分配的最小的单位。而线程是进程的一个子集它是CPU调度和执行的最小单位

  2. 线程相对进程更轻量,特别是上下文切换,进程要比线程消耗更多的计算机资源。

    • 进程切换时,涉及到当前进程的CPU环境的保存和新被调度运行进程的CPU环境的设置;
    • 线程切换时,仅需要保存和设置少量的寄存器内容,不涉及存储管理方面的操作。
  3. 数据共享方面,同一进程下不同线程间数据很易共享,不同进程的线程间数据很难共享

    • 线程通信相对简单,因为它们共享进程内的内存
    • 进程间通信较为复杂,同一台计算机的进程通信称为 IPC(Inter-process communication),不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如 HTTP

【拓展】什么是协程?

协程,是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。

4. 讲一下线程状态并且解释一下?

操作系统层面下,分为五种状态:

image-20200624161235829
  1. 新建(NEW):新创建了一个线程对象,还未与操作系统线程关联。
  2. 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了线程对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。
  3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片1(timeslice) ,执行程序代码。
  4. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:
    (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
    (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
    (三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
  5. 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

5. 讲一下进程间通讯方式?

参考网站:https://blog.csdn.net/weixin_43730678/article/details/89061538

  1. 无名管道(PIPE):管道是一种半双工的通信方式(数据只能单向流动),而且只能在具有亲缘关系的进程(即只能在父子进程和兄弟进程之间通讯)间使用。进程的亲缘关系通常是指父子进程关系

  2. 命名管道(FIFO):有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

  3. 信号(signal)信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身.

  4. 信号量(semaphore)信号量是个计数器记录临界资源的个数,用于多进程对共享数据的访问。进程访问临界资源时控制,用来实现进程的同步控制。

  5. 消息队列MessageQueue:消息队列是消息的链接表,存放在内核中并有消息队列标识符标识。。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

  6. 共享内存SharedMemory:共享内存允许两个或多个进程共享一定的存储区,因为数据不需要在进程之间复制,所以他是最快的IPC。

信号量底层是怎么实现的?

信号和信号量有什么区别?

信号:是由用户、系统或者进程发送给目标进程的信息,以通知目标进程某个状态的改变或系统异常
信号量:信号量是一个特殊的变量,它的本质是计数器,信号量里面记录了临界资源的数目,有多少数目,信号量的值就为多少,进程对其访问都是原子操作(pv操作,p:占用资源,v:释放资源)。它的作用就是,调协进程对共享资源的访问,让一个临界区同一时间只有一个进程在访问它。

所以它们两的区别也就显而易见了,信号是通知进程产生了某个事件,信号量是用来同步进程的(用来调协进程对共享资源的访问的)

6. 进程的调度算法有哪些?

  • 先来先服务调度算法:先来先服务调度算法是一种最简单的调度算法,也称为先进先出或严格排队方案。当每个进程就绪后,它加入就绪队列。当前正运行的进程停止执行,选择在就绪队列中存在时间最长的进程运行。该算法既可以用于作业调度,也可以用于进程调度。先来先去服务比较适合于常作业(进程),而不利于段作业(进程)。

  • 时间片轮转调度算法:时间片轮转调度算法主要适用于分时系统。在这种算法中,系统将所有就绪进程按到达时间的先后次序排成一个队列,进程调度程序总是选择就绪队列中第一个进程执行,即先来先服务的原则,但仅能运行一个时间片

  • 短作业优先调度算法:短作业优先调度算法是指对短作业优先调度的算法,从后备队列中选择一个或若干个估计运行时间最短的作业,将它们调入内存运行。 短作业优先调度算法是一个非抢占策略,他的原则是下一次选择预计处理时间最短的进程,因此短进程将会越过长作业,跳至队列头。

  • 最短剩余时间优先调度算法:最短剩余时间是针对最短进程优先增加了抢占机制的版本。在这种情况下,进程调度总是选择预期剩余时间最短的进程。当一个进程加入到就绪队列时,他可能比当前运行的进程具有更短的剩余时间,因此只要新进程就绪,调度程序就能可能抢占当前正在运行的进程。像最短进程优先一样,调度程序正在执行选择函数是必须有关于处理时间的估计,并且存在长进程饥饿的危险。

  • 高响应比优先调度算法:高响应比优先调度算法主要用于作业调度,该算法是对先来先服务调度算法和短作业优先调度算法的一种综合平衡,同时考虑每个作业的等待时间和估计的运行时间。在每次进行作业调度时,先计算后备作业队列中每个作业的响应比,从中选出响应比最高的作业投入运行。

    响应比 =(等待时间+要求服务时间)/ 要求服务时间,即RR=(w+s)/s=1+w/s,因此响应比一定是大于等于1的。

  • 优先级调度算法:优先级调度算法每次从后备作业队列中选择优先级最高的一个或几个作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。在进程调度中,优先级调度算法每次从就绪队列中选择优先级最高的进程,将处理机分配给它,使之投入运行。

先时短最高优

7. 线程同步的四种方式

原文链接:https://blog.csdn.net/qq_40261882/article/details/100538711

1、临界区: 通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。它并不是核心对象,不是属于操作系统维护的,而是属于进程维护的。

总结下关键段:
1)关键段共初始化化、销毁、进入和离开关键区域四个函数。
2)关键段可以解决线程的互斥问题,但因为具有“线程所有权”,所以无法解决同步问题。
3)推荐关键段与旋转锁配合使用。

2、互斥对象: 互斥对象和临界区很像,采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程同时访问。当前拥有互斥对象的线程处理完任务后必须将线程交出,以便其他线程访问该资源。

总结下互斥量Mutex:
1)互斥量是内核对象,它与关键段都有“线程所有权”所以不能用于线程的同步。
2)互斥量能够用于多个进程之间线程互斥问题,并且能完美的解决某进程意外终止所造成的“遗弃”问题。

3、信号量: 信号量也是内核对象。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目

在用CreateSemaphore()创建信号量时即要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最 大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1 ,只要当前可用资源计数是大于0 的,就可以发出信号量信号。但是当前可用计数减小 到0 时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离 开的同时通过ReleaseSemaphore ()函数将当前可用资源计数加1 。在任何时候当前可用资源计数决不可能大于最大资源计数。

4、事件对象: 通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作

总结下事件Event
1)事件是内核对象,事件分为手动置位事件和自动置位事件。事件Event内部它包含一个使用计数(所有内核对象都有),一个布尔值表示是手动置位事件还是自动置位事件,另一个布尔值用来表示事件有无触发。
2)事件可以由SetEvent()来触发,由ResetEvent()来设成未触发。还可以由PulseEvent()来发出一个事件脉冲。
3)事件可以解决线程间同步问题,因此也能解决互斥问题。

[<死锁专题>]

1. 什么是死锁?

死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 如下图所示:如果此时有一个线程 A,已经持有了锁 A,但是试图获取锁 B,线程 B 持有锁 B,而试图获取锁 A,这种情况下就会产生死锁。

2. 死锁产生的必要条件?
  1. 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
  2. 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
  4. 环路等待条件:在发生死锁时,必然存在一个进程–资源的环形链。

互请不还

3. 解决死锁的基本方法?
  1. 预防死锁----- 确保系统永远不会进入死锁状态

    破坏请求条件:一次性分配所有资源,这样就不会再有请求了;

    破坏保持条件:只要有一个资源得不到分配,也不给这个进程分配其他的资源:

    破坏不可剥夺条件:当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源;

    破坏环路等待条件:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反。

  2. 避免死锁----- 在使用前进行判断,只允许不会产生死锁的进程申请资源

    银行家算法:当进程首次申请资源时,要测试该进程对资源的最大需求量,如果系统现存的资源可以满足它的最大需求量则按当前的申请量分配资源,否则就推迟分配。
    当进程在执行中继续申请资源时,先测试该进程已占用的资源数与本次申请资源数之和是否超过了该进程对资源的最大需求量。若超过则拒绝分配资源。若没超过则再测试系统现存的资源能否满足该进程尚需的最大资源量,若满足则按当前的申请量分配资源,否则也要推迟分配。
    安全序列:是指系统能按某种进程推进顺序(P1, P2, P3, …, Pn),为每个进程 Pi 分配其所需要的资源,直至满足每个进程对资源的最大需求,使每个进程都可以顺序地完成。这种推进顺序就叫安全序列【银行家算法的核心就是找到一个安全序列】。
    系统安全状态 :如果系统能找到一个安全序列,就称系统处于安全状态,否则,就称系统处于不安全状态。

  3. 检测死锁

  4. 解除死锁

    资源剥夺:挂起某些死锁进程,并抢占它的资源,将这些资源分配给其他死锁进程(但应该防止被挂起的进程长时间得不到资源);

    撤销进程:强制撤销部分、甚至全部死锁进程并剥夺这些进程的资源(撤销的原则可以按进程优先级和撤销进程代价的高低进行);

    进程回退:让一个或多个进程回退到足以避免死锁的地步。进程回退时自愿释放资源而不是被剥夺。要求系统保持进程的历史信息,设置还原点。


  1. 时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段,称作它的时间片,即该进程允许运行的时间,使各个程序从表面上看是同时进行的。如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。而不会造成CPU资源浪费。在宏观上:我们可以同时打开多个应用程序,每个程序并行不悖,同时运行。但在微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,每个程序轮流执行。 ↩︎

相关文章:

  • for while (list each)的用法
  • 【烈日炎炎战后端】设计模式(1.1万字)
  • 【烈日炎炎战后端】 数据结构(0.7万字)
  • JavaScript学习总结——原型
  • 2的幂在约瑟夫环问题的应用
  • 【烈日炎炎战后端】MySQL理论(2.8万字)
  • Mysql5.6主从复制
  • 【烈日炎炎战后端】MySQL编程(3.6万字)
  • 【Mongodb】Master-Slave 复制
  • 解决前端文件修改后浏览器页面未更新的问题
  • 【烈日炎炎战后端】Redis(6.1万字)
  • UIScrollView视差模糊效果
  • 真正的上锁前,为何要调用preempt_disable()来关闭抢占的case【转】
  • 【烈日炎炎战后端】Linux(0.3万字)
  • POJ3159 Candies(最短路径:SPFA+链表+栈)
  • 「译」Node.js Streams 基础
  • ES6语法详解(一)
  • exif信息对照
  • IP路由与转发
  • JavaScript-Array类型
  • JavaWeb(学习笔记二)
  • Java新版本的开发已正式进入轨道,版本号18.3
  • Linux中的硬链接与软链接
  • MySQL用户中的%到底包不包括localhost?
  • webpack入门学习手记(二)
  • 大整数乘法-表格法
  • 关于Android中设置闹钟的相对比较完善的解决方案
  • 关于Flux,Vuex,Redux的思考
  • 前端性能优化--懒加载和预加载
  • 使用common-codec进行md5加密
  • 在Mac OS X上安装 Ruby运行环境
  • 第二十章:异步和文件I/O.(二十三)
  • 京东物流联手山西图灵打造智能供应链,让阅读更有趣 ...
  • 如何在招聘中考核.NET架构师
  • ​​​​​​​​​​​​​​Γ函数
  • ​queue --- 一个同步的队列类​
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • ​VRRP 虚拟路由冗余协议(华为)
  • # 达梦数据库知识点
  • $con= MySQL有关填空题_2015年计算机二级考试《MySQL》提高练习题(10)
  • (02)Hive SQL编译成MapReduce任务的过程
  • (1)bark-ml
  • (7)STL算法之交换赋值
  • (c语言)strcpy函数用法
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第5节(封闭类和Final方法)
  • (poj1.3.2)1791(构造法模拟)
  • (附源码)php新闻发布平台 毕业设计 141646
  • (七)c52学习之旅-中断
  • (区间dp) (经典例题) 石子合并
  • (转)scrum常见工具列表
  • (转)创业的注意事项
  • (转)可以带来幸福的一本书
  • (转)淘淘商城系列——使用Spring来管理Redis单机版和集群版
  • (最完美)小米手机6X的Usb调试模式在哪里打开的流程
  • ***php进行支付宝开发中return_url和notify_url的区别分析