OS - Cache synchronization

模拟一个本地的 Netflix 的 cache服务器,为了更快的发送,并且利用最小与主服务器的带宽。这个是伯克利OS的section5 作业的最后一题,我觉得是前5个section中比较有趣的一道题吧。

Let’s say you’re building a local cache for Netflix videos at your favorite ISP. Here are some requirements for your cache:

  • Your goal is to deliver videos to customers as fast as possible, but use as little of your ISP’s ingress bandwidth as possible. So, you decide to build a fully associative, multi-threaded cache.
  • You need to implement these two functions:

              – “char *getVideo(int videoID)” – Returns a pointer to the video data (represented as a byte array).

              void doneWithVideo(int videoID)” – The video needs to remain in the cache while it’s being streamed to the user, so until we call this second function, doneWithVideo, you cannot evict the video from the cache.

  • Your cache should have 1024 entries.
  • You can use these functions:   

         – “char *downloadVideoFromInternet(int videoID) – Download a video from Netflix’s origin servers. This function takes a while to complete! 

         – “void free(char *video)” – Free the memory used to hold a video.

  • You must never return an incomplete half-downloaded video to “getVideo()”. Wait until the download completes.
  • You must be able to download multiple videos from Netflix at the same time.
  • Your cache should never hold 2 copies of the same video.
  • Once a video is in the cache, it must be able to be streamed to multiple users at the same time.

First, design the struct that you will use for each cache entry. You can add locks or other metadata. You can also add more global variables, if you need them.

Skeleton 代码:

struct cacheEntry {
int videoID;
char *video;
// NEW FIELDS

};

struct cacheEntry CACHE[1024];


char *getVideo(int videoID) {
    //fill in the code
    for (int i = 0; i < 1024; i++) {
        if (CACHE[i].videoID == videoID) {           
            //fill in the code
            return CACHE[i].video;
        }
        //fill in the code
    }
    //fill in the code 
    for (int i = 0; i < 1024; i++) {
    // Why is this in a for loop? I don’t know.
    if (CACHE[i].video != NULL) {
        free(CACHE[i].video);
    }
    CACHE[i].videoID = videoID;
    //fill in the code

    CACHE[i].video = downloadVideoFromInternet(videoID);
    return CACHE[i].video;
    }
}

Finally, implement the doneWithVideo function:

char *doneWithVideo(int videoID) {

   //fill in the code
}

就是填写一个视频服务器的代码了。

让这个服务器线程安全,且cache - friendly 线程安全不难,但是cache-friendly,提高性能要求高一点。从第一次获取锁,到最后一次放弃锁是我们要保护的内容。当然,每一次 return 或者 break 前都需要 release lock,否则会出错!!

- cache最多有1024个入口(我觉得这个应该是说cache的大小最多能有1024个video那么大,虽然一般的电脑不才几兆么,要买这么贵的服务器,果然视频服务商很费钱)

- 在getvideo( )完成之前,不能发送过去,必须return一个完整的video。

- cache里面不能有两个一摸一样的video (也就是说,当你收到一个request,第一件事是去查找我有没有这个video,而不是马上去原始的服务器下载,如果每一个都去原始的服务器下载,就没有任何的优势了)

- 可以同时从Netflix上下载多个不同的video (多线程) 

- 一旦一个video在缓存上,那么这个video就可以被同时传送给多个用户 (多线程)

cache的内容可能会被不断的修改,比如当前请求视频的人传送完成了,那么这个cache[i]的video的内容可能随着新的请求用户的增加而被free,然后载入新用户所请求的video。所以是一个完全动态的过程。

struct cacheEntry {
    int videoID;
    char *video;
    // NEW FIELDS
    bool downloading;
    int users;   //用user来判断哪个video该被删去
};
Lock cacheLock;
struct cacheEntry CACHE[1024];
char *getVideo(int videoID) {
    while (true) {
        bool wait_for_download = 0;
        do {
            lock_acquire(&cacheLock);               //获取锁
            for (int i = 0; i < 1024; i++) {
                if (CACHE[i].videoID == videoID) {  //cache中有这个videoID
                    if (!CACHE[i].downloading) {    //如果这个电影不在下载中,直接发过去就好
                         CACHE[i].users += 1; 
                         lock_release(&cacheLock);
                         wait_for_download = 0;     
                         return CACHE[i].video;
                    } else {
                         wait_for_download = 1;    //cache中有这个videoID,但是正在下载中
                         lock_release(&cacheLock);
                         break;
                    }
              }
          }
      } while (wait_for_download);               //保证do里面的东西只做一次
     for (int i = 0; i < 1024; i++) {
          if (CACHE[i].users > 0) {              //如果这个video正在被转发中,则检查下一个
               continue;
           }
          // Why is this in a for loop? I don’t know.
          if (CACHE[i].video != NULL) {          //如果video有内容,就free
              free(CACHE[i].video);
          }
          CACHE[i].videoID = videoID; 
          CACHE[i].users = 1;
          CACHE[i].downloading = true;         
          lock_release(&cacheLock);              
          CACHE[i].video = downloadVideoFromInternet(videoID);
          CACHE[i].downloading = false;         //下载结束
          return CACHE[i].video;
    }
      //也有可能现在1024个cache[i]都是有user的,所以就要等,那么简单的release就好了。
    lock_release(&cacheLock);
   }  //为什么总整个getvideo是一个大的while true呢? 因为video可能不断的下载,不断的删除,所以是在变化的。让getvideo一直循环的话没有问题。也有可能高峰期间1024个cache[i]都是有user的,就么更要while循环了!等待传输完成。因为你的缓存就只有1024个这么多,这个本地服务器最多同时只有1024个不同的视频。 
//如果每一个用户请求的都是不同的视频,最多同时只能服务1024个用户,那肯定是相当慢的,但我觉得视频有热度的嘛,少数视频才有高的人气,可能同一时间很多人都在请求同一个视频,那么只要线程安全还是相当快的。     
 }

线程安全不仅仅保证了数据的正确性,也保证了传输速度,如果没有上锁,可能会出现错误的users数字,比如这个video明明在本地有缓存,但是因为某些线程调度的原因,导致了users 小于 0,那么就需要去原始服务器上下载,就会拖累服务客户的速度。

Finally, implement the doneWithVideo function:

char *doneWithVideo(int videoID) {
    lock_acquire(&cacheLock);   //保证线程安全即可
    for (int i = 0; i < 1024; i++) {
        if (CACHE[i].videoID == videoID) {
            CACHE[i].users -= 1;
        }
    }
    lock_release(&cacheLock);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值