{"id":20200210,"url":"https://github.com/superb-man/memory-management","last_synced_at":"2025-03-03T08:42:38.090Z","repository":{"id":253434355,"uuid":"825593437","full_name":"Superb-Man/Memory-Management","owner":"Superb-Man","description":"Paging and swapping implementation using Linked List structure for OS-lab offline-6 in xv6.","archived":false,"fork":false,"pushed_at":"2024-11-02T14:30:39.000Z","size":97,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-13T20:14:27.627Z","etag":null,"topics":["page-replacement","paging","swapping"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Superb-Man.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-08T06:31:22.000Z","updated_at":"2025-01-13T13:20:59.000Z","dependencies_parsed_at":null,"dependency_job_id":"80c1fd66-6fee-4eca-b5ae-c8b8620b8e1a","html_url":"https://github.com/Superb-Man/Memory-Management","commit_stats":null,"previous_names":["superb-man/memory-management"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Superb-Man%2FMemory-Management","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Superb-Man%2FMemory-Management/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Superb-Man%2FMemory-Management/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Superb-Man%2FMemory-Management/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Superb-Man","download_url":"https://codeload.github.com/Superb-Man/Memory-Management/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241637149,"owners_count":19994925,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["page-replacement","paging","swapping"],"created_at":"2024-11-14T04:42:25.324Z","updated_at":"2025-03-03T08:42:38.083Z","avatar_url":"https://github.com/Superb-Man.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix\nVersion 6 (v6).  xv6 loosely follows the structure and style of v6,\nbut is implemented for a modern RISC-V multiprocessor using ANSI C.\n\n# Allocate Live List Node and SwapList Node\n```C\nstruct liveListNode *\nallocate_livelist_node(void)\n{\n  //printf(\"allocate live e asche!!\\n\") ;\n  struct run *r = livelist_node_mem.freelist ;\n\n  //need to acquire lock\n  acquire(\u0026livelist_node_mem.lock) ;\n  if(livelist_node_mem.freelist == 0) { //if freelist is empty\n    //need to allocate\n    release(\u0026livelist_node_mem.lock) ;\n    char *mem = kalloc() ; // 4KB allocating\n    char *m_end = mem + PGSIZE ; //\n    for(; mem + sizeof(struct liveListNode) \u003c= m_end ; mem+=sizeof(struct liveListNode)) { \n      r = (struct run*) mem ; \n      acquire(\u0026livelist_node_mem.lock) ;\n      r-\u003enext = livelist_node_mem.freelist ;\n      livelist_node_mem.freelist = r ;\n      release(\u0026livelist_node_mem.lock) ;\n    }\n    acquire(\u0026livelist_node_mem.lock) ;\n    r = livelist_node_mem.freelist ;\n  }\n  livelist_node_mem.freelist = r-\u003enext ;\n  release(\u0026livelist_node_mem.lock) ;\n  //printf(\"why can't allocate?\\n\") ;\n  return (struct liveListNode*) r ;\n}\n```\n- The Allocate swapListNode is almost like allocating live list node\n# Swapping Out\n```\nwe need to know the reference count of the physical page.\nif it's 0 then we need to free it from the physical memory\n- \n- Swap out the page of the given node\n- add the node to the swapped list\n- remove the node from the live list\n- free the physical page\n- update the reference count\n- update the swap count \n\n```\n```C\nvoid swap_out(struct liveListNode* n){\n  //printf(\"comming to swap-out\\n\") ;\n  if(n == 0){\n    panic(\"null pointer to in swap_out\");\n  }\n  struct swap *s = swapalloc(); \n  if(s == 0)\n    panic(\"swap not allocated\") ;\n  uint64 set = *n-\u003epte \u0026 PTE_SWAPPED ;\n  if(set){\n    panic(\"swapped bit on\");\n  }\n  //printf(\"========swapping out=========\\n\") ;\n  uint64 pa = PTE2PA(*n-\u003epte) ;\n  swapout(s,(char*)pa);\n  printf(\"\u003c==============Swapped Out=================\u003e\\n\") ;\n  int ref_cnt = 0;\n  int f = 0;\n  int rppn = PTE2PPN(*n-\u003epte) ;\n\n  struct liveListNode* l = live.list ;//pointing to the head of live list\n  if(l == 0)\n    panic(\"live list head empty\\n\");\n  // struct liveListNode* found;\n  while(l-\u003enext){ // traverse through the whole livenode list\n    //if physical page number matches then set the valid bit 0 and swapped bit to 1 ;\n    if(rppn == PTE2PPN(*l-\u003enext-\u003epte)){\n      f = 1;\n      *l-\u003enext-\u003epte \u0026= (~PTE_V);\n      *l-\u003enext-\u003epte |= PTE_SWAPPED;\n\n      // struct liveListNode* curr = l-\u003enext;\n      //now copying the l-\u003enext node to cur and\n      //adding it to swappedlist\n      //before we need to allocate a node using freelist\n      //creating a temporary node\n\n      struct swappedListNode* sn = 0 ;\n      // panic(\"here-panic\") ;\n      sn = allocate_swappedlist_node();\n      if(sn == 0)\n        panic(\"swapped list node is not alloacted\");\n      sn-\u003eprocess_id = l-\u003enext-\u003eprocess_id ;\n      sn-\u003epte = l-\u003enext-\u003epte ;\n      sn-\u003esp = s ; //As we are swapping out we need to have that swap* s saved in our linked list\n      sn-\u003evpn = l-\u003enext-\u003evpn ;\n      if(swapped.list == 0)\n        panic(\"swapped list head 0\");\n\n      //Now the next will be head-\u003enext(linked list)\n      //Now This is a FIFO linked list\n      //at each time we are inserting at the head of the list\n\n      sn-\u003enext = swapped.list-\u003enext;\n      swapped.list-\u003enext = sn;\n      swapped.swappedCount++ ;\n      //printf(\"Swapped out a pte and swapped size : %d\\n\\n\",swapped.swappedCount) ;\n      //================Freeing===================//\n      struct liveListNode* t = l-\u003enext-\u003enext;\n      //now the l-\u003enext node to be freed from the live list\n      //we need to free\n      struct run *rr ;\n      if(l-\u003enext == 0) panic(\"swap-free\") ;\n      struct liveListNode *ll = l-\u003enext ;\n      ll-\u003enext = 0 ;\n      ll-\u003eprocess_id = 0 ;\n      ll-\u003epte = 0 ;\n      ll-\u003evpn = 0 ;\n      rr = (struct run*) ll ;\n      //Now acquire locks\n      acquire(\u0026livelist_node_mem.lock) ;\n      rr-\u003enext = livelist_node_mem.freelist ;\n      livelist_node_mem.freelist = rr ;\n      release(\u0026livelist_node_mem.lock) ;\n\n\n      l-\u003enext = t ;\n      //for referencing same Physical page number\n      ++ref_cnt ;\n    }\n    else{\n      l = l-\u003enext;\n    }\n  }\n  if(!f){\n    panic(\"pte not asdffound\\n\"); //if the pte is not found in the live list\n  }\n  live.liveCount--; //the PTE has been moved to DISK!!\n  printf(\"\u003c===============Removed from livePages,number of live pages : %d =====\u003e\\n \",live.liveCount) ;\n\n  swapCount(s,4,ref_cnt) ;\n\n\n\n\n  acquire(\u0026refCount.lock);\n  refCount.count[rppn] = 0; //setting the reference count to 0\n  release(\u0026refCount.lock); \n  kfree_helper((void*) PPN2PA(rppn)); //freeing the physical page\n  //printf(\"=========Swap-Out done==========\\n\") ;\n}\n```\n# Swap in Function\n```\n- The function ensures that the number of pages currently resident in memory. \n- live.liveCount does not exceed a maximum threshold MAX_LIVE_PAGE.\n- If its already been swapped then remove from swapped list\n- Add that into livelist\n```\n```C\nvoid swap_in(int vpn, int process_id, uint64 *pte){\n  //printf(\"comming to swap-in\\n\") ;\n```\n- swapping out nodes to swapin a new one\n\n```C\n  //swapin requires the node to be added in live-pages\n  while(live.liveCount \u003e= MAX_LIVE_PAGE){\n    struct liveListNode* t = live.list;\n    if(!t) panic(\"live list head empty\");\n    else if(!t-\u003enext) panic(\"live list empty\");\n    t = t-\u003enext;\n    //traversing to the end of the livelist\n    while(t-\u003enext) { \n      t = t-\u003enext;\n    }\n    swap_out(t); // swapping out the last node because we need to swap in the new node\n  }\n```\n- This loop searches the swapped list to find the node corresponding \nto the process and virtual page number that need to be swapped in.\n```C\n  if(*pte \u0026 PTE_V){\n    panic(\"valid bit set\\n\");\n  }\n  struct swappedListNode* n;\n  n = swapped.list ; //pointing to the head\n  if(n == 0){\n    panic(\"swap list empty\\n\");\n  }\n  int f = 0;\n  struct swap *s;\n  char* mem ;\n  int ref_cnt = 0 ;\n\n  while(n-\u003enext){ //traversing through the whole swapped list to find the node to be swapped in\n    //the condition is needed for fork()\n    if(n-\u003enext-\u003eprocess_id == process_id \u0026\u0026 n-\u003enext-\u003evpn == vpn){\n      f = 1 ;\n      //for swappin we need to allocate memory\n      mem =(char *) kalloc();\n      if(n-\u003enext-\u003esp == 0){\n        panic(\"sp 0\\n\");\n      }\n      swapin(mem,n-\u003enext-\u003esp);\n      printf(\"\u003c==============Swapped In=================\u003e\\n\") ;\n      //printf(\"Swapped in\\n\") ;\n      s = n-\u003enext-\u003esp;\n      break;\n    }\n    else\n      n = n-\u003enext;\n  }\n  if(f == 0){\n    panic(\"swap in: swap not found\\n\");\n  }\n  ```\n- The loop searches for the node with a swap structure (sp) that matches the one just swapped in\n```C\n  n = swapped.list ; // again poiniting to head\n  while(n-\u003enext){ // traversing through the whole swapped list to remove the node from the swapped list\n    if(n-\u003enext-\u003esp == s){\n      if(s == 0)\n        panic(\"matched!!\");\n\n      pte_t *pte = n-\u003enext-\u003epte ;\n      *pte = (PTE_FLAGS(*pte)) | (PA2PTE((uint64)mem)) | (PTE_V) ;\n      *pte \u0026= (~PTE_SWAPPED) ;\n\n      int pid = n-\u003enext-\u003eprocess_id;\n      int vp = n-\u003enext-\u003evpn;\n\n      struct swappedListNode *t = n-\u003enext-\u003enext;\n      struct swappedListNode *tt = n-\u003enext ;\n      struct run *rr ;\n      //======Freeing===========//\n      //freeing from swappedlist\n      if(!tt)\n        panic(\"swapfree\");\n      tt-\u003enext = 0 ;\n      tt-\u003eprocess_id = 0 ;\n      tt-\u003evpn = 0 ;\n      tt-\u003esp = 0 ;\n      rr = (struct run*) tt ;\n      acquire(\u0026swappedlist_node_mem.lock) ;\n      rr-\u003enext = swappedlist_node_mem.freelist ;\n      swappedlist_node_mem.freelist = rr ;\n      release(\u0026swappedlist_node_mem.lock) ;\n      //===========Freeing done===========//\n      swapped.swappedCount-- ;\n      n-\u003enext = t ;\n      swapCount(s,2,0) ; //decrementing the reference count\n      if(!swapCount(s,3,0)) // if the reference count is 0 then free the swap structure\n        swapfree(s);\n      ++ref_cnt; \n      //needs to be added on live list!!\n      addLive(pte, pid, vp, 0);\n    }\n    else\n      n = n-\u003enext;\n  }\n  acquire(\u0026refCount.lock);\n  int ppn = PA2PPN(mem);\n  //how many processes are currently using the swapped-in page. \n  /*This is important for managing shared pages and ensuring \n    that pages are not prematurely freed if multiple processes reference them.\n    */\n  refCount.count[ppn] = ref_cnt; // \n  release(\u0026refCount.lock);\n}\n\n```\n\n# Adding Page Table Entry into Swapped List\n```\n- When a new process uses the page that was swapped out before by another process the swappedlist needs to refelct the change.The oldprocess_id is used to locate the swapped page in the list, and the newprocess_id is used to update the ownership or usage record.Typically happens when a child process inherits mem-mapping from parents.\n\n```\n\n```C\nvoid addSwapped(pte_t *pte, int oldprocess_id, int newprocess_id, int vpn){\n  //printf(\"comming to add-swapped\\n\") ;\n  //does it need to be removed from Live-list?\n  //No because addswapped is called in map-pages\n  //when there's swapped bit is set(must've been) swapped out before!!\n  //See swap_out function It sets the swappedbit\n  if(*pte \u0026 PTE_V){\n    panic(\"valid bit on\");\n  }\n  struct swappedListNode* new = allocate_swappedlist_node() ;\n  if(!new) panic(\"swapped node alloc!\") ;\n  new-\u003enext = 0 ;\n  new-\u003esp = 0 ;\n  new-\u003eprocess_id = newprocess_id ;\n  new-\u003epte = pte ;\n  new-\u003evpn = vpn ;\n\n  if(swapped.list == 0)\n    panic(\"swapped list head null\");\n\n  struct swappedListNode* curr = swapped.list ;//pointing to the head\n  int f = 0;\n  while(curr-\u003enext){\n    if(curr-\u003enext-\u003eprocess_id == oldprocess_id ){\n      int x = curr-\u003enext-\u003evpn ;\n      if(x == vpn){\n        //using the same swap structure\n        new-\u003esp = curr-\u003enext-\u003esp;\n        //the function is called on map-pages\n        //map-pages is called on uvm-copy\n        //uvm-copy is called on fork()\n        //So we need to update the reference cnt because we don't want to free it if some child process also use it\n        //That's why iterating through and checking parent_process_id(old) with the live_list_process_ids\n        swapCount(new-\u003esp , 1, 0) ;\n        f = 1;\n        break ;\n      }\n    }\n    curr = curr-\u003enext;\n  }\n  if(f) {\n    new-\u003enext = swapped.list-\u003enext;\n    swapped.list-\u003enext = new ;\n    swapped.swappedCount++ ;\n  }\n  else {\n    panic(\"swap not found\") ;\n  }\n}\n```\n# Add Live Page\n```C\nvoid addLive(pte_t *pte, int process_id, int vpn, int h){\n  //printf(\"comming to addlive\\n\") ;\n\n  if(*pte \u0026 PTE_SWAPPED){\n    panic(\"swapped bit on\");\n  }\n\n  //printf(\"hoise\\n\") ;\n  struct liveListNode* nd = allocate_livelist_node();\n  //printf(\"allocate hoise addlive e \\n\") ;\n  if(nd == 0){\n    panic(\"liveListNode alloc\");\n  }\n  nd-\u003eprocess_id = process_id;\n  nd-\u003epte = pte;\n  nd-\u003evpn = vpn;\n  nd-\u003enext = 0;\n  if(live.list == 0){\n    panic(\"list head empty\");\n  }\n  nd-\u003enext = live.list-\u003enext;\n  live.list-\u003enext = nd;\n  int ppn = PTE2PPN(*pte);\n  if(ppn \u003c 0 || ppn \u003e= PAGE_COUNT){\n    panic(\"invalid ppn\");\n  }\n  live.liveCount++ ;\n  printf(\"\u003c=================Added livePages,number of live pages : %d ========\u003e\\n \",live.liveCount) ;\n  //live_count++ ;\n  // printf(\"Now live pages : %d\\n\", live_count) ;\n\n  //printf(\"Ekhan porzonto aste parche??\\n\") ;\n  if(live.liveCount \u003e= MAX_LIVE_PAGE){\n    //==============swapping out the last node=====================//\n    //Swap out will only be called when there's unique live pages \u003e= MAX-LIVE PAGES\n    struct liveListNode* t = live.list;\n    if(!t) panic(\"live list head empty\");\n    else if(!t-\u003enext) panic(\"live list empty\");\n    t = t-\u003enext;\n    //traversing to the end of the livelist\n    while(t-\u003enext) {\n      t = t-\u003enext;\n    }\n    printf(\"\u003c================More than live_list size, Need to be swapped out ==================\u003e\\n\") ;\n    swap_out(t);\n  }\n  //printf(\"reaching end of addlive\\n\") ;\n\n}\n```\n```\nAs we are adding to livelist and swapped list that should be freed too\n- RemoveFromSwapped\n- RemoveFromLive\nimplemented on code\n```\n\n# Kfree-Updated\n```C\n\n// Free the page of physical memory pointed at by pa,\n// which normally should have been returned by a\n// call to kalloc().  (The exception is when\n// initializing the allocator; see kinit above.)\nint cnt = 0 ;\nvoid kfree(void *pa){\n  //printf(\"called\\n\") ;\n  uint64 ppn = PA2PPN((uint64)pa) ;\n  acquire(\u0026refCount.lock);\n  int c = refCount.count[ppn]; \n  release(\u0026refCount.lock);\n  if(c \u003c= 0){\n    panic(\"kfree__\");\n  }\n  acquire(\u0026refCount.lock);\n  refCount.count[ppn]--; \n  int count = refCount.count[ppn];\n  release(\u0026refCount.lock);\n  if(count == 0){ // No process is using this page\n    //uint64 pa2 = PPN2PA(ppn);\n    kfree_helper((void*)((uint64)PPN2PA(ppn)));\n  }\n}\n\n\n/**\n * We need to count the references of PPN for any kalloc and kfree\n*/\n\n\nvoid\nkfree_helper(void *pa)\n{\n  cnt++ ;\n  struct run *r;\n  //checks if the address is page-aligned and \n  //within valid physical memory bounds.\n\n  if(((uint64)pa % PGSIZE) != 0 || (char*)pa \u003c end || (uint64)pa \u003e= PHYSTOP)\n    panic(\"kfree\");\n\n  // Fill with junk to catch dangling refs.\n  memset(pa, 1, PGSIZE);\n\n  r = (struct run*)pa;\n\n  acquire(\u0026kmem.lock);\n  r-\u003enext = kmem.freelist;\n  kmem.freelist = r;\n  release(\u0026kmem.lock);\n  //printf(\"%d k free called\\n\", cnt) ;\n}\n```\n# Free Page Count\n```C\nint freePageCount(){\n  int c = 0;\n  struct run *cur ;\n  acquire(\u0026kmem.lock);\n  cur = kmem.freelist;\n  while(cur){\n    ++c;\n    cur = cur-\u003enext;\n  }\n  release(\u0026kmem.lock);\n\n  printf(\"free page count in freelist is %d\\n\",c) ;\n\n  //return c ;\n\n  c = 0 ;\n  for(int i = 0; i \u003c PAGE_COUNT; ++i){\n    acquire(\u0026refCount.lock);\n    if(refCount.count[i] == 0) ++c;\n    release(\u0026refCount.lock);\n  }\n  printf(\"free page count in ref-count is %d\\n\",c) ;\n\n  return 1 ;\n}\n```\n\n- There hadbeen some key changes in vm.c functions as we now have to deal with many bits \n.\n\n# Traphandler in vm.c\n- Called in userTrap() in trap.c\n\n```C\n    if(r_scause() == 0x0f || r_scause() == 0x0c || r_scause() == 0x0d){\n      handled =  trapHandler(p-\u003epagetable, p-\u003epid);\n      if(!handled){\n        setkilled(p);\n      }\n\n    }\n```\n\n```C\nint trapHandler(pagetable_t p, int process_id){\n  //printf(\"trapHandler-called\\n\") ;\n  uint64 va = r_stval();\n  if(va \u003e= MAXVA) return 0;\n  pte_t *pte = walk(p,va,0,0);\n  if(pte == 0){\n    printf(\"pte 0\");\n    return 0;\n  }\n  acquireSlock();\n  int vpn = VA2VPN(va);\n  if(*pte \u0026 PTE_V){\n    releaseSlock();\n    return 1;\n  }\n  swap_in(vpn, process_id, pte);\n  releaseSlock();\n  return 1;\n}\n```\n# Count of live Pages\n\n```C\nvoid Count(uint64 sz,int pid,pagetable_t pagetable) {\n  pte_t *pte;\n  uint64 i;\n  int livecount = 0, swapcount = 0;\n  for (i = 0; i \u003c sz; i += PGSIZE)\n  {\n    if ((pte = walk(pagetable, i, 0,1)) == 0)\n      panic(\"uvmcopy: pte should exist\");\n    if ((*pte \u0026 PTE_V) == 0 \u0026\u0026 (*pte \u0026 PTE_SWAPPED) == 0)\n      panic(\"uvmcopy: page not present\");\n\n    if (*pte \u0026 PTE_SWAPPED)\n    {\n      swapcount++;\n    }\n    else if (*pte \u0026 PTE_V)\n    {\n      livecount++;\n    }\n  }\n\n  printf(\"process_id: %d livePage :%d swappedPage: %d\\n\",pid,livecount,swapcount) ;\n}\n```\n\n```\n- There's an issue of some extra-pages\n- It is due to initialization of \n    main()-\u003e\n        kvminit()-\u003e\n            kvmmake()-\u003e\n                kvmmap() \n                ...\n                ...\n\n- The freerange() in kalloc.c should be called on kinit() to free 1\u003c\u003c15 (+-) pages \n- So when any program starts running it should show either 0 or some pages of kernel pagetable\n```\n# Free Range\n```C\nvoid\nfreerange(void *pa_start, void *pa_end)\n{\n\n  char *p;\n  p = (char*)PGROUNDUP((uint64)pa_start);\n  for(; p + PGSIZE \u003c= (char*)pa_end; p += PGSIZE){\n    refCount.count[PA2PPN((uint64)p)] = 0;\n    kfree_helper((void*)(p)) ;\n  }\n}\n```\n\n\n\n\nACKNOWLEDGMENTS\n\nxv6 is inspired by John Lions's Commentary on UNIX 6th Edition (Peer\nto Peer Communications; ISBN: 1-57398-013-7; 1st edition (June 14,\n2000)).  See also https://pdos.csail.mit.edu/6.1810/, which provides\npointers to on-line resources for v6.\n\nThe following people have made contributions: Russ Cox (context switching,\nlocking), Cliff Frey (MP), Xiao Yu (MP), Nickolai Zeldovich, and Austin\nClements.\n\nWe are also grateful for the bug reports and patches contributed by\nTakahiro Aoyagi, Silas Boyd-Wickizer, Anton Burtsev, carlclone, Ian\nChen, Dan Cross, Cody Cutler, Mike CAT, Tej Chajed, Asami Doi,\neyalz800, Nelson Elhage, Saar Ettinger, Alice Ferrazzi, Nathaniel\nFilardo, flespark, Peter Froehlich, Yakir Goaron, Shivam Handa, Matt\nHarvey, Bryan Henry, jaichenhengjie, Jim Huang, Matúš Jókay, John\nJolly, Alexander Kapshuk, Anders Kaseorg, kehao95, Wolfgang Keller,\nJungwoo Kim, Jonathan Kimmitt, Eddie Kohler, Vadim Kolontsov, Austin\nLiew, l0stman, Pavan Maddamsetti, Imbar Marinescu, Yandong Mao, Matan\nShabtay, Hitoshi Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel\nNider, Hayato Ohhashi, OptimisticSide, Harry Porter, Greg Price, Jude\nRich, segfault, Ayan Shafqat, Eldar Sehayek, Yongming Shen, Fumiya\nShigemitsu, Cam Tenny, tyfkda, Warren Toomey, Stephen Tu, Rafael Ubal,\nAmane Uehara, Pablo Ventura, Xi Wang, WaheedHafez, Keiichi Watanabe,\nNicolas Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng,\nZhUyU1997, and Zou Chang Wei.\n\n\nThe code in the files that constitute xv6 is\nCopyright 2006-2022 Frans Kaashoek, Robert Morris, and Russ Cox.\n\nERROR REPORTS\n\nPlease send errors and suggestions to Frans Kaashoek and Robert Morris\n(kaashoek,rtm@mit.edu).  The main purpose of xv6 is as a teaching\noperating system for MIT's 6.1810, so we are more interested in\nsimplifications and clarifications than new features.\n\nBUILDING AND RUNNING XV6\n\nYou will need a RISC-V \"newlib\" tool chain from\nhttps://github.com/riscv/riscv-gnu-toolchain, and qemu compiled for\nriscv64-softmmu.  Once they are installed, and in your shell\nsearch path, you can run \"make qemu\".\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuperb-man%2Fmemory-management","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsuperb-man%2Fmemory-management","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuperb-man%2Fmemory-management/lists"}