feat[OS/win4]: сделал эту еб**ую лабу

This commit is contained in:
ElectronixTM
2025-10-10 00:20:58 +03:00
parent 72d5756b79
commit 058af0b4b8
7 changed files with 393 additions and 0 deletions

View File

@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.30)
# Set the project name
project(
lab4_win
LANGUAGES C
VERSION 0.0.1
DESCRIPTION "Лаба на тему виртуальной памяти"
)
add_executable(out main.c)

View File

@ -0,0 +1,35 @@
# Виртуальная память
Прошу обратить внимание на файлики report.txt и vm.bin. report.txt - место, куда будут сгружаться отчеты потока-монитора, vm.bin - файл, где находится программа для потока-симулятора. Подробнее про его команды можно почитать в методичке. Также прикладывается скрипт на питоне для генерации программ, его описание приложу, когда напишу
# Как запускать
Лаба требует файлов report.txt и vm.bin, и как вы их создадите в общем-то не важно. vm.bin я создавал через SIMASM. report создастся сам. Компилируете лабу любым нормальным для вас способом, она в терминальчик выдаст порядок своих действий. Там 2 потока, один делает вид, что работает с памятью и исполняет команды из vm.bin. Он будет писать какую команду исполнил и успешно ли. Второй поток - монитор, он раз в какое-то время (можно отрегулировать в сорцах) пишет отчетики в report.txt. Там он пишет статистику по занятой памяти, страничному файлу и подобному. Для его более удобного чтения есть скрипт util.py. Он тоже написан за 5 минут, поэтому для него важно, чтобы файл назывался именно report.txt, она помимо свободных страниц выведет еще и процентовку и немного другой инфы, в общем читать это поудобнее будет.
На выходе сейчас у вас должно получиться что запрашивается сколько-то памяти, потом пишется отчет, потом с памятью заигрывается, пишется новый отчет, потом память освобождается и пишется 3 отчет. на самом деле их будет 5 (все будут в одном файле), но это потому что с таймингами особо не угадаешь
В общем-то о том, что происходит в рамках отчета и надо будет рассказать на лабе
Касаемо кода сложно что-то хорошее сказать, только скажу, что не прям все я протестировал, так что если будете заигрывать с чем-то кроме аллокаций и освобождений, то там бог знает что выйдет
# SIMASM
Простой скрипт, фактически просто пачка регулярок, которые из человекочитаемого текста собирают бинарь, который надо по условию задачи
Каждая команда состоит из 5 частей и заканчивается точкой с запятой. Не все команды используют все части, но так как писать нормальный парсер мне лень, а simasm был написан за 10 минут, чтобы я во время тестов по 10 раз байты руками не перекладывал, он требует все поля. Теперь по каждому полю отдельно:
1. Поле, задающее минимальное время от начала процесса, которое должно пройти, прежде чем команда исполнится. Должно указываться в десятичных цифрах и может заканчиваться на 2 суффикса (ms - миллисекунды, s - секунды)
2. Поле, указывающее адрес региона, над которым будет производиться махинация. регион не должен быть слишком маленьким, потому что первые 64 КБ виртуальной памяти зарезервированы операционкой
3. Команда. Почти полностью соответствуют командам из методички:
- `exit` - выходит из программы, но фактически не используется
- `reserve_region` - резервирует какое-то количество памяти (указывается в 4 параметре), отдает страницы, но фактически еще память из системы не забирает. Чтобы ее забрать надо что-то со страницами поделать. Для этого есть `mess_region`
- `pass_block` - резервирует регион с `MEM_COMMIT`, не знаю, зачем это надо, но это единственное сходное в методичке что есть
- `non_save_block` - аллоцирует страницы, изменения на которых не пойдут на старничный файл
- `free_region` - освобождает занятые страницы
- `lock_block` - блокирует страницы и препятствует их попаданию в swap
- `unlock_block` - разблокирует страницы и разрешает им уходить в swap
- `mess_region` - выполняет тупые операции чтения или записи с блоком, чтобы ось его на самом деле выдала, а не просто сказала, что сделала это
4. Размер области или региона в памяти. Доступны суффиксы Gb - гигабайты, Mb - мегабайты, kb - килобайты
5. Параметр записи - что разрешается делать со страницами r - только читать, `rw` - читать и писать, `rwe` - читать, писать и исполнять, `re` - читать и исполнять, `e` - только исполнять
если подать флаг `-f FILE` - будет читать из указанного файла команды, иначе будет читать из стандартного потока ввода, если подать `-o OUTPUT` - запишет бинарный вывод в файл, в противном случае выдаст хексы в стандартный вывод

222
OSs/lab-virt-win/main.c Normal file
View File

