Apa??? Bikin kernel lagi???? :(
Looking for the English version?
Pada final praktikum, kita akan melanjutkan task-4 dari praktikum modul 4 yang sebelumnya. Kali ini, kita akan membuat sebuah filesystem sederhana yang dapat digunakan untuk menyimpan file-file yang kita buat. Filesystem yang akan kita buat ini akan menggunakan metode penyimpanan data yang sederhana, yaitu dengan menyimpan data file ke dalam blok-blok yang telah disediakan oleh filesystem. Jika kalian sudah tidak sabar ingin langsung mengerjakan task-task yang ada, bisa search TODO pada workspace ini. Berikut adalah gambaran yang akan kalian kerjakan pada final praktikum kali ini.
- Membuat filesystem yang dapat digunakan untuk menyimpan file-file yang kita buat.
- Melengkapi kernel untuk dapat membaca dan menulis file ke dalam filesystem yang telah kita buat.
- Membuat shell sederhana yang dapat digunakan untuk mengakses filesystem yang telah kita buat.
Penjelasan pada praktikum final akan sering menggunakan angka heksadesimal. Penggunaan angka heksadesimal ditandai dengan prefix 0x. Jika kalian belum terbiasa dengan angka heksadesimal, kalian dapat menggunakan kalkulator yang mendukung mode heksadesimal atau menggunakan konversi angka heksadesimal ke desimal.
Jika kalian sudah melewati modul 4, pasti sudah tidak asing lagi dengan struktur disk yang akan kita gunakan. Disk yang kita gunakan terdiri dari beberapa blok. Selanjutnya blok akan disebut sektor. Setiap sektor memiliki ukuran 512 bytes. Sektor pertama akan digunakan sebagai boot sector, yang berisi hasil kompilasi dari bootloader.asm. Sektor kedua hingga sektor ke-15 akan digunakan untuk menyimpan kode teks dari kernel yang kita buat.
Dengan melihat hasil dari modul 4, berikut adalah struktur disk yang akan kita gunakan. Dapat dilihat menggunakan aplikasi seperti HxD atau menggunakan perintah hexdump atau xxd.
Untuk memudahkan dalam ilustrasi selanjutnya, struktur disk akan digambarkan sebagai berikut.
Satu sektor akan digambarkan sebagai satu blok. Alamat sektor akan dinomori ulang dari 0x00. Sehingga sektor pertama akan memiliki alamat 0x00, sektor kedua akan memiliki alamat 0x01, dan seterusnya. Satu baris akan berisi 16 sektor. Sehingga baris pertama akan berisi sektor dengan alamat 0x00 hingga 0x0F, baris kedua akan berisi sektor dengan alamat 0x10 hingga 0x1F, dan seterusnya.
Untuk mencari alamat sektor pada isi file floppy.img, kita dapat mengonversi alamat sektor ke dalam alamat byte dengan cara seperti pada gambar di atas.
Filesystem yang akan dibuat akan menggunakan beberapa komponen, yaitu map, node, dan data. Map akan disimpan sebanyak 1 sektor pada sektor 0x100. Node akan disimpan sebanyak 2 sektor pada sektor 0x101 dan 0x102. Data akan disimpan sebanyak 1 sektor pada sektor 0x103.
Berikut adalah ilustrasi dari struktur filesystem yang akan kita buat.
Map akan digunakan untuk menandai blok-blok pada disk yang telah digunakan oleh file. Setiap blok akan memiliki status 0x00 jika sektor yang bersangkutan belum digunakan, dan 0x01 jika sektor yang bersangkutan telah digunakan. Contohnya, karena pada sektor 0x00 telah digunakan oleh bootloader, maka isi dari map ke-0 adalah 0x01. Komponen map akan digunakan ketika kita ingin menulis file ke dalam disk untuk mengetahui sektor mana saja yang dapat kita gunakan.
Berikut adalah ilustrasi dari komponen map.
Map akan berukuran 1 sektor (512 bytes). Item ke-0 hingga item ke-15 pada map akan memiliki status 0x01 karena telah digunakan oleh sistem operasi. Item ke-16 hingga item ke-255 akan memiliki status 0x00 karena belum digunakan. Mulai dari item ke-256 (0x100) hingga item ke 511 (0x1FF) akan ditandai sebagai sektor yang telah digunakan. Hal ini dikarenakan kita tidak memperbolehkan file untuk menulis data pada sektor yang berada di atas sektor 0x100.
Node akan digunakan untuk menyimpan informasi dari file atau direktori yang kita buat. Setiap node akan memiliki ukuran 16 bytes. Sehingga, total akan terdapat 64 item node yang bisa disimpan. Berikut adalah ilustrasi dari komponen node.
Berikut adalah penjelasan dari setiap item pada node.
-
P: kolom pertama pada item node berguna sebagai penunjuk parent node dari node yang bersangkutan dan akan bernilai
0xFFjika parent dari node yang bersangkutan adalah root node.Sebagai contoh, pada node index ke-1, nilai dari kolom pertama adalah
0x00. Hal ini menunjukkan bahwa node index ke-1 adalah parent node dari node index ke-0. Sedangkan pada node index ke-0, nilai dari kolom pertama adalah0xFF. Hal ini menunjukkan bahwa parent node dari node index ke-0 adalah root node. -
D: kolom kedua pada item node berguna sebagai penunjuk index dari komponen data yang akan digunakan untuk menyimpan data file. Jika nilai dari kolom kedua adalah
0xFF, maka node tersebut merupakan direktori.Sebagai contoh, pada node index ke-0, nilai dari kolom kedua adalah
0x00. Hal tersebut berarti informasi data dari file tersebut bisa diakses pada komponen data index ke-0. Sedangkan pada node index ke-1, nilai dari kolom kedua adalah0xFF. Hal tersebut berarti node tersebut merupakan direktori. -
Node name: kolom ketiga hingga kolom terakhir pada item node berguna sebagai nama dari node tersebut. Nama dari node akan memiliki panjang maksimal 13 karakter (karakter terakhir adalah karakter null).
Komponen data akan digunakan untuk petunjuk sektor-sektor yang digunakan untuk menyimpan data file. Setiap item data akan memiliki ukuran 16 bytes. Sehingga, total akan terdapat 32 item data yang bisa disimpan. Berikut adalah ilustrasi dari komponen data.
Setiap kolom pada item data akan menunjukkan alamat sektor yang digunakan untuk menyimpan data file. Karena satu byte hanya dapat menunjukkan alamat sektor hingga 255 (0xFF), maka kita hanya dapat menyimpan alamat sektor hingga sektor 0xFF. Oleh karena itu, item map ke-256 hingga akhir akan ditandai sebagai sektor yang telah digunakan.
Berikut adalah ilustrasi dari ketiga komponen filesystem yang telah dijelaskan sebelumnya.
Pada task ini, kalian diminta untuk membuat syscall readSector dan writeSector yang akan digunakan untuk membaca dari disk ke memory dan menulis dari memory ke disk.
Berikut adalah implementasi dari readSector dan penjelasannya.
void readSector(byte* buf, int sector) {
int ah = 0x02; // read sector service number
int al = 0x01; // number of sectors to read
int ch = div(sector, 36); // cylinder number
int cl = mod(sector, 18) + 1; // sector number
int dh = mod(div(sector, 18), 2); // head number
int dl = 0x00; // drive number
interrupt(
0x13,
ah << 8 | al,
buf,
ch << 8 | cl,
dh << 8 | dl
);
}-
Interrupt vector yang akan digunakan adalah
0x13untuk melakukan operasi disk I/O. -
Register
ahakan diisi dengan0x02yang menunjukkan operasiread. -
Register
alakan diisi dengan0x01yang menunjukkan jumlah sektor yang akan dibaca. -
Register
chdanclakan diisi dengan nomor cylinder dan sector yang akan dibaca.Pada floppy disk, terdapat 2 head, 18 sector per track, dan 36 track per cylinder. Sehingga, nomor cylinder akan dihitung dengan membagi nomor sektor dengan 36. Sedangkan nomor sector akan dihitung dengan mengambil sisa pembagian nomor sektor dengan 18 dan ditambahkan dengan 1.
-
Register
dhdandlakan diisi dengan nomor head dan drive yang akan digunakan.Pada floppy disk, terdapat 2 head. Sehingga, nomor head akan dihitung dengan membagi nomor sektor dengan 18 dan mengambil sisa pembagian dengan 2. Sedangkan nomor drive akan diisi dengan
0x00yang menunjukkan drive pertama.
Untuk writeSector, kalian dapat menggunakan implementasi yang sama dengan readSector dengan mengganti nilai register ah dengan 0x03 yang menunjukkan operasi write.
Pada filesystem.h, terdapat beberapa konstanta dan tipe data yang akan digunakan untuk membantu dalam implementasi filesystem. Kalian diminta untuk mengimplementasikan fungsi fsRead yang akan digunakan untuk membaca direktori atau file dari filesystem. Fungsi fsRead akan menerima parameter sebagai berikut.
void fsRead(struct file_metadata* metadata, enum fs_return* status);-
metadataadalah pointer kefile_metadatayang akan digunakan untuk menyimpan informasi dari file atau direktori yang akan dibaca.Struktur
file_metadataakan memiliki struktur sebagai berikut.struct file_metadata { byte parent_index; unsigned int filesize; char node_name[MAX_FILENAME]; byte buffer[FS_MAX_SECTOR * SECTOR_SIZE]; };
parent_indexadalah index dari parent node dari file atau direktori yang akan dibaca.filesizeadalah ukuran dari file yang akan dibaca.filesizeberisi 0 dalam pemanggilan fungsifsRead.node_nameadalah nama dari file atau direktori yang akan dibaca.bufferadalah pointer ke buffer yang akan digunakan untuk menyimpan data dari file atau direktori yang akan dibaca.bufferberisi0x00dalam pemanggilan fungsifsRead.
-
statusadalah pointer kefs_returnyang akan digunakan untuk menyimpan status dari operasi yang dilakukan.
Langkah-langkah yang harus dilakukan pada fungsi fsRead adalah sebagai berikut.
-
Membaca filesystem dari disk ke memory.
-
Iterasi setiap item node untuk mencari node yang memiliki nama yang sesuai dengan
metadata->node_namedan parent index sesuai denganmetadata->parent_index. -
Jika node yang dicari tidak ditemukan, maka set
statusdenganFS_R_NODE_NOT_FOUND. -
Jika node yang ditemukan adalah direktori, maka set
statusdenganFS_R_TYPE_IS_DIRECTORY. -
Jika node yang ditemukan adalah file, maka proses selanjutnya adalah sebagai berikut.
- Set
metadata->filesizedengan 0. - Lakukan iterasi i dari 0 hingga
FS_MAX_SECTOR - Jika data index ke-i dari node yang ditemukan adalah
0x00, maka hentikan iterasi. - Lakukan
readSectoruntuk membaca data dari sektor yang ditunjuk oleh data pada data index dengan sectors ke-i disimpan ke dalammetadata->buffer + i * SECTOR_SIZE. - Tambahkan
SECTOR_SIZEkemetadata->filesize.
- Set
-
Set
statusdenganFS_R_SUCCESS.
Selanjutnya kalian diminta untuk mengimplementasikan fungsi fsWrite yang akan digunakan untuk menulis file ke dalam filesystem. Fungsi fsWrite akan menerima parameter yang sama dengan fsRead sebagai berikut.
void fsWrite(struct file_metadata* metadata, enum fs_return* status);Pada fungsi fsWrite, metadata yang diterima akan berisi informasi berikut.
parent_indexadalah index dari parent node dari file yang akan ditulis. Jikaparent_indexadalah0xFF, maka file yang akan ditulis akan disimpan pada root directory.filesizeadalah ukuran dari file yang akan ditulis. Jikafilesizeadalah 0, maka file yang akan ditulis adalah direktori.node_nameadalah nama dari file yang akan ditulis.bufferadalah pointer ke buffer yang berisi data dari file yang akan ditulis.
Langkah-langkah yang harus dilakukan pada fungsi fsWrite adalah sebagai berikut.
-
Membaca filesystem dari disk ke memory.
-
Lakukan iterasi setiap item node untuk mencari node yang memiliki nama yang sama dengan
metadata->node_namedan parent index yang sama denganmetadata->parent_index. Jika node yang dicari ditemukan, maka setstatusdenganFS_R_NODE_ALREADY_EXISTSdan keluar. -
Selanjutnya, cari node yang kosong (nama node adalah string kosong) dan simpan index-nya. Jika node yang kosong tidak ditemukan, maka set
statusdenganFS_W_NO_FREE_NODEdan keluar. -
Iterasi setiap item data untuk mencari data yang kosong (alamat sektor data ke-0 adalah
0x00) dan simpan index-nya. Jika data yang kosong tidak ditemukan, maka setstatusdenganFS_W_NO_FREE_DATAdan keluar. -
Iterasi setiap item map dan hitung blok yang kosong (status blok adalah
0x00ataufalse). Jika blok yang kosong kurang darimetadata->filesize / SECTOR_SIZE, maka setstatusdenganFS_W_NOT_ENOUGH_SPACEdan keluar. -
Set nama dari node yang ditemukan dengan
metadata->node_name, parent index denganmetadata->parent_index, dan data index dengan index data yang kosong. -
Lakukan penulisan data dengan cara sebagai berikut.
-
Buatlah variabel counter yang akan digunakan untuk menghitung jumlah sektor yang telah ditulis (akan disebut dengan j).
-
Lakukan iterasi i dari 0 hingga
SECTOR_SIZE. -
Jika item map pada index ke-i adalah
0x00, maka tulis index i ke dalam data item sektor ke-j dan tulis data dari buffer ke dalam sektor ke-i. -
Penulisan dapat menggunakan fungsi
writeSectordarimetadata->buffer + i * SECTOR_SIZE. -
Tambahkan 1 ke j.
-
-
Tulis kembali filesystem yang telah diubah ke dalam disk.
-
Set
statusdenganFS_W_SUCCESS.
Setelah berhasil mengimplementasikan fungsi fsRead dan fsWrite, selanjutnya adalah pembuatan shell sederhana. Shell akan menggunakan read-eval-print-loop (REPL) yang akan menerima perintah dari user dan mengeksekusi perintah tersebut. Pada task ini, kalian diminta untuk mengimplementasikan fungsi printCWD yang akan digunakan untuk menampilkan current working directory (CWD) dari shell.
Fungsi printCWD akan menerima parameter byte cwd yang menunjukkan node index dari current working directory. Fungsi akan menampilkan path dari root (/) hingga node yang ditunjuk oleh cwd. Jika cwd adalah 0xFF, maka path yang ditampilkan adalah /. Setiap node yang ditampilkan akan dipisahkan oleh karakter /.
Selanjutnya, kalian diminta untuk mengimplementasikan fungsi parseCommand yang akan digunakan untuk memisahkan perintah yang diberikan oleh user. Fungsi parseCommand akan menerima parameter sebagai berikut.
void parseCommand(char* buf, char* cmd, char arg[2][64]);bufadalah string yang berisi perintah yang diberikan oleh user.cmdadalah string yang akan digunakan untuk menyimpan perintah yang diberikan oleh user.argadalah array of string yang akan digunakan untuk menyimpan argumen dari perintah yang diberikan oleh user.
Karena hanya akan ada 2 argumen yang diberikan oleh user, maka arg akan memiliki ukuran 2. Jika argumen yang diberikan oleh user adalah 1, maka arg[1] akan berisi string kosong. Jika argumen yang diberikan oleh user adalah 0, maka arg[0] dan arg[1] akan berisi string kosong.
Fungsi cd akan digunakan untuk mengubah current working directory dari shell. Berikut adalah spesifikasi dari fungsi cd.
-
cd <dirname>dapat memindahkan current working directory ke direktori yang berada di bawah current working directory. -
cd ..akan memindahkan current working directory ke parent directory dari current working directory. -
cd /akan memindahkan current working directory ke root directory. -
cdhanya dapat memindahkan current working directory ke direktori, tidak dapat memindahkan current working directory ke file. -
Implementasi relative path dan absolute path tidak diwajibkan.
Fungsi ls akan digunakan untuk menampilkan isi dari direktori. Berikut adalah spesifikasi dari fungsi ls.
-
lsakan menampilkan isi dari current working directory. -
ls .akan menampilkan isi dari current working directory. -
ls <dirname>akan menampilkan isi dari direktori yang berada di bawah current working directory. -
lshanya dapat menampilkan isi dari direktori, tidak dapat menampilkan isi dari file. -
Implementasi relative path dan absolute path tidak diwajibkan.
Fungsi mv akan digunakan untuk memindahkan file atau direktori. Berikut adalah spesifikasi dari fungsi mv.
-
mv <filename> <dirname>/<outputname>akan memindahkan file yang berada di bawah current working directory ke direktori yang berada di bawah current working directory. -
mv <filename> /<outputname>akan memindahkan file yang berada di bawah current working directory ke direktori root directory. -
mv <filename> ../<outputname>akan memindahkan file yang berada di bawah current working directory ke parent directory dari current working directory. -
mvhanya dapat memindahkan file, tidak dapat memindahkan direktori. -
Implementasi relative path dan absolute path tidak diwajibkan.
Fungsi cp akan digunakan untuk menyalin file. Berikut adalah spesifikasi dari fungsi cp.
-
cp <filename> <dirname>/<outputname>akan menyalin file yang berada di bawah current working directory ke direktori yang berada di bawah current working directory. -
cp <filename> /<outputname>akan menyalin file yang berada di bawah current working directory ke direktori root directory. -
cp <filename> ../<outputname>akan menyalin file yang berada di bawah current working directory ke parent directory dari current working directory. -
cphanya dapat menyalin file, tidak dapat menyalin direktori. -
Implementasi relative path dan absolute path tidak diwajibkan.
Fungsi cat akan digunakan untuk menampilkan isi dari file. Berikut adalah spesifikasi dari fungsi cat.
-
cat <filename>akan menampilkan isi dari file yang berada di bawah current working directory. -
Implementasi relative path dan absolute path tidak diwajibkan.
Fungsi mkdir akan digunakan untuk membuat direktori. Berikut adalah spesifikasi dari fungsi mkdir.
mkdir <dirname>akan membuat direktori yang berada di bawah current working directory.
Untuk melakukan testing, kalian dapat menjalankan make build run pada terminal untuk melakukan kompilasi dan menjalankan OS. Setelah itu tutup OS dan jalankan make generate test=1 untuk melakukan populasi file dan direktori ke dalam filesystem (ubah nilai 1 dengan nomor test yang sesuai). Setelah itu, jalankan kembali OS dengan make run dan coba perintah shell yang telah kalian implementasikan.
Berikut adalah struktur filesystem yang akan digunakan pada test ini.
/
├─ dir1
│ ├─ dir1-1
│ │ └─ dir1-1-1
│ └─ dir1-2
│ └─ dirname
├─ dir2
│ └─ dirname
└─ dir3
Berikut adalah struktur filesystem yang akan digunakan pada test ini.
/
├─ file-0
├─ dir-1
│ └─ dir-2
│ └─ . . .
│ └─ dir-62
└─ file-63
Berikut adalah struktur filesystem yang akan digunakan pada test ini.
/
├─ 1024
├─ 4096
├─ 8192_0
├─ 8192_1
├─ ...
└─ 8192_13
Berikut adalah struktur filesystem yang akan digunakan pada test ini.
/
├─ dir1
│ ├─ katanya
│ ├─ dir3
│ │ ├─ bikin
│ │ ├─ fp
│ │ └─ dir4
│ ├─ dir5
│ │ ├─ cuma
│ │ └─ seminggu
├─ dir2
└─ doang
-
Untuk debugging filesystem, kalian dapat mengecek menggunakan hexedit pada Linux atau HxD pada Windows. Dengan informasi sektor map
0x100, node0x101dan0x102, serta data0x103, kalian dapat mengetahui data yang tersimpan pada filesystem. Untuk mendapatkan offset byte dari sektor, kalian dapat menggunakan rumusoffset = sektor * 512atauoffset = sektor * 0x200. Sebagai contoh untuk mengetahui isi dari filesystem map, dapat membuka HxD dan hexedit dengan menekanCtrl + Gdan memasukkan offset byte dari sektor map (0x100 * 0x200 = 0x20000). -
bcctidak memberikan error checking sebanyakgcc. Kalian dapat menggunakangccuntuk melakukan error checking pada saat kompilasi. -
Dikarenakan penggunaan
bccdengan mode ANSI C, kalian tidak dapat mendeklarasikan variabel di tengah blok kode atau scope. Variabel harus dideklarasikan di awal blok kode atau scope. -
Selalu jalankan
makepada direktoripraktikum-final, bukan pada subdirektori. -
Sedikit sneak peek apa yang akan kalian buat.
Bochs.for.Windows.-.Display.2024-06-10.03-51-51.mp4







