zshuangyan / blog

我的个人博客
2 stars 0 forks source link

会议室预订系统和ip分片重组的hole descriptor list算法(rfc815) #22

Open zshuangyan opened 5 years ago

zshuangyan commented 5 years ago

在网上看到淘宝的一个面试题,实现会议室预订系统。类似会议室预订的,还有汽车出租系统,本质上都是都某个资源的时间占用问题。

我们排除一些细节问题,把问题缩小为某一天去预订一个会议室,在没有发生任何预订的情况下,这个会议室的时间片是一段连续的时间【00:00:00】到【23:59:59】,我们称之为时间片ST。

新产生的预订可能从中间切分ST,生成两段新的时间片。例如,预订时间是【08:00:00】到【09:000:00】,那么会切分出【00:00:00】到【07:59:59】,【09:00:01】到【23:59:59】。也可能刚好从时间片的开头占用,或者占用到时间片的结尾,或者是占用完整的时间片。

随着越来越多的预订,剩下的可以被占用的时间片将是被占用时间分割开的一个个小的时间片,当新的会议室请求到来时,我们要从这一个个时间片中寻找可以满足预订要求的时间片(即时间片的开始时间<=会议预订的开始时间&时间片的结束时间>=会议预订的结束时间),找到满足要求的时间片后,把这个旧的时间片基于当前的占用时间进行切分。

最近看IP相关的资料时,发现在rfc815中提供的重组机制竟然和自己之前为实现会议室预订系统的算法惊人地相似,下面是具体的逻辑:

    An arriving fragment can fill any of the existing holes in a number

of ways.  Most simply, it can completely fill a hole.  Alternatively, it

may  leave some remaining space at either the beginning or the end of an

existing hole.  Or finally, it can lie in  the  middle  of  an  existing

hole,  breaking the hole in half and leaving a smaller hole at each end.

Because of these possibilities, it might seem that  a  number  of  tests

must  be  made  when  a  new  fragment  arrives,  leading  to  a  rather

complicated algorithm.  In fact, if properly  expressed,  the  algorithm

can compare each hole to the arriving fragment in only four tests.

                                   4

     We  start  the algorithm when the earliest fragment of the datagram

arrives.  We begin by creating an empty data buffer area and putting one

entry in its  hole  descriptor  list,  the  entry  which  describes  the

datagram  as  being completely missing.  In this case, hole.first equals

zero, and hole.last equals infinity. (Infinity is presumably implemented

by a very large integer, greater than 576, of the implementor's choice.)

The following eight steps are then used to insert each of  the  arriving

fragments  into  the  buffer  area  where the complete datagram is being

built up.  The arriving fragment is  described  by  fragment.first,  the

first  octet  of  the fragment, and fragment.last, the last octet of the

fragment.

   1. Select the next hole  descriptor  from  the  hole  descriptor
      list.  If there are no more entries, go to step eight.

   2. If fragment.first is greater than hole.last, go to step one.

   3. If fragment.last is less than hole.first, go to step one.

         - (If  either  step  two  or  step three is true, then the
           newly arrived fragment does not overlap with the hole in
           any way, so we need pay no  further  attention  to  this
           hole.  We return to the beginning of the algorithm where
           we select the next hole for examination.)

   4. Delete the current entry from the hole descriptor list.

         - (Since  neither  step  two  nor step three was true, the
           newly arrived fragment does interact with this  hole  in
           some  way.    Therefore,  the current descriptor will no
           longer be valid.  We will destroy it, and  in  the  next
           two  steps  we  will  determine  whether  or  not  it is
           necessary to create any new hole descriptors.)

   5. If fragment.first is greater than hole.first, then  create  a
      new  hole  descriptor "new_hole" with new_hole.first equal to
      hole.first, and new_hole.last equal to  fragment.first  minus
      one.

                                   5

         - (If  the  test in step five is true, then the first part
           of the original hole is not filled by this fragment.  We
           create a new descriptor for this smaller hole.)

   6. If fragment.last is less  than  hole.last  and  fragment.more
      fragments   is  true,  then  create  a  new  hole  descriptor
      "new_hole", with new_hole.first equal to  fragment.last  plus
      one and new_hole.last equal to hole.last.

         - (This   test  is  the  mirror  of  step  five  with  one
           additional feature.  Initially, we did not know how long
           the reassembled datagram  would  be,  and  therefore  we
           created   a   hole   reaching  from  zero  to  infinity.
           Eventually, we will receive the  last  fragment  of  the
           datagram.    At  this  point, that hole descriptor which
           reaches from the last octet of the  buffer  to  infinity
           can  be discarded.  The fragment which contains the last
           fragment indicates this fact by a flag in  the  internet
           header called "more fragments".  The test of this bit in
           this  statement  prevents  us from creating a descriptor
           for the unneeded hole which describes the space from the
           end of the datagram to infinity.)

   7. Go to step one.

   8. If the hole descriptor list is now empty, the datagram is now
      complete.  Pass it on to the higher level protocol  processor
      for further handling.  Otherwise, return.

     4.  Part Two:  Managing the Hole Descriptor List

     The  main  complexity  in  the  eight  step  algorithm above is not

performing the arithmetical tests, but in adding  and  deleting  entries

from  the  hole descriptor list.  One could imagine an implementation in

which the storage management package was  many  times  more  complicated

than  the rest of the algorithm, since there is no specified upper limit

on the number of hole descriptors which will exist for a datagram during

reassembly.   There  is  a  very  simple  way  to  deal  with  the  hole

descriptors, however.  Just put each hole descriptor in the first octets

                                   6

of  the  hole  itself.    Note  that by the definition of the reassembly

algorithm, the minimum size of  a  hole  is  eight  octets.    To  store

hole.first  and  hole.last  will presumably require two octets each.  An

additional two octets will be required to thread together the entries on

the hole descriptor list.  This leaves at least two more octets to  deal

with implementation idiosyncrasies.

     There  is  only  one obvious pitfall to this storage strategy.  One

must execute the eight step algorithm above before copying the data from

the fragment into the reassembly buffer.  If one were to copy  the  data

first,  it might smash one or more hole descriptors.  Once the algorithm

above has been run, any hole descriptors which are about to  be  smashed

have already been rendered obsolete.