Xv6 with picoc & Linkage editor
v1.0
The project delineate mutual cohesion between c library, linkage editor ( linker), interpreter and operating system by porting the same on xv6 kernel
|
00001 #include "types.h" 00002 #include "defs.h" 00003 #include "param.h" 00004 #include "spinlock.h" 00005 #include "fs.h" 00006 #include "buf.h" 00007 00008 // Simple logging. Each system call that might write the file system 00009 // should be surrounded with begin_trans() and commit_trans() calls. 00010 // 00011 // The log holds at most one transaction at a time. Commit forces 00012 // the log (with commit record) to disk, then installs the affected 00013 // blocks to disk, then erases the log. begin_trans() ensures that 00014 // only one system call can be in a transaction; others must wait. 00015 // 00016 // Allowing only one transaction at a time means that the file 00017 // system code doesn't have to worry about the possibility of 00018 // one transaction reading a block that another one has modified, 00019 // for example an i-node block. 00020 // 00021 // Read-only system calls don't need to use transactions, though 00022 // this means that they may observe uncommitted data. I-node and 00023 // buffer locks prevent read-only calls from seeing inconsistent data. 00024 // 00025 // The log is a physical re-do log containing disk blocks. 00026 // The on-disk log format: 00027 // header block, containing sector #s for block A, B, C, ... 00028 // block A 00029 // block B 00030 // block C 00031 // ... 00032 // Log appends are synchronous. 00033 00034 // Contents of the header block, used for both the on-disk header block 00035 // and to keep track in memory of logged sector #s before commit. 00036 struct logheader { 00037 int n; 00038 int sector[LOGSIZE]; 00039 }; 00040 00041 struct log { 00042 struct spinlock lock; 00043 int start; 00044 int size; 00045 int busy; // a transaction is active 00046 int dev; 00047 struct logheader lh; 00048 }; 00049 struct log log; 00050 00051 static void recover_from_log(void); 00052 00053 void 00054 initlog(void) 00055 { 00056 if (sizeof(struct logheader) >= BSIZE) 00057 panic("initlog: too big logheader"); 00058 00059 struct superblock sb; 00060 initlock(&log.lock, "log"); 00061 readsb(ROOTDEV, &sb); 00062 log.start = sb.size - sb.nlog; 00063 log.size = sb.nlog; 00064 log.dev = ROOTDEV; 00065 recover_from_log(); 00066 } 00067 00068 // Copy committed blocks from log to their home location 00069 static void 00070 install_trans(void) 00071 { 00072 int tail; 00073 00074 for (tail = 0; tail < log.lh.n; tail++) { 00075 struct buf *lbuf = bread(log.dev, log.start+tail+1); // read log block 00076 struct buf *dbuf = bread(log.dev, log.lh.sector[tail]); // read dst 00077 memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst 00078 bwrite(dbuf); // write dst to disk 00079 brelse(lbuf); 00080 brelse(dbuf); 00081 } 00082 } 00083 00084 // Read the log header from disk into the in-memory log header 00085 static void 00086 read_head(void) 00087 { 00088 struct buf *buf = bread(log.dev, log.start); 00089 struct logheader *lh = (struct logheader *) (buf->data); 00090 int i; 00091 log.lh.n = lh->n; 00092 for (i = 0; i < log.lh.n; i++) { 00093 log.lh.sector[i] = lh->sector[i]; 00094 } 00095 brelse(buf); 00096 } 00097 00098 // Write in-memory log header to disk. 00099 // This is the true point at which the 00100 // current transaction commits. 00101 static void 00102 write_head(void) 00103 { 00104 struct buf *buf = bread(log.dev, log.start); 00105 struct logheader *hb = (struct logheader *) (buf->data); 00106 int i; 00107 hb->n = log.lh.n; 00108 for (i = 0; i < log.lh.n; i++) { 00109 hb->sector[i] = log.lh.sector[i]; 00110 } 00111 bwrite(buf); 00112 brelse(buf); 00113 } 00114 00115 static void 00116 recover_from_log(void) 00117 { 00118 read_head(); 00119 install_trans(); // if committed, copy from log to disk 00120 log.lh.n = 0; 00121 write_head(); // clear the log 00122 } 00123 00124 void 00125 begin_trans(void) 00126 { 00127 acquire(&log.lock); 00128 while (log.busy) { 00129 sleep(&log, &log.lock); 00130 } 00131 log.busy = 1; 00132 release(&log.lock); 00133 } 00134 00135 void 00136 commit_trans(void) 00137 { 00138 if (log.lh.n > 0) { 00139 write_head(); // Write header to disk -- the real commit 00140 install_trans(); // Now install writes to home locations 00141 log.lh.n = 0; 00142 write_head(); // Erase the transaction from the log 00143 } 00144 00145 acquire(&log.lock); 00146 log.busy = 0; 00147 wakeup(&log); 00148 release(&log.lock); 00149 } 00150 00151 // Caller has modified b->data and is done with the buffer. 00152 // Append the block to the log and record the block number, 00153 // but don't write the log header (which would commit the write). 00154 // log_write() replaces bwrite(); a typical use is: 00155 // bp = bread(...) 00156 // modify bp->data[] 00157 // log_write(bp) 00158 // brelse(bp) 00159 void 00160 log_write(struct buf *b) 00161 { 00162 int i; 00163 00164 if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1) 00165 panic("too big a transaction"); 00166 if (!log.busy) 00167 panic("write outside of trans"); 00168 00169 for (i = 0; i < log.lh.n; i++) { 00170 if (log.lh.sector[i] == b->sector) // log absorbtion? 00171 break; 00172 } 00173 log.lh.sector[i] = b->sector; 00174 struct buf *lbuf = bread(b->dev, log.start+i+1); 00175 memmove(lbuf->data, b->data, BSIZE); 00176 bwrite(lbuf); 00177 brelse(lbuf); 00178 if (i == log.lh.n) 00179 log.lh.n++; 00180 b->flags |= B_DIRTY; // XXX prevent eviction 00181 } 00182 00183 //PAGEBREAK! 00184 // Blank page. 00185