]> wirehaze git hosting - ppos.git/blob - ppos/hardware/disk.c

wirehaze git hosting

tasks implementation (ongoing)
[ppos.git] / ppos / hardware / disk.c
1 // PingPongOS - PingPong Operating System
2 // Prof. Carlos A. Maziero, DINF UFPR
3 // Versão 2.0 -- Junho de 2025
4
5 // ATENÇÃO: ESTE ARQUIVO NÃO DEVE SER ALTERADO
6 // ALTERAÇÕES SERÃO DESCARTADAS NA CORREÇÃO.
7
8 // Implementação do disco virtual, que simula um disco rígido.
9
10 // para depurar a operação do disco
11 //#define DEBUG_DISK 1
12
13 // padrão de API UNIX a usar (para sigaction)
14 #define _XOPEN_SOURCE 700
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <signal.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <time.h>
24 #include "cpu.h"
25 #include "disk.h"
26
27 #define NOERROR 0
28 #define ERROR -1
29
30 // parâmetros de operação do disco simulado
31 #define DISK_BLOCK_SIZE 64 // tamanho de cada bloco, em bytes
32 #define DISK_DELAY_MIN 30 // atraso minimo, em milisegundos
33 #define DISK_DELAY_MAX 300 // atraso maximo, em milisegundos
34 #define DISK_SIGNAL SIGRTMIN // sinal a ser usado no timer interno
35
36 //----------------------------------------------------------------------
37
38 // estrutura com os dados internos do disco (estado inicial desconhecido)
39 struct disk_t
40 {
41 int status; // estado do disco
42 char *file; // nome do arquivo que simula o disco
43 int fd; // descritor do arquivo que simula o disco
44 int numblocks; // numero de blocos do disco
45 int blocksize; // tamanho dos blocos em bytes
46 char *buffer; // buffer da próxima operação (read/write)
47 int prev_block; // bloco da ultima operação
48 int next_block; // bloco da próxima operação
49 int delay_min, delay_max; // tempos de acesso mínimo e máximo
50 timer_t timer; // timer que simula o tempo de acesso
51 struct itimerspec delay; // struct do timer de tempo de acesso
52 struct sigevent sigev; // evento associado ao timer
53 struct sigaction signal; // tratador de sinal do timer
54 };
55
56 // disco
57 static struct disk_t disk = {.status = DISK_STATUS_UNKNOWN};
58
59 //----------------------------------------------------------------------
60
61 // arma o timer que simula o tempo de acesso ao disco;
62 // ao disparar, ele gera um sinal DISK_SIGNAL
63 static void disk_settimer()
64 {
65 int time_ms;
66
67 // tempo no intervalo [DISK_DELAY_MIN ... DISK_DELAY_MAX], proporcional a
68 // distancia entre o proximo bloco a ler (next_block) e a ultima leitura
69 // (prev_block), somado a um pequeno fator aleatorio
70 time_ms = abs(disk.next_block - disk.prev_block) *
71 (disk.delay_max - disk.delay_min) /
72 disk.numblocks +
73 disk.delay_min +
74 random() % (disk.delay_max - disk.delay_min) / 10;
75
76 #ifdef DEBUG_DISK
77 printf("DISK: [From block %d to block %d in %d ms]\n",
78 disk.prev_block, disk.next_block, time_ms);
79 #endif
80
81 // primeiro disparo, em nano-segundos,
82 disk.delay.it_value.tv_nsec = time_ms * 1000000;
83
84 // primeiro disparo, em segundos
85 disk.delay.it_value.tv_sec = time_ms / 1000;
86
87 // próximos disparos nao ocorrem (disparo único)
88 disk.delay.it_interval.tv_nsec = 0;
89 disk.delay.it_interval.tv_sec = 0;
90
91 // arma o timer
92 if (timer_settime(disk.timer, 0, &disk.delay, NULL) == -1)
93 {
94 perror("DISK:");
95 abort();
96 }
97 #ifdef DEBUG_DISK
98 printf("DISK: timer is set\n");
99 #endif
100 }
101
102 //----------------------------------------------------------------------
103
104 // trata o sinal do timer que simula o tempo de acesso ao disco
105 static void disk_sighandle(int)
106 {
107 #ifdef DEBUG_DISK
108 printf("DISK: signal received\n");
109 printf("fd: %d, block %d, size %d\n", disk.fd, disk.next_block,
110 disk.blocksize);
111 #endif
112
113 // verificar qual a operação pendente e realizá-la
114 switch (disk.status)
115 {
116 case DISK_STATUS_READ:
117 // faz a leitura previamente agendada
118 pread(disk.fd, disk.buffer, disk.blocksize,
119 disk.next_block * disk.blocksize);
120 break;
121
122 case DISK_STATUS_WRITE:
123 // faz a escrita previamente agendada
124 pwrite(disk.fd, disk.buffer, disk.blocksize,
125 disk.next_block * disk.blocksize);
126 break;
127
128 default:
129 // erro: estado desconhecido
130 perror("DISK: unknown disk state");
131 abort();
132 }
133
134 // guarda numero de bloco da ultima operação
135 disk.prev_block = disk.next_block;
136
137 // disco se torna ocioso novamente
138 disk.status = DISK_STATUS_IDLE;
139
140 // gera uma IRQ virtual para o "kernel" do usuário
141 raise(IRQ_DISK + SIGRTMIN);
142 }
143
144 //----------------------------------------------------------------------
145
146 // inicia o disco virtual contido em "disk_image"
147 // retorno: 0 (sucesso) ou -1 (erro)
148 static int disk_init(char *disk_image)
149 {
150 // o disco já foi iniciado ?
151 if (disk.status != DISK_STATUS_UNKNOWN)
152 return (ERROR);
153
154 // estado atual do disco
155 disk.status = DISK_STATUS_IDLE;
156 disk.next_block = disk.prev_block = 0;
157
158 // abre o arquivo no disco (leitura/escrita, sincrono)
159 disk.file = disk_image;
160 disk.fd = open(disk.file, O_RDWR | O_SYNC);
161 if (disk.fd < 0)
162 {
163 perror("DISK: ");
164 abort();
165 }
166
167 // define seu tamanho em blocos
168 disk.blocksize = DISK_BLOCK_SIZE;
169 disk.numblocks = lseek(disk.fd, 0, SEEK_END) / disk.blocksize;
170
171 // ajusta atrasos mínimo e máximo de acesso no disco
172 disk.delay_min = DISK_DELAY_MIN;
173 disk.delay_max = DISK_DELAY_MAX;
174
175 // associa sinal do timer interno ao handle apropriado
176 disk.signal.sa_handler = disk_sighandle;
177 sigemptyset(&disk.signal.sa_mask);
178 disk.signal.sa_flags = SA_NODEFER;
179 sigaction(DISK_SIGNAL, &disk.signal, 0);
180
181 // cria o timer que simula o tempo de acesso ao disco
182 disk.sigev.sigev_notify = SIGEV_SIGNAL;
183 disk.sigev.sigev_signo = DISK_SIGNAL;
184 if (timer_create(CLOCK_REALTIME, &disk.sigev, &disk.timer) == -1)
185 {
186 perror("DISK:");
187 abort();
188 }
189
190 #ifdef DEBUG_DISK
191 printf("DISK: initialized\n");
192 #endif
193
194 return (NOERROR);
195 }
196
197 //----------------------------------------------------------------------
198
199 // função que implementa a interface de acesso ao disco virtual
200 int hw_disk_cmd(int cmd, int block, void *buffer)
201 {
202 #ifdef DEBUG_DISK
203 printf("DISK: received command %d\n", cmd);
204 #endif
205
206 switch (cmd)
207 {
208 // inicia o disco
209 case DISK_CMD_INIT:
210 return (disk_init(buffer));
211
212 // solicita status do disco
213 case DISK_CMD_STATUS:
214 return (disk.status);
215
216 // solicita tamanho do disco
217 case DISK_CMD_DISKSIZE:
218 if (disk.status == DISK_STATUS_UNKNOWN)
219 return (ERROR);
220 return (disk.numblocks);
221
222 // solicita tamanho de bloco
223 case DISK_CMD_BLOCKSIZE:
224 if (disk.status == DISK_STATUS_UNKNOWN)
225 return (ERROR);
226 return (disk.blocksize);
227
228 // solicita atraso mínimo
229 case DISK_CMD_DELAYMIN:
230 if (disk.status == DISK_STATUS_UNKNOWN)
231 return (ERROR);
232 return (disk.delay_min);
233
234 // solicita atraso máximo
235 case DISK_CMD_DELAYMAX:
236 if (disk.status == DISK_STATUS_UNKNOWN)
237 return (ERROR);
238 return (disk.delay_max);
239
240 // solicita operação de leitura ou de escrita
241 case DISK_CMD_READ:
242 case DISK_CMD_WRITE:
243 if (disk.status != DISK_STATUS_IDLE)
244 return (ERROR);
245 if (!buffer)
246 return (ERROR);
247 if (block < 0 || block >= disk.numblocks)
248 return (ERROR);
249
250 // registra que ha uma operação pendente
251 disk.buffer = buffer;
252 disk.next_block = block;
253 if (cmd == DISK_CMD_READ)
254 disk.status = DISK_STATUS_READ;
255 else
256 disk.status = DISK_STATUS_WRITE;
257
258 // arma o timer que simula o atraso do disco
259 disk_settimer();
260
261 return (NOERROR);
262
263 default:
264 return (ERROR);
265 }
266 }
267
268 //----------------------------------------------------------------------