我正在 Docker 容器內的 Linux(Ubuntu 發行版)之上撰寫來賓作業系統。檔案系統被實作為位于主機作業系統中的單個檔案,因此每當客戶作業系統檔案系統中的檔案發生更改時,必須打開主機作業系統上的檔案,必須覆寫正確的塊,并且檔案必須被關閉。
我和我的伙伴開發了以下遞回輔助函式來接收塊號和偏移量,以抽象出塊級別的所有細節以用于更高級別的功能:
/**
* Recursive procedure to write n bytes from buf to the
* block specified by block_num. Also updates FAT to
* reflect changes.
*
* @param block_num identifier for block to begin writing
* @param buf buffer to write from
* @param n number of bytes to write
* @param offset number of bytes to start writing from as
* measured from start of file
*
* @returns number of bytes written
*/
int write_bytes(int block_num, const char *buf, int n, int offset) {
BlockTuple red_tup = reduce_block_offset(block_num, offset);
block_num = red_tup.block;
offset = red_tup.offset;
FILE *fp = fopen(fat->fname, "r ");
int bytes_to_write = min(n, fat->block_size - offset);
int write_n = max(bytes_to_write, 0);
fseek(fp, get_block_start(block_num) offset, SEEK_SET);
fwrite(buf, 1, write_n, fp); // This line is returning 48 bytes written
fclose(fp);
// Check if there are bits remaining
int bytes_left = n - write_n;
if (bytes_left > 0) {
// Recursively write on next block
int next_block = get_free_block();
set_fat_entry(block_num, next_block); // point block to next block
set_fat_entry(next_block, 0xFFFF);
return write_bytes(next_block, buf write_n, bytes_left, max(0, offset - fat->block_size)) write_n;
} else {
set_fat_entry(block_num, 0xFFFF); // mark file as terminated
return write_n;
}
}
問題是 fwrite(3)報告寫入了 48 個位元組(當 n 傳遞為 48 時)但是在主機作業系統上對檔案進行 hexdump 顯示沒有位元組被更改:
00000000 00 01 ff ff ff ff 00 00 00 00 00 00 00 00 00 00 |................|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00008000
這特別古怪,因為當我的合作伙伴在完全相同的提交(沒有未提交的更改)上運行代碼時,她的寫入通過并且主機作業系統上的檔案 hexdumps 到:
00000000 00 01 ff ff ff ff 00 00 00 00 00 00 00 00 00 00 |................|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000100 66 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |f1..............|
00000110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000120 00 01 00 00 02 00 01 06 e7 36 75 63 00 00 00 00 |.........6uc....|
00000130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000200 e0 53 f8 88 c0 0d 37 ca 84 1f 19 b0 6c a8 68 7b |.S....7.....l.h{|
00000210 57 83 cf 13 f0 42 21 d3 21 e1 da de d4 8a f1 e6 |W....B!.!.......|
00000220 f0 12 98 fb 1c 30 4c 04 b3 16 1d 96 17 ba d7 5a |.....0L........Z|
00000230 7e f3 8a f5 6a 42 6b ef 58 f6 bc 01 db 0c 02 53 |~...jBk.X......S|
00000240 e5 10 7e f3 4a d5 3f ac 8e 38 82 c3 95 f8 11 8e |..~.J.?..8......|
00000250 a6 82 eb 3b 24 56 9a 75 44 36 8b 25 60 83 4c 04 |...;$V.uD6.%`.L.|
00000260 07 9e 14 99 9c 9f 87 3c 8a d4 c3 e8 17 60 81 0e |.......<.....`..|
00000270 bc eb 1d 35 68 fc d5 be 4f 1c 9d 5e 72 57 65 01 |...5h...O..^rWe.|
00000280 b7 43 54 26 d6 6d ba 51 bf 12 8c a1 03 d5 66 b3 |.CT&.m.Q......f.|
00000290 90 0d 60 b8 95 8d 15 bd 53 9a 70 77 4f 7a 04 1e |..`.....S.pwOz..|
000002a0 9e b2 4c 9a 79 dd de 48 cd fe 1e dc 57 7d d1 7f |..L.y..H....W}..|
000002b0 3f f5 77 96 fa e7 d7 33 33 48 ce 0a 4d 61 ab 96 |?.w....33H..Ma..|
000002c0 5f c4 88 bf c6 3a 09 37 76 c4 b8 db bc 6a 7d c0 |_....:.7v....j}.|
000002d0 c4 89 68 e7 b4 70 f8 a6 a8 00 9d c4 63 da fb 66 |..h..p......c..f|
000002e0 be d2 cd 68 1c d2 ff bf 00 e9 37 ab 6b 1a 3c f2 |...h......7.k.<.|
000002f0 7b c1 a2 c4 46 ae db 93 b4 4f 64 79 14 2a 1a d4 |{...F....Ody.*..|
00000300 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00008000
我指的是未寫入的 48 個位元組是寫入從地址 00000100-0000012E 運行的目錄塊的位元組(下面的位元組代表正在寫入的實際檔案,在到達之前我的代碼段錯誤寫)。值得注意的是我的容器仍然可以格式化檔案系統檔案,所以所有的寫入都沒有被破壞。此代碼段僅代表第一個無效的寫入。
我們都在同一個 Docker 容器中運行代碼。我能想到的唯一區別是我的電腦是 Windows 而她的是 Mac。可能是什么問題?
我首先相信的是與阻止我寫入的主機作業系統存在一些沖突,但是分配和列印回傳的回傳值fwrite(3)確實在兩臺機器上都寫入了 48 個位元組。
我還期望我的緩沖區只是全 0(它最初是使用 calloc(3) 分配的),但是列印出緩沖區的前 48 個位元組證明了這個理論是錯誤的。
我最終認為這是 <stdio.h> 中高層介面的一些問題,而不是 <unistd.h> 中的低層介面。我fopen(3), fwrite(3), flseek(3), fclose(3)用它們的較低級別的等效項(write(2)等)替換了每個,它仍然顯示了 48 個位元組,沒有對檔案進行實際更改。
編輯:客戶作業系統檔案系統可以根據用戶引數進行格式化。上面的所有測驗都是在塊大小為 256 位元組和總共 128 個塊的情況下執行的。我再次嘗試完全相同的寫入序列,塊大小為 1024 位元組,總共 16384 個塊,沒有錯誤。目前還不清楚為什么代碼在我伙伴的機器上適用于兩種格式配置而不適用于我的機器,但這可能會縮小范圍。
運行strace顯示圍繞寫入的以下摘錄:
openat(AT_FDCWD, "minfs", O_RDWR) = 4
newfstatat(4, "", {st_mode=S_IFREG|0777, st_size=32768, ...}, AT_EMPTY_PATH) = 0
lseek(4, 0, SEEK_SET) = 0
read(4, "\0\1\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 256) = 256
write(4, "f1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 48) = 48
close(4)
再次顯示位元組已寫入,但hd程式完成后顯示與上述相同的輸出。我的想法是,也許在摘錄中寫入的位元組稍后會被覆寫,但在上面的摘錄之后唯一的寫入strace是:
lseek(4, 0, SEEK_SET) = 0
read(4, "\0\1\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 320) = 320
write(4, "\0", 1) = 1
close(4)
它應該位于地址 320,緊接在上面地址 256 的寫入之后。
uj5u.com熱心網友回復:
事實證明,不匹配是由于有關何時同步更改的未定義行為造成的mmap(2)。有一段代碼mmap(2)更改了映射的記憶體區域,然后立即對包含映射記憶體區域的主機作業系統上的檔案進行讀/寫。Mac 似乎會在下一節之前寫入更改,而 Windows 直到事后才會同步,從而導致未定義的行為。
通過使用強制直寫行為的標志msync(2)修改映射區域后立即呼叫 來解決該問題。mmap(2)MS_SYNC
鏈接到此處的檔案:mmap(2),msync(2)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/537567.html