@ -0,0 +1,222 @@
#include <stdio.h>
#include <windows.h>
#include <stdint.h>
enum {
THREAD_SIM,
THREAD_MONITOR,
THREAD_NUM
};
HANDLE threads[THREAD_NUM];
int threads_params[THREAD_NUM];
SYSTEM_INFO sys_info;
DWORD WINAPI thread_sim(LPVOID param);
DWORD WINAPI thread_monitor(LPVOID param);
#define SIMOP_EXIT 0x00
#define SIMOP_RESERVE 0x01
#define SIMOP_PASS_BLOCK 0x02
#define SIMOP_NON_SAVE 0x03
#define SIMOP_FREE_REG 0x04
#define SIMOP_RETURN_BLOCK 0x05
#define SIMOP_LOCK_BLOCK 0x06
#define SIMOP_UNLOCK_BLOCK 0x07
#define SIMOP_MESS_REGION 0x08
#define SLEEP_MILLIS 2000
// Некоторые поля увелчены для выравнивания
struct sim_cmd{
uint64_t millis_offset;
uint64_t op;
uint64_t region;
uint64_t reg_sz;
uint64_t access;
};
int main()
{
int ret = 0;
GetSystemInfo(&sys_info);
threads[THREAD_SIM] = CreateThread(NULL, 0, thread_sim,
&threads_params[THREAD_SIM], 0, NULL);
if (!threads[THREAD_SIM]) {
printf("Couldn't create simulator\n");
return EXIT_FAILURE;
}
threads[THREAD_MONITOR] = CreateThread(NULL, 0, thread_monitor,
&threads_params[THREAD_MONITOR], 0, NULL);
if (!threads[THREAD_MONITOR]) {
printf("Couldn't create monitor\n");
ret = EXIT_FAILURE;
goto close_sim_thread;
}
printf("waiting...\n");
DWORD status = WaitForMultipleObjects(THREAD_NUM, threads, TRUE, INFINITE);
printf("done waiting (%lu).\n", GetLastError());
close_monitor_thread:
CloseHandle(&threads[THREAD_MONITOR]);
close_sim_thread:
CloseHandle(&threads[THREAD_SIM]);
return ret;
}
int read_sim_cmd(FILE *src, struct sim_cmd *dest)
{
const size_t cmd_sz = sizeof(struct sim_cmd);
size_t bytes_read = fread(dest, sizeof(char), cmd_sz, src);
if (bytes_read != cmd_sz) return -ENODATA;
return 0;
}
int write_sim_cmd(FILE *src, struct sim_cmd *dest)
{
const size_t cmd_sz = sizeof(struct sim_cmd);
size_t bytes_written = fwrite(dest, sizeof(char), cmd_sz, src);
if (bytes_written != cmd_sz) return -ENODATA;
return 0;
}
#define QUIT_NUMBER 'q'
static uint64_t last_clock = 0;
static int do_cmd(struct sim_cmd *cmd)
{
if (cmd->millis_offset > last_clock) Sleep(cmd->millis_offset - last_clock);
switch (cmd->op) {
case SIMOP_RESERVE:
if (!VirtualAlloc((LPVOID)cmd->region, cmd->reg_sz,
MEM_RESERVE | MEM_COMMIT, cmd->access)) {
return -ENOMEM;
}
break;
case SIMOP_FREE_REG:
if (VirtualFree((LPVOID)cmd->region, cmd->reg_sz, MEM_DECOMMIT) == 0) {
return -EFAULT;
}
break;
case SIMOP_LOCK_BLOCK:
if (VirtualLock((LPVOID)cmd->region, cmd->reg_sz) == 0) {
return -ENOLCK;
}
break;
case SIMOP_UNLOCK_BLOCK:
if(VirtualUnlock((LPVOID)cmd->region, cmd->reg_sz) == 0) {
return -ENOLCK;
}
break;
case SIMOP_PASS_BLOCK:
if (!VirtualAlloc((LPVOID)cmd->region, cmd->reg_sz, MEM_COMMIT, cmd->access)) {
return -ENOMEM;
}
break;
case SIMOP_NON_SAVE:
if (!VirtualAlloc((LPVOID)cmd->region, cmd->reg_sz,
MEM_RESERVE | MEM_RESET, cmd->access)) {
return -ENOMEM;
}
case SIMOP_RETURN_BLOCK:
if (!VirtualFree((LPVOID)cmd->region, 0, MEM_RELEASE)) {
printf("error was %lu\n", GetLastError());
return -EFAULT;
}
break;
case SIMOP_MESS_REGION:
printf("messing with pages...\n");
if (cmd->access == PAGE_READWRITE || cmd->access == PAGE_EXECUTE_READWRITE) {
printf("memsetting\n");
memset((LPVOID)cmd->region, 0x77, cmd->reg_sz);
} else if (cmd->access == PAGE_READONLY) {
#define BUFF_SIZE 256
printf("reading to buffer...\n");
char buff[BUFF_SIZE] = {0};
for (size_t i = 0; i < cmd->reg_sz; i += BUFF_SIZE) {
memcpy(buff, &((char*)(cmd->region))[i], BUFF_SIZE);
}
}
break;
case SIMOP_EXIT:
return QUIT_NUMBER;
default:
break;
}
return 0;
}
DWORD WINAPI thread_sim(LPVOID param)
{
DWORD status = 0;
FILE *program = fopen("vm.bin", "r");
if (!program) {
printf("can't open file vm.bin\n");
status = EFAULT;
goto exit;
}
static struct sim_cmd cmd;
while(read_sim_cmd(program, &cmd) == 0) {
int ret = do_cmd(&cmd);
printf("command (%02llx) done with code (%d)\n", cmd.op, ret);
status = abs(ret);
if (ret < 0) {
break;
} else if (ret == QUIT_NUMBER) {
status = 0;
break;
}
}
close_program:
fclose(program);
exit:
printf("return from vm\n");
return status;
}
int write_report(FILE *f)
{
MEMORYSTATUS ms;
GlobalMemoryStatus(&ms);
size_t bytes_written = fprintf(
f,
"Memory status (Total/Avail):\n"
"Physical: %zu/%zu\n"
"Virtual: %zu/%zu\n"
"Page file: %zu/%zu\n"
"Page size (bytes): %lu\n"
"Granularity: %lu\n\n",
ms.dwTotalPhys, ms.dwAvailPhys,
ms.dwTotalVirtual, ms.dwAvailVirtual,
ms.dwTotalPageFile, ms.dwAvailPageFile,
sys_info.dwPageSize,
sys_info.dwAllocationGranularity
);
if (bytes_written < 1) {
return -EFAULT;
}
return 0;
}
DWORD WINAPI thread_monitor(LPVOID param)
{
DWORD status = 0;
FILE *report = fopen("report.txt", "w");
if (!report) {
printf("fault\n");
status = EFAULT;
goto close_report;
}
for (size_t i = 0; i < 5; i++) {
Sleep(SLEEP_MILLIS);
printf("report written with status: %d\n", write_report(report));
}
close_report:
fclose(report);
return status;
}

101
OSs/lab-virt-win/simasm.py Normal file
View File

@ -0,0 +1,101 @@
#/usr/bin/python3
"""
Маленький скрипт для сборки бинарных файликов для потока-симулятора из +- человекочитаемого текста
"""
import argparse
import sys
import struct
import re
time_suffixes = {
"": 1,
"ms": 1,
"s": 1000,
}
size_suffiexes = {
"": 1,
"kb": 2 ** 10,
"Mb": 2 ** 20,
"Gb": 2 ** 30
}
operations = {
"exit": 0x00,
"reserve_region": 0x01,
"pass_block": 0x02,
"non_save_block": 0x03,
"free_region": 0x04,
"return_block": 0x05,
"lock_block": 0x06,
"unlock_block": 0x07,
"mess_region": 0x08
}
access = {
"page_noaccess": 0x01,
"page_readonly": 0x02,
"r": 0x02,
"page_readwrite": 0x04,
"rw": 0x04,
"page_execute": 0x10,
"e": 0x10,
"page_execute_read": 0x20,
"re": 0x20,
"page_execute_readwrite": 0x40,
"rwe": 0x40
}
def main():
parser = argparse.ArgumentParser(prog="simasm")
parser.add_argument("--file", "-f", type=str, help="file with program")
parser.add_argument("--output", "-o", type=str, help="file to store program")
args = parser.parse_args()
program = ""
if args.file:
with open(args.file, "r", encoding="utf-8") as f:
program = f.read()
else:
program = sys.stdin.read()
program = re.sub("#.*$", "", program, flags=re.M)
program = " ".join(program.split())
lines = list(filter(lambda s: s!="", program.split(";")))
binary = bytearray()
for line in lines:
words = line.split()
if len(words) != 5:
print("wrong line")
sys.exit(1)
# timestamp
m = re.match(r"(\d+)(\w*)", words[0])
assert m
time_offset = int(m.group(1)) * time_suffixes[m.group(2)]
region_in = words[1]
region = 0
if region_in.startswith("0x"):
region = int(region_in, 16)
else:
region = int(region_in)
operation = operations[words[2]]
m = re.match(r"(\d+)(\w*)", words[3])
assert m
size = int(m.group(1)) * size_suffiexes[m.group(2)]
pg_access = access[words[4]]
binary += struct.pack("QQQQQ", time_offset, operation, region, size, pg_access)
if args.output:
with open(args.output, "wb") as f:
f.write(binary)
else:
print(binary.hex())
if __name__ == "__main__":
main()

View File

@ -0,0 +1,4 @@
# time | region | operation | size | access
1000ms 0x010000 reserve_region 1Gb rw;
3000ms 0x010000 mess_region 1Gb rw;
3000ms 0x010000 return_block 1Gb rw;

20
OSs/lab-virt-win/util.py Normal file
View File

@ -0,0 +1,20 @@
#/usr/bin/python3
# чтобы удобнее было посмотреть разницу
import re
with open("report.txt", 'r') as f:
lines = f.readlines()
while len(lines) > 0:
for line in lines[1:4]:
line = line.strip()
m = re.match(r"([\w ]+): (\d+)/(\d+)", line)
assert m
name = m.group(1)
total = int(m.group(2))
avail = int(m.group(3))
print(f"{name}: {total}/{avail} ({avail/total*100:.2f}%, "
f"delta: {total - avail})")
print()
lines = lines[7:]

BIN
OSs/lab-virt-win/vm.bin Normal file

Binary file not shown.