Bài 1: Đọc và in thông tin từ BOOT- Khai báo thư viện: #include #include // thực hiện các thao tác nhập xuất #include // nhập xuất trên cửa sổ console #include // gọi các hàm liên quan đ
Trang 1BÁO CÁO THỰC HÀNH Học phần: Hệ điều hành Giảng viên: Đinh Văn Tuân
Sinh viên: Nguyễn Tiến Hiệp
Mã sinh viên: B21DCCN344
Nhóm học phần: 08
I Chuẩn bị
- Cài đặt máy ảo, đĩa ảo trên thẻ nhớ USB, lập trình các bài tập đọc FAT
- Chuẩn bị USB: Copy lên thẻ nhớ USB các file: Chương trình cài đặt phần mềm VMWare Player, File chứa máy ảo Windows 98 và ổ đĩa FAT 16 Windows 98 Fat16.vmx do giảng viên cung cấp
- Cài đặt máy ảo:
+ Bước 1: Cài đặt chương trình chạy máy ảo VMWare Player bằng cách chạy file VMware-player-full-16.2.4.exe
+ Bước 2: Chạy chương trình máy ảo vừa cài xong Mở máy ảo đã tạo sẵn bằng việc click vào nút Open a Virtual Machine Browse và chọn file Windows 98 Fat16.vmx trong thư mục Virtual
+ Bước 3: Khởi động máy ảo, hệ điều hành sẽ hiện lên Khởi động Turbo C, mở và chạy file FAT.cpp Tùy chỉnh file FAT.cpp để đọc thêm thông tin và làm bài tập
II Nội dung thực hành
Dựa vào nội dung được học trên lớp viết chương trình C/C++ để thực hiện các nội dung sau:
Trang 2Bài 1: Đọc và in thông tin từ BOOT
- Khai báo thư viện:
#include<iostream.h>
#include<stdio.h> // thực hiện các thao tác nhập xuất
#include<conio.h> // nhập xuất trên cửa sổ console
#include<dos.h> // gọi các hàm liên quan đến hệ điều hành
#include<string.h> // hàm xử lý xâu -
Khai báo cấu trúc Bootsector với FAT16:
struct BOOT
{ char jump[3];//lệnh JUMP chỉ thị cho CPU bỏ qua phần thông tin và nhảy tới thực hiện mã mồi của hệ điều hành, nếu đây là đĩa mồi của
hệ điều hành char OEM[8];// Trường này lưu trữ thông tin nhận dạng hoặc nhà sản xuất OEM (Original Equipment Manufacturer) của thiết bị gốc Thường có độ dài là 8 bytes
int bytes_per_sector;// Lưu trữ số byte trong mỗi sector Sector là đơn vị cơ bản của việc lưu trữ trên đĩa cứng
char sectors_per_cluster;// Số sector trong mỗi cluster, là đơn vị lưu trữ cơ bản trong hệ thống tập tin FAT
int reserved;.// Dùng để lưu trữ số lượng sector được dành trước khi bắt đầu vùng dữ liệu
char FAT_cnt; // Số lượng bảng FAT trên đĩa Đối với FAT16, thường là 2 int ROOT_size; // Kích thước của phần thư mục gốc được xác định bằng số lượng entry trong thư mục gốc int total_sectors;.// Tổng số sector trong đĩa
char media; // Xác định loại phương tiện lưu trữ (ví dụ: 0xF8 là đĩa cứng)
int FAT_size; // Kích thước của mỗi bảng FAT
int sectors_per_track; // Số sector trên mỗi track của đĩa int
head_cnt;// Số lượng đầu đọc/ghi trên đĩa
Trang 3long hidden_sectors; // Số lượng sector ẩn trước khi đĩa bắt đầu long total_sectors_long; // Tổng số sector trên đĩa, sử dụng kiểu dữ liệu long char unknown[3];// Các trường không xác định char serial; //
Số Serial của đĩa char volume[11]; // Tên của phân vùng char FAT_type[8]; // Loại hệ thống tập tin (ví dụ: FAT16)
char loader[448]; // Mã mồi (bootstrap code) có độ dài 448 byte, được thực thi khi hệ điều hành được khởi động từ đĩa
char mark[2]; //Dấu hiệu cuối cùng của 512-byte bootstrap sector, thường có giá trị là 0x55AA để xác nhận tính đúng đắn của sector
};
- Hàm in Boot: void main(){ int drive = 3;
//A=0, B=1, C=2, D=3 …
//Đọc boot sector từ ổ đĩa D
BOOT boot; //khởi tạo 1 struct
int res = absread(drive, 1, 0, &boot); //Hàm cho phép đọc BOOT if(res != 0){ //bằng 0 đọc được, khác 0 không đọc được
printf(“Cannot read boot sector\n”); return; }
printf(“Reading disk parameters\n”);//thông báo đang đọc các tham
số của ổ đĩa
printf(“ -\n”);
printf(“Sector size: %d\n”, boot.bytes_per_sector);//in thông tin
về kích thước của sector printf(“FAT type:”);
// duyệt qua từng ký tự trong mảng FAT type của cấu trúc BOOT int i;
for(i=0; i<8;i++)
// In ra màn hình mỗi ký tự trong mảng FAT Type Dòng này sẽ được lặp qua 8 lần (đối với FAT16)
printf(“%c”, boot.FAT_type[i]);
}
Bài 2: Đọc, phân tích, hiển thị nội dung boot sector với bảng FAT
int main(){
Trang 4//Đọc bảng FAT16 từ ổ đĩa
D
unsigned int *fat = (unsigned int *)malloc (boot.FAT_size
* boot.bytes_per_sector); \\Cấp phát bộ nhớ động để lưu trữ bảng FAT16
\\Kiểm tra xem việc cấp phát bộ nhớ có thành công hay không Nếu không đủ bộ nhớ, in ra thông báo lỗi và kết thúc chương trình
if (fat == NULL){ printf(“Not enough
memory\n”); return; }
printf(“\n\n”; printf(“Reading FAT16
parameters\n”);
printf(“ -\n”);
printf(“FAT size: %d \n”, boot.FAT_size);\\ In ra kích thước của mỗi bảng FAT16
printf(“Reserved: %d \n”, boot.reserved); \\In ra số lượng sector được dành trước khi bắt đầu vùng dữ liệu (reserved)
res = absread(drive, boot.FAT_size, boot.reserved, fat); \\Đọc bảng
FAT16
//Kiểm tra xem việc đọc bảng FAT16 có thành công hay không Nếu không, in ra thông báo lỗi và kết thúc chương trình
if(res != 0){ printf(“Cannot read
FAT\n”); return;
}
//in ra màn hình giá trị của 15 ô đầu tiên trong bảng
FAT printf(“Content of first 15 FAT cells:”); for(i=0;
i<15;i++) printf(“%u “, fat[i]);
//Đếm số lượng Clusters không sử dụng trong 100 Clusters đầu tiên int free_count = 0; //khởi tạo biến đếm
//Duyệt qua 98 Clusters đầu tiên (2-99), do Clusters thứ 0 và 1 không được sử dụng
for(i=2; i < 100;i++){ //first 2 clusters are not used
if(fast[i] == 0) free_count++; //kiểm tra
}
Trang 5printf(“\n”); printf(“Number of free clusters from first
100 clusters:”); printf(“%d\n”, free_count);
tin
}
//in ra màn hình các Cluster của file bắt đầu từ Cluster thứ n
unsigned int n = 5; //khởi tạo n=5
unsigned int cur = n; //khởi tạo biến duyệt qua các Cluster của tập printf(“Clusters of a file from %u:
“,n); while(cur < 0xFFF8){
printf(“->%u”, cur); cur = fat[cur];
}
Giải thích:
+ Cấp phát không gian nhớ cho bảng FAT Không gian nhớ được tính bằng boot.FAT_size(kích thước FAT tính bằng sector – đối với FAT 12/16) * boot.bytes_per_sector(kích thước sector tính bằng bytes) + Hiển thị một số nội dung của bảng FAT: boot.FAT_size, boot.reserved(Số sector dành cho vùng đầu đĩa đến trước FAT)
+ Để đọc bảng FAT cũng sử dụng hàm đọc đĩa absread
+ Sau khi đọc bảng FAT thành công, sử dụng vòng lặp để duyệt 15 ô đầu tiên trong bảng FAT và in nội dung của nó
Bài 3: Đọc, phân tích, hiển thị ROOT
- Khai báo 1 struct của ROOT:
struct ROOT
{ char name[8]; //Lưu trữ tên của ROOT hay lưu trữ tên của mục trong thư mục gốc char ext[3]; //Lưu trữ phần mở rộng của tên file, thêm dấu
Trang 6trắng ở cuối nếu nó lớn hơn 3 bytes char attr; //Lưu trữ thuộc tính của mục, chẳng hạn như chỉ đọc, ẩn, hệ thống, thư mục,
char reserved[10]; //Các trường dự trữ không xác định
char time[2]; //thời gian tạo (giờ, phút, giây)
char date[2]; //ngày tạo (ngày, tháng, năm)
int first_cluster; //cluster đầu tiên của mục
long size; //kích thước của ROOT
};
- Đọc phân tích và hiển thị ROOT:
int main(){
//Đọc ROOT từ ổ đĩa D
printf(“\n\n”); printf(“Reading ROOT
information:\n”);
printf(“ -\n”);
//tính toán số byte cần để lưu trữ thông tin trong ROOT, giả sử mỗi mục trong phần thư mục chiếm 32byte int num_byte
= boot.ROOT_size *32; //sizeof(ROOT)
//cấp phát bộ nhớ động và kiểm tra cấp phát có thành công hay không
ROOT *root = (ROOT *)malloc(num_byte);
If(root == NULL) return;
//Tính toán số lượng sector cần để lưu trữ thông tin trong phần thư mục gốc (ROOT) int num_sector = num_byte /
boot.bytes_per_sector;
//Tính toán vị trí bắt đầu của phần thư mục gốc (ROOT) trên đĩa dựa trên thông tin trong bảng BOOT
int root_begin = boot.reverved + boot.FAT_size * boot.FAT_cnt; res = absread(drive, num_sector, root_begin, (void *)root);//hàm đọc ROOT
Trang 7//Kiểm tra xem việc đọc thông tin từ phần thư mục gốc có thành công hay không Nếu không, in ra thông báo lỗi và thoát khỏi hàm if(res != 0){ printf(:\n Cannot read
ROOT \n); return;
}
//In ra 3 mục đầu tiên trong ROOT
printf(“3 first items of root:\n”);
for(i=0;i<3;i++){
//Kiểm tra xem tên của mục có bắt đầu bằng dấu cách hay
không Nếu có, bỏ qua mục này và tiếp tục với mục tiếp theo if(root[i].name[0] == ‘ ‘) continue;
//In ra tên của mục đó cho đến khi gặp dấu cách hoặc hết độ dài cho phép
for(int j=0; j<0 && root[i].name[j] != ‘ ‘; j++)
printf(“%c”, root[i].name[j]);
printf(“\t”);
//In ra màn hình số cluster đầu tiên và kích thước của mục printf(“%d \t %ld\n”, root[i].first_cluster, root[i].size); }
}
* Giải thích:
+ Cấp phát động không gian cho ROOT với không gian nhớ được tính bằng công thức: num_byte = boot.ROOT_size(Số khoản mục tối đa trong ROOT) * 32
+ Để đọc ROOT cần tính tổng số sector cần đọc theo công thức: num_sector = num_byte / boot.bytes_per_sector, vị trí sector bắt đầu được tính theo công thức: root_begin = boot.reserved + boot.FAT_size * boot.FAT_cnt(số bảng FAT)
+ Sử dụng hàm absread để đọc ROOT
Trang 8+ Sau khi đọc xong sử dụng vòng lặp for để đọc 3 khoản mục đầu tiên trong đó có name(tên khoản mục), first_cluster(Cluster của khoản mục), size(kích thước khoản mục)
Bài 4: Duyệt số thứ tự hoặc nội dung các cluster của file cho trước int main(){
//Tìm một tập tin với tên được cung cấp trong biến char filename[ ] printf(“\n”);
//in ra các cluster thuộc về một tập tin cụ thể
printf(“Clusters belong to file readme:”);
int k; char str[9]; int first_cluster = -1;
for(i=0; i< boot.ROOT_size; i++){
//Sao chép root[i].name vào biến str để tạo thành chuỗi ký tự có ký
tự kết thúc null
for(k=0;k<8 && root[i].name[k] != ‘ ‘; k++)
str[k] = root[i].name[k];
str[k] = 0;
//Nhập tên tập tin từ người dùng để tìm kiếm
char filename[8]; printf(“\n Enter a file
name:”); scanf(“%s”, filename);
if(strcmp(str, filename) == 0){
first_cluster = root[i].first_cluster;
break;
}
}
// In ra các cluster thuộc về tập tin if(first_cluster >= 0){ // Kiểm tra xem đã tìm thấy tập tin hay chưa
// Nếu đã tìm thấy tập tin, in ra màn hình các cluster thuộc về tập tin đó bằng cách duyệt qua bảng FAT
cur = first_cluster;
while(cur < 0xFFF8){
Trang 9printf(“%u “,cur); cur =
fat[cur];
}
}
// Giải phóng bộ nhớ đã cấp phát cho mảng ROOT và fat.getchar()
để đợi một ký t t bàn phím trước khi thoát chự ừ ương trình free(root);
free(rat);
getchar();
}
* Giải thích:
+ Để duyệt các cluster của 1 file cho trước ta phải kiểm tra file đó
có tồn tại trong root Sử dụng hàm strcmp(str, filename) để kiểm tra + Nếu file đó tồn tại đặt first_cluster = root[i].cluster
+ Sử dụng vòng lặp while để duyệt với điều kiện cur < 0xFFF8 với cur ban đầu bằng first_cluster Qua từng vòng lặp cur sẽ bằng giá trị của cluster tiếp theo
Bài 5: Viết đoạn chương trình in ra giống câu lệnh dir
a Hàm chuyển đổi giá trị của ký tự x thành một mảng nhị phân 8 bit và lưu vào mảng s
void getbit(char x, char s[8]){
for( int i=0;i<8;i++){
//Kiểm tra xem bit thứ i của x có bật hay không ?
//Nếu bit đó bật, kết quả của biểu thức sẽ khác 0 và điều kiện trong toán tử điều kiện ? : sẽ trả về giá trị 1
//Ngược lại, nếu bit đó tắt, kết quả của biểu thức sẽ là 0 và giá trị trả về sẽ là 0
s[i]=(x &(1<<i)) ?1:0;
Trang 10}
b Hàm chuyển đổi một nhị phân thành 1 số nguyên
//Nhận đầu vào là một con trỏ s trỏ tới một mảng ký tự, và hai số nguyên i
và j, đại diện cho các chỉ số mảng int convert(char *s, int i, int j){
//sum:tổng của các giá trị nhị phân
//pow:giá trị của lũy thừa 2 int
sum=0,pow=1; for( int
k=i;k<=j;k++){
//Giá trị nhị phân được nhân với giá trị lũy thừa pow và cọng vào biến sum sum+=pow * (s[k]-‘0’); //trừ đi ‘0’ để chuyển đổi từ ký tự thành
số pow*=2; //nhân với 2 để tính đến vị trí nhị phân tiếp theo
} return
sum;
}
c In ra nội dung giống lệnh DIR
Hàm Dir được dùng để hiển thị danh sách các tệp và thư mục trong mục hiện tại.Mỗi đối tượng hiển thị tên, kích thước,kiểu,ngày tạo, thể loại printf(" -DIR -\n");
for( i=0;i<6;i++){ //do chỉ có 6 tệp
int j=0;
// In ra tên của tệp tin/ thư mục
for( j=0;j<8;j++)
{ printf("%c",root[i].name[j]
);
}
printf("\t");
//In ra kiểu của tệp tin/ thư mục
for( j=0;j<3;j++)
{ printf("%c",root[i].ext[j]
);
}
printf("\t");
//In ra kích thước của tệp tin/thư mục theo đúng định dạng
char n[20];
//Chuyển đổi kích thức của tệp tin/thư mục từ kiểu long sang
Trang 11kiểu chuỗi kí tự n.
sprintf(n,"%ld",root[i].size);
//Nếu chiều dài của n chia hết cho 3 thì in ra dấu ‘,’ sau các vị trí chia dư cho 3 bằng 2, ngoại trừ 2 vị trí đầu và cuối
if( strlen(n) %3==0){ for( j=0;j<strlen(n);j++){ if( j!=0 && j!=strlen(n)-1 && j%3==2){
printf("%c,",n[j]);
} else{
printf("%c",n[j]);
} }
//Nếu chiều dài của n chia cho 3 dư 1 thì in ra dấu ‘,’ sau các
vị trí chia dư cho 3 bằng 0, ngoại trừ 2 vị trí đầu và cuối
}else if( strlen(n) %3==1){ for(
j=0;j<strlen(n);j++){ if(
j!=strlen(n)-1 && j%3==0){
printf("%c,",n[j]);
} else { printf("%c",n[j]);
} }
//Nếu chiều dài của n chia cho 3 dư 2 thì in ra dấu ‘,’ sau các
vị trí chia dư cho 3 bằng 1, ngoại trừ 2 vị trí đầu và cuối
}else { for( j=0;j<strlen(n);j++)
{ if( j!=strlen(n) -1&& j
%3==1) { printf("%c,",n[j]);
} else { printf("%c",n[j]);
} }
}
printf("\t");
//In ra ngày, tháng, năm tạo tệp tin/ thư mục theo đúng định dạng
char s[16],s1[8],s2[8];
//chuyển đổi ngày và tháng tạo file về dạng một mảng nhị
Trang 12getbit(root[i].date[0],s1);
//chuyển đổi năm tạo file về dạng một mảng nhị phân getbit(root[i].date[1],s2);
//Lưu các giá trị nhị phân của ngày, tháng, năm tạo file vào cùng trong một mảng
for( j=0;j<8;j++){
s[j]=s1[j];
s[j+8]=s2[j];
}
//Sử dụng hàm convert để chuyển đổi mảng nhị phân thành số nguyên
int ngay=convert(s,0,4);//Ngày từ bit 0 đến bit 4
int thang=convert(s,5,8); //Tháng từ bit 5 đến bit 8 int
nam=convert(s,9,15); //Năm từ bit 9 đến bit 15 nam+=1993;
//để hiển thị năm 2023 //In ra theo định dạng dd-mm-yyyy if(
ngay<10){ printf("0%d-",ngay);
}else{
printf("%d-",ngay);
} if( thang < 10){
printf("0%d-",thang);
}else{
printf("%d-",thang);
}
printf("%d\t",nam%100);
//In ra giờ và phút tạo tệp tin/ thư mục theo đúng định dạng
//chuyển đổi giờ tạo file về dạng một mảng nhị phân getbit(root[i].time[0],s1);
//chuyển đổi giờ tạo file về dạng một mảng nhị phân getbit(root[i].time[1],s2);
//Lưu các giá trị nhị phân của giờ, phút tạo file
vào cùng trong một mảng
for( j=0;j<8;j++)
{ s[j]=s1[j];
s[j+8]=s2[j];
Trang 13//Sử dụng hàm convert để chuyển đổi mảng nhị phân thành số nguyên
int gio=convert(s,11,15);//Giờ từ bit 11 đến 15
int phut=convert(s,5,10);//Phút từ bit 5 đến 10
//In ra theo đúng định dạng: giờ:phút(a hoặc p)
if( gio<12){
printf("%d:",gio);
}else{
printf("%d:",gio-12);
} if( phut <10){
printf("0%d",phut);
}else{
printf("%d",phut);
} if( gio<12) printf("a");else
printf("p");
printf("\n");
}
* Giải thích:
+ Viết 2 hàm getBit() và hàm convert() Hàm getBit có chức năng chuyển 1 kí tự kiểu char sang 1 dãy bit nhị phân Hàm convert có chức năng biến đổi 1 dãy nhị phân sang một số kiểu int
+ Duyệt qua 7 thư mục hoặc file có trong root
+ Ban đầu duyệt qua tên thư mục hay file đó với độ dài 8 byte kiểu char
+ Duyệt kích thước file hay thư mục đó và in đúng định dạng có dấu ‘,’ ngăn cách khi kích thước có nhiều hơn 3 chữ số + Sử dụng 3 dãy kí tự char s[16], s1[8], s2[8] và hàm getBit() để chuyển đổi kí tự sang dãy bit