diff --git a/1.sh b/1.sh new file mode 100644 index 0000000..3c124d2 --- /dev/null +++ b/1.sh @@ -0,0 +1,33 @@ +# Start of file. This line should not cause errors +echo Command 000 +echo Command 001 +echo Command 002 +ehchhho Command 003 +echo Command 004 +echo Command 005 +echo Command 006 # Look, it's command 6. This shouldn't print +echo Command 007 +echo Command 008 +# Empty commands (like this one) and blank lines should not go in history + + + + + +/ls / +mkdir a +mkdir a/b +mkdir a/b/c +mkdir a/b/c/hello +cd a +cd b/c/hello +/ls +/wc /README.md +cd / +/wc README.md +echo --- +history +echo --- +history -t +exit +echo If you see this line, exit is not working. diff --git a/2.sh b/2.sh new file mode 100644 index 0000000..d07fb12 --- /dev/null +++ b/2.sh @@ -0,0 +1,433 @@ +# Make sure empty lines (including this one) are handled +# properly to pass this test. +# +# Bad/unknown commands **do** go into the history. + +thisisacommandthatprobablydoesntexistbutletstryitanywayhuh +echo Command 000 +echo Command 001 +echo Command 002 +echo Command 003 +echo Command 004 +echo Command 005 + +echo Command 006 + +echo Command 007 + +echo Command 008 + +echo Command 009 +echo Command 010 +echo Command 011 +echo Command 012 +echo Command 013 +echo Command 014 +echo Command 015 +echo Command 016 +echo Command 017 +echo Command 018 +echo Command 019 +echo Command 020 +echo Command 021 +echo Command 022 +echo Command 023 +echo Command 024 +echo Command 025 +echo Command 026 +echo Command 027 +echo Command 028 +echo Command 029 +echo Command 030 +echo Command 031 +echo Command 032 +echo Command 033 +echo Command 034 +echo Command 035 +echo Command 036 +echo Command 037 +echo Command 038 +echo Command 039 +echo Command 040 +echo Command 041 +echo Command 042 +echo Command 043 +echo Command 044 +echo Command 045 +echo Command 046 +echo Command 047 +echo Command 048 +echo Command 049 +echo Command 050 +echo Command 051 +echo Command 052 +echo Command 053 +echo Command 054 +echo Command 055 +echo Command 056 +echo Command 057 +echo Command 058 +echo Command 059 +echo Command 060 +echo Command 061 +echo Command 062 +echo Command 063 +echo Command 064 +echo Command 065 +echo Command 066 +echo Command 067 +echo Command 068 +echo Command 069 +echo Command 070 +echo Command 071 +echo Command 072 +echo Command 073 +echo Command 074 +echo Command 075 +echo Command 076 +echo Command 077 +echo Command 078 +echo Command 079 +echo Command 080 +echo Command 081 +echo Command 082 +echo Command 083 +echo Command 084 +echo Command 085 +echo Command 086 +echo Command 087 +echo Command 088 +echo Command 089 +echo Command 090 +echo Command 091 +echo Command 092 +echo Command 093 +echo Command 094 +echo Command 095 +echo Command 096 +echo Command 097 +echo Command 098 +echo Command 099 +echo Command 100 +echo Command 101 +echo Command 102 +echo Command 103 +echo Command 104 +echo Command 105 +echo Command 106 +echo Command 107 +echo Command 108 +echo Command 109 +echo Command 110 +echo Command 111 +echo Command 112 +echo Command 113 +echo Command 114 +echo Command 115 +echo Command 116 +echo Command 117 +echo Command 118 +echo Command 119 +echo Command 120 +echo Command 121 +echo Command 122 +echo Command 123 +echo Command 124 +echo Command 125 +echo Command 126 +echo Command 127 +echo Command 128 +echo Command 129 +echo Command 130 +echo Command 131 +echo Command 132 +echo Command 133 +echo Command 134 +echo Command 135 +echo Command 136 +echo Command 137 +echo Command 138 +echo Command 139 +echo Command 140 +echo Command 141 +echo Command 142 +echo Command 143 +echo Command 144 +echo Command 145 +echo Command 146 +echo Command 147 +echo Command 148 +echo Command 149 +echo Command 150 +echo Command 151 +echo Command 152 +echo Command 153 +echo Command 154 +echo Command 155 +echo Command 156 +echo Command 157 +echo Command 158 +echo Command 159 +echo Command 160 +echo Command 161 +echo Command 162 +echo Command 163 +echo Command 164 +echo Command 165 +echo Command 166 +echo Command 167 +echo Command 168 +echo Command 169 +echo Command 170 +echo Command 171 +echo Command 172 +echo Command 173 +echo Command 174 +echo Command 175 +echo Command 176 +echo Command 177 +echo Command 178 +echo Command 179 +echo Command 180 +echo Command 181 +echo Command 182 +echo Command 183 +echo Command 184 +echo Command 185 +echo Command 186 +echo Command 187 +echo Command 188 +echo Command 189 +echo Command 190 +echo Command 191 +echo Command 192 +echo Command 193 +echo Command 194 +echo Command 195 +echo Command 196 +echo Command 197 +echo Command 198 +echo Command 199 +echo Command 200 +echo Command 201 +echo Command 202 +echo Command 203 +echo Command 204 +echo Command 205 +echo Command 206 +echo Command 207 +echo Command 208 +echo Command 209 +echo Command 210 +echo Command 211 +echo Command 212 +echo Command 213 +echo Command 214 +echo Command 215 +echo Command 216 +echo Command 217 +echo Command 218 +echo Command 219 +echo Command 220 +echo Command 221 +echo Command 222 +echo Command 223 +echo Command 224 +echo Command 225 +echo Command 226 +echo Command 227 +echo Command 228 +echo Command 229 +echo Command 230 +echo Command 231 +echo Command 232 +echo Command 233 +echo Command 234 +echo Command 235 +echo Command 236 +echo Command 237 +echo Command 238 +echo Command 239 +echo Command 240 +echo Command 241 +echo Command 242 +echo Command 243 +echo Command 244 +echo Command 245 +echo Command 246 +echo Command 247 +echo Command 248 +echo Command 249 +echo Command 250 +echo Command 251 +echo Command 252 +echo Command 253 +echo Command 254 +echo Command 255 +echo Command 256 +echo Command 257 +echo Command 258 +echo Command 259 +echo Command 260 +echo Command 261 +echo Command 262 +echo Command 263 +echo Command 264 +echo Command 265 +echo Command 266 +echo Command 267 +echo Command 268 +echo Command 269 +echo Command 270 +echo Command 271 +echo Command 272 +echo Command 273 +echo Command 274 +echo Command 275 +echo Command 276 +echo Command 277 +echo Command 278 +echo Command 279 +echo Command 280 +echo Command 281 +echo Command 282 +echo Command 283 +echo Command 284 +echo Command 285 +echo Command 286 +echo Command 287 +echo Command 288 +echo Command 289 +echo Command 290 +echo Command 291 +echo Command 292 +echo Command 293 +echo Command 294 +echo Command 295 +echo Command 296 +echo Command 297 +echo Command 298 +echo Command 299 +echo Command 300 +echo Command 301 +echo Command 302 +echo Command 303 +echo Command 304 +echo Command 305 +echo Command 306 +echo Command 307 +echo Command 308 +echo Command 309 +echo Command 310 +echo Command 311 +echo Command 312 +echo Command 313 +echo Command 314 +echo Command 315 +echo Command 316 +echo Command 317 +echo Command 318 +echo Command 319 +echo Command 320 +echo Command 321 +echo Command 322 +echo Command 323 +echo Command 324 +echo Command 325 +echo Command 326 +echo Command 327 +echo Command 328 +echo Command 329 +echo Command 330 +echo Command 331 +echo Command 332 +echo Command 333 +echo Command 334 +echo Command 335 +echo Command 336 +echo Command 337 +echo Command 338 +echo Command 339 +echo Command 340 +echo Command 341 +echo Command 342 +echo Command 343 +echo Command 344 +echo Command 345 +echo Command 346 +echo Command 347 +echo Command 348 +echo Command 349 +echo Command 350 +echo Command 351 +echo Command 352 +echo Command 353 +echo Command 354 +echo Command 355 +echo Command 356 +echo Command 357 +echo Command 358 +echo Command 359 +echo Command 360 +echo Command 361 +echo Command 362 +echo Command 363 +echo Command 364 +echo Command 365 +echo Command 366 +echo Command 367 +echo Command 368 +echo Command 369 +echo Command 370 +echo Command 371 +echo Command 372 +echo Command 373 +echo Command 374 +echo Command 375 +echo Command 376 +echo Command 377 +echo Command 378 +echo Command 379 +echo Command 380 +echo Command 381 +echo Command 382 +echo Command 383 +echo Command 384 +echo Command 385 +echo Command 386 +echo Command 387 +echo Command 388 +echo Command 389 +echo Command 390 +echo Command 391 +echo Command 392 +echo Command 393 +echo Command 394 +echo Command 395 +echo Command 396 +echo Command 397 +echo Command 398 +echo Command 399 +echo Command 400 +echo Command 401 +echo Command 402 +echo Command 403 +echo Command 404 +echo Command 405 +echo Command 406 +pwd # May or may not exist... +echo Command 407 +echo Command 408 +echo Command 409 +echo Command 410 +echo Command 411 +echo Command 412 +echo Command 413 +echo Command 414 +echo Command 415 +echo Command 416 +echo Command 417 +echo Command 418 +echo Command 419 +echo ----- +history diff --git a/3.sh b/3.sh new file mode 100644 index 0000000..6c4e8d8 --- /dev/null +++ b/3.sh @@ -0,0 +1,430 @@ +!542 # Doesn't actually exist :-) +history +echo Command 002 +echo Command 003 +echo Command 004 +echo Command 005 +echo Command 006 +echo Command 007 +echo Command 008 +echo Command 009 +echo Command 010 +echo Command 011 +echo Command 012 +echo Command 013 +echo Command 014 +echo Command 015 +echo Command 016 +echo Command 017 +echo Command 018 +echo Command 019 +echo Command 020 +echo Command 021 +echo Command 022 +echo Command 023 +echo Command 024 +echo Command 025 +echo Command 026 +echo Command 027 +echo Command 028 +echo Command 029 +echo Command 030 +echo Command 031 +echo Command 032 +echo Command 033 +echo Command 034 +echo Command 035 +echo Command 036 +echo Command 037 +echo Command 038 +echo Command 039 +echo Command 040 +echo Command 041 +echo Command 042 +echo Command 043 +echo Command 044 +echo Command 045 +echo Command 046 +echo Command 047 +echo Command 048 +echo Command 049 +echo Command 050 +echo Command 051 +echo Command 052 +echo Command 053 +echo Command 054 +echo Command 055 +echo Command 056 +echo Command 057 +echo Command 058 +echo Command 059 +echo Command 060 +echo Command 061 +echo Command 062 +echo Command 063 +echo Command 064 +echo Command 065 +echo Command 066 +echo Command 067 +echo Command 068 +echo Command 069 +echo Command 070 +echo Command 071 +echo Command 072 +echo Command 073 +echo Command 074 +echo Command 075 +echo Command 076 +echo Command 077 +echo Command 078 +echo Command 079 +echo Command 080 +echo Command 081 +echo Command 082 +echo Command 083 +echo Command 084 +echo Command 085 +echo Command 086 +echo Command 087 +echo Command 088 +echo Command 089 +echo Command 090 +echo Command 091 +echo Command 092 +echo Command 093 +echo Command 094 +echo Command 095 +echo Command 096 +echo Command 097 +echo Command 098 +echo Command 099 +echo Command 100 +echo Command 101 +echo Command 102 +echo Command 103 +echo Command 104 +echo Command 105 +echo Command 106 +echo Command 107 +echo Command 108 +echo Command 109 +echo Command 110 +echo Command 111 +echo Command 112 +echo Command 113 +echo Command 114 +echo Command 115 +echo Command 116 +echo Command 117 +echo Command 118 +echo Command 119 +echo Command 120 +echo Command 121 +echo Command 122 +echo Command 123 +echo Command 124 +echo Command 125 +echo Command 126 +echo Command 127 +echo Command 128 +echo Command 129 +echo Command 130 +echo Command 131 +echo Command 132 +echo Command 133 +echo Command 134 +echo Command 135 +echo Command 136 +echo Command 137 +echo Command 138 +echo Command 139 +echo Command 140 +echo Command 141 +echo Command 142 +echo Command 143 +echo Command 144 +echo Command 145 +echo Command 146 +echo Command 147 +echo Command 148 +echo Command 149 +echo Command 150 +echo Command 151 +echo Command 152 +echo Command 153 +echo Command 154 +echo Command 155 +echo Command 156 +echo Command 157 +echo Command 158 +echo Command 159 +echo Command 160 +echo Command 161 +echo Command 162 +echo Command 163 +echo Command 164 +echo Command 165 +echo Command 166 +echo Command 167 +echo Command 168 +echo Command 169 +echo Command 170 +echo Command 171 +echo Command 172 +echo Command 173 +echo Command 174 +echo Command 175 +echo Command 176 +echo Command 177 +echo Command 178 +echo Command 179 +echo Command 180 +echo Command 181 +echo Command 182 +echo Command 183 +echo Command 184 +echo Command 185 +echo Command 186 +echo Command 187 +echo Command 188 +echo Command 189 +echo Command 190 +echo Command 191 +echo Command 192 +echo Command 193 +echo Command 194 +echo Command 195 +echo Command 196 +echo Command 197 +echo Command 198 +echo Command 199 +echo Command 200 +echo Command 201 +echo Command 202 +echo Command 203 +echo Command 204 +echo Command 205 +echo Command 206 +echo Command 207 +echo Command 208 +echo Command 209 +echo Command 210 +echo Command 211 +echo Command 212 +echo Command 213 +echo Command 214 +echo Command 215 +echo Command 216 +echo Command 217 +echo Command 218 +echo Command 219 +echo Command 220 +echo Command 221 +echo Command 222 +echo Command 223 +echo Command 224 +echo Command 225 +echo Command 226 +echo Command 227 +echo Command 228 +echo Command 229 +echo Command 230 +echo Command 231 +echo Command 232 +echo Command 233 +echo Command 234 +echo Command 235 +echo Command 236 +echo Command 237 +echo Command 238 +echo Command 239 +echo Command 240 +echo Command 241 +echo Command 242 +echo Command 243 +echo Command 244 +echo Command 245 +echo Command 246 +echo Command 247 +echo Command 248 +echo Command 249 +echo Command 250 +echo Command 251 +echo Command 252 +echo Command 253 +echo Command 254 +echo Command 255 +echo Command 256 +echo Command 257 +echo Command 258 +echo Command 259 +echo Command 260 +echo Command 261 +echo Command 262 +echo Command 263 +echo Command 264 +echo Command 265 +echo Command 266 +echo Command 267 +echo Command 268 +echo Command 269 +echo Command 270 +echo Command 271 +echo Command 272 +echo Command 273 +echo Command 274 +echo Command 275 +echo Command 276 +echo Command 277 +echo Command 278 +echo Command 279 +echo Command 280 +echo Command 281 +echo Command 282 +echo Command 283 +echo Command 284 +echo Command 285 +echo Command 286 +echo Command 287 +echo Command 288 +echo Command 289 +echo Command 290 +echo Command 291 +echo Command 292 +echo Command 293 +echo Command 294 +echo Command 295 +echo Command 296 +echo Command 297 +echo Command 298 +echo Command 299 +echo Command 300 +echo Command 301 +echo Command 302 +echo Command 303 +echo Command 304 +echo Command 305 +echo Command 306 +echo Command 307 +echo Command 308 +echo Command 309 +echo Command 310 +echo Command 311 +echo Command 312 +echo Command 313 +echo Command 314 +echo Command 315 +echo Command 316 +echo Command 317 +echo Command 318 +echo Command 319 +echo Command 320 +echo Command 321 +echo Command 322 +echo Command 323 +echo Command 324 +echo Command 325 +echo Command 326 +echo Command 327 +echo Command 328 +echo Command 329 +echo Command 330 +echo Command 331 +echo Command 332 +echo Command 333 +echo Command 334 +echo Command 335 +echo Command 336 +echo Command 337 +echo Command 338 +echo Command 339 +echo Command 340 +echo Command 341 +echo Command 342 +echo Command 343 +echo Command 344 +echo Command 345 +echo Command 346 +echo Command 347 +echo Command 348 +echo Command 349 +echo Command 350 +echo Command 351 +echo Command 352 +echo Command 353 +echo Command 354 +echo Command 355 +echo Command 356 +echo Command 357 +echo Command 358 +echo Command 359 +echo Command 360 +echo Command 361 +echo Command 362 +echo Command 363 +echo Command 364 +echo Command 365 +echo Command 366 +echo Command 367 +echo Command 368 +echo Command 369 +echo Command 370 +echo Command 371 +echo Command 372 +echo Command 373 +echo Command 374 +echo Command 375 +echo Command 376 +echo Command 377 +echo Command 378 +echo Command 379 +echo Command 380 +echo Command 381 +echo Command 382 +echo Command 383 +echo Command 384 +echo Command 385 +echo Command 386 +echo Command 387 +echo Command 388 +echo Command 389 +echo Command 390 +echo Command 391 +echo Command 392 +echo Command 393 +echo Command 394 +echo Command 395 +echo Command 396 +echo Command 397 +echo Command 398 +echo Command 399 +echo Command 400 +echo Command 401 +echo Command 402 +echo Command 403 +echo Command 404 +echo Command 405 +echo Command 406 +echo Command 407 +echo Command 408 +echo Command 409 +echo Command 410 +echo Command 411 +echo Command 412 +echo Command 413 +echo Command 414 +echo Command 415 +echo Command 416 +echo Command 417 +echo Command 418 +echo Command 419 +echo Command 420 +echo ----- +history +echo ----- +!420 # Works! +!325 # Works! +!325 # Shouldn't work the 2nd time -- gets popped off history. +!401 # Works! +!23 # Does not exist anymore +!999 # Hasn't happened yet diff --git a/4.sh b/4.sh new file mode 100644 index 0000000..22e012c --- /dev/null +++ b/4.sh @@ -0,0 +1,432 @@ +echo Command 000 +echo Command 001 +cat /README.md +echo Command 003 +echo Command 004 +echo Command 005 +echo Command 006 +echo Command 007 +echo Command 008 +echo Command 009 +echo Command 010 +echo Command 011 +echo Command 012 +echo Command 013 +echo Command 014 +echo Command 015 +echo Command 016 +echo Command 017 +echo Command 018 +echo Command 019 +echo Command 020 +echo Command 021 +echo Command 022 +echo Command 023 +echo Command 024 +echo Command 025 +echo Command 026 +echo Command 027 +echo Command 028 +echo Command 029 +echo Command 030 +echo Command 031 +echo Command 032 +echo Command 033 +echo Command 034 +echo Command 035 +echo Command 036 +echo Command 037 +echo Command 038 +echo Command 039 +echo Command 040 +echo Command 041 +echo Command 042 +echo Command 043 +echo Command 044 +echo Command 045 +echo Command 046 +echo Command 047 +echo Command 048 +echo Command 049 +echo Command 050 +echo Command 051 +echo Command 052 +echo Command 053 +echo Command 054 +echo Command 055 +echo Command 056 +echo Command 057 +echo Command 058 +echo Command 059 +echo Command 060 +echo Command 061 +echo Command 062 +echo Command 063 +echo Command 064 +echo Command 065 +echo Command 066 +echo Command 067 +echo Command 068 +echo Command 069 +echo Command 070 +echo Command 071 +echo Command 072 +echo Command 073 +echo Command 074 +echo Command 075 +echo Command 076 +echo Command 077 +echo Command 078 +echo Command 079 +echo Command 080 +echo Command 081 +echo Command 082 +echo Command 083 +echo Command 084 +echo Command 085 +echo Command 086 +echo Command 087 +echo Command 088 +echo Command 089 +echo Command 090 +echo Command 091 +echo Command 092 +echo Command 093 +echo Command 094 +echo Command 095 +echo Command 096 +echo Command 097 +echo Command 098 +echo Command 099 +echo Command 100 +echo Command 101 +echo Command 102 +echo Command 103 +echo Command 104 +echo Command 105 +echo Command 106 +echo Command 107 +echo Command 108 +echo Command 109 +echo Command 110 +echo Command 111 +echo Command 112 +echo Command 113 +echo Command 114 +echo Command 115 +echo Command 116 +echo Command 117 +echo Command 118 +echo Command 119 +echo Command 120 +echo Command 121 +echo Command 122 +echo Command 123 +echo Command 124 +echo Command 125 +echo Command 126 +echo Command 127 +echo Command 128 +echo Command 129 +echo Command 130 +# Potato +echo Command 131 +echo Command 132 +echo Command 133 +echo Command 134 +echo Command 135 +echo Command 136 +echo Command 137 +echo Command 138 +echo Command 139 +echo Command 140 +echo Command 141 +echo Command 142 +echo Command 143 +echo Command 144 +echo Command 145 +echo Command 146 +echo Command 147 +echo Command 148 +echo Command 149 +echo Command 150 +echo Command 151 +echo Command 152 +echo Command 153 +echo Command 154 +echo Command 155 +echo Command 156 +echo Command 157 +echo Command 158 +echo Command 159 +echo Command 160 +echo Command 161 +echo Command 162 +echo Command 163 +echo Command 164 +echo Command 165 +echo Command 166 +echo Command 167 +echo Command 168 +echo Command 169 +echo Command 170 +echo Command 171 +echo Command 172 +echo Command 173 +echo Command 174 +echo Command 175 +echo Command 176 +echo Command 177 +echo Command 178 +echo Command 179 +echo Command 180 +echo Command 181 +echo Command 182 +echo Command 183 +echo Command 184 +echo Command 185 +echo Command 186 +echo Command 187 +echo Command 188 +echo Command 189 +echo Command 190 +echo Command 191 +echo Command 192 +echo Command 193 +echo Command 194 +echo Command 195 +echo Command 196 +echo Command 197 +echo Command 198 +echo Command 199 +echo Command 200 +echo Command 201 +echo Command 202 +echo Command 203 +echo Command 204 +echo Command 205 +echo Command 206 +echo Command 207 +echo Command 208 +echo Command 209 +echo Command 210 +echo Command 211 +echo Command 212 +echo Command 213 +echo Command 214 +echo Command 215 +echo Command 216 +echo Command 217 +echo Command 218 +echo Command 219 +echo Command 220 +echo Command 221 +echo Command 222 +echo Command 223 +echo Command 224 +echo Command 225 +echo Command 226 +echo Command 227 +echo Command 228 +echo Command 229 +echo Command 230 +echo Command 231 +echo Command 232 +echo Command 233 +echo Command 234 +echo Command 235 +echo Command 236 +echo Command 237 +echo Command 238 +echo Command 239 +echo Command 240 +echo Command 241 +echo Command 242 +echo Command 243 +echo Command 244 +echo Command 245 +echo Command 246 +echo Command 247 +echo Command 248 +echo Command 249 +echo Command 250 +echo Command 251 +echo Command 252 +echo Command 253 +echo Command 254 +echo Command 255 +echo Command 256 +echo Command 257 +echo Command 258 +echo Command 259 +echo Command 260 +echo Command 261 +echo Command 262 +echo Command 263 +echo Command 264 +echo Command 265 +echo Command 266 +echo Command 267 +echo Command 268 +echo Command 269 +echo Command 270 +echo Command 271 +echo Command 272 +echo Command 273 +echo Command 274 +echo Command 275 +echo Command 276 +echo Command 277 +echo Command 278 +echo Command 279 +echo Command 280 +echo Command 281 +echo Command 282 +echo Command 283 +echo Command 284 +echo Command 285 +echo Command 286 +echo Command 287 +echo Command 288 +echo Command 289 +echo Command 290 +echo Command 291 +echo Command 292 +echo Command 293 +echo Command 294 +echo Command 295 +echo Command 296 +echo Command 297 +echo Command 298 +echo Command 299 +echo Command 300 +echo Command 301 +echo Command 302 +echo Command 303 +echo Command 304 +echo Command 305 +echo Command 306 +echo Command 307 +echo Command 308 +echo Command 309 +echo Command 310 +echo Command 311 +echo Command 312 +echo Command 313 +echo Command 314 +echo Command 315 +echo Command 316 +echo Command 317 +echo Command 318 +echo Command 319 +echo Command 320 +echo Command 321 +echo Command 322 +echo Command 323 +echo Command 324 +echo Command 325 +echo Command 326 +echo Command 327 +echo Command 328 +echo Command 329 +echo Command 330 +echo Command 331 +echo Command 332 +echo Command 333 +echo Command 334 +echo Command 335 +echo Command 336 +echo Command 337 +echo Command 338 +echo Command 339 +echo Command 340 +echo Command 341 +echo Command 342 +echo Command 343 +echo Command 344 +echo Command 345 +echo Command 346 +echo Command 347 +echo Command 348 +echo Command 349 +echo Command 350 +echo Command 351 +echo Command 352 +echo Command 353 +echo Command 354 +echo Command 355 +echo Command 356 +echo Command 357 +echo Command 358 +echo Command 359 +echo Command 360 +echo Command 361 +echo Command 362 +echo Command 363 +echo Command 364 +echo Command 365 +echo Command 366 +echo Command 367 +echo Command 368 +echo Command 369 +echo Command 370 +echo Command 371 +ls / +echo Command 373 +echo Command 374 +echo Command 375 +echo Command 376 +echo Command 377 +echo Command 378 +echo Command 379 +echo Command 380 +echo Command 381 +echo Command 383 +echo Command 384 +mkdir /a +ls /a +echo Command 385 +echo Command 386 +echo Command 387 +echo Command 388 +mysterycommand +echo Command 389 +echo Command 390 +echo Command 391 +echo Command 392 +grep Potato$ /README.md +grep Potato$ /4.sh +echo Command 394 +echo Command 395 +echo Command 396 +echo Command 397 +echo Command 398 +echo Command 399 +echo Command 400 +echo Command 401 +echo Command 402 +echo Command 403 +echo Command 404 +echo Command 405 +wc /README.md +echo Command 407 +echo Command 408 +echo Command 409 +echo Command 410 +echo Command 411 +echo Command 412 +echo Command 413 +echo Command 414 +echo Command 415 +echo Command 416 +echo Command 417 +echo Command 418 +echo Command 419 +echo ----- +!ls # ls an empty dir +!m # run mysterycommand and fail since it doesn't exist +!grepfzw # no command had this prefix +!grep # find 'Potato' in this script (*not* in README.md) +!w # word count README.md +!gr # find 'Potato' again +!e # last echo command (-----) diff --git a/Makefile b/Makefile index cf031b0..430d995 100644 --- a/Makefile +++ b/Makefile @@ -83,11 +83,14 @@ endif LDFLAGS = -z max-page-size=4096 -$K/kernel: $(OBJS) $K/kernel.ld +$K/kernel: $(OBJS) $K/kernel.ld $K/strace.h $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $(OBJDUMP) -S $K/kernel > $K/kernel.asm $(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym +$K/strace.h: generate-traces.sh $U/user.h + ./generate-traces.sh > $K/strace.h + $K/%.o: $K/%.S $(CC) -g -c -o $@ $< @@ -110,7 +113,7 @@ $U/usys.o : $U/usys.S $U/_forktest: $U/forktest.o $(ULIB) # forktest has less library code linked in - needs to be small # in order to be able to max out the proc table. - $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o + $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o $U/umalloc.o $U/printf.o $(OBJDUMP) -S $U/_forktest > $U/forktest.asm mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h @@ -123,18 +126,43 @@ mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h .PRECIOUS: %.o UPROGS=\ + $U/_about\ + $U/_benchmark\ + $U/_broken\ $U/_cat\ + $U/_catlines\ + $U/_compress\ + $U/_compress_test\ + $U/_clear\ + $U/_clock\ $U/_echo\ + $U/_fnr\ $U/_forktest\ + $U/_freemem\ $U/_grep\ $U/_init\ $U/_kill\ + $U/_leetify\ $U/_ln\ $U/_ls\ $U/_mkdir\ + $U/_mmap\ + $U/_mmaptest\ + $U/_mt70\ + $U/_mt80\ + $U/_mt90\ + $U/_nice\ + $U/_opt_cat\ + $U/_pwd\ $U/_rm\ + $U/_reboot\ $U/_sh\ + $U/_shell\ + $U/_shutdown\ + $U/_spinner\ + $U/_strace\ $U/_stressfs\ + $U/_tolower\ $U/_usertests\ $U/_grind\ $U/_wc\ @@ -143,8 +171,8 @@ UPROGS=\ $U/_forphan\ $U/_dorphan\ -fs.img: mkfs/mkfs README.md $(UPROGS) - mkfs/mkfs fs.img README.md $(UPROGS) +fs.img: mkfs/mkfs README.md tm.txt $(UPROGS) 1.sh 2.sh 3.sh 4.sh test.sh + mkfs/mkfs fs.img README.md tm.txt $(UPROGS) 1.sh 2.sh 3.sh 4.sh test.sh -include kernel/*.d user/*.d diff --git a/README.md b/README.md index 85f6164..8981ad2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,152 @@ # FogOS -Fall 2025 Edition - ![FogOS](docs/fogos.gif) +A 64-bit RISC-V operating system, built on top of [MIT's xv6](https://pdos.csail.mit.edu/6.1810/). + +xv6 provides the base: a multiprocessor kernel, virtual memory, a crash-safe logging filesystem, and a small set of Unix utilities. Everything documented below is what I built on top of it — nine new system calls, a priority scheduler, physical page reference counting, anonymous memory mapping, a build-time syscall tracer, a shell written from scratch, and a rewritten heap allocator. + +It runs on bare metal under QEMU. No host OS underneath, no libc. + +--- + +## Quick start + +Requires a `riscv64` GNU toolchain and `qemu-system-riscv64`. + +```sh +make qemu # build and boot FogOS in QEMU + # (exit with Ctrl-A then X) +``` + +Once booted: + +```sh +$ shell # my shell +$ strace ls # trace every syscall a program makes +$ benchmark wc README.md # time a command, count its syscalls +$ freemem # free memory, in KB +$ mt90 # allocator stress test + heap map +$ compress_test # RLE round-trip test suite +``` + +--- + +## Kernel + +### Auto-generated syscall tracing + +`strace_on()` sets a per-process flag; every subsequent syscall prints its name, decoded arguments, and return value. + +The decoder isn't hand-written. `generate-traces.sh` parses `user/usys.pl` and `user/user.h` at build time and emits `kernel/strace.h`, deriving argument names and printf format specifiers directly from the C prototypes. A `/*str*/` comment annotation marks pointers that should render as strings rather than addresses: + +```c +int mkdir(const char* /*str*/ path); → [3|sh] mkdir(path = "a/b") = 0 +``` + +Add a syscall to `usys.pl` and it becomes traceable with no extra work. The Makefile wires this in as a real build dependency, so the tracer can't drift out of sync with the syscall table. + +### Priority scheduler + +Stock xv6 round-robins flatly over the process table. My scheduler finds the highest priority among `RUNNABLE` processes, then descends through priority levels, giving higher-priority processes proportionally more turns. Priority is exposed through a `nice` syscall (`0`–`3`, `0` highest, stored as `priority = 3 - nice`) and inherited across `fork`. Run `spinner` under different `nice` values to watch it take effect. + +### Physical page reference counting + +`kalloc.c` keeps a refcount per physical page, indexed by `(pa - KERNBASE) / PGSIZE`. `kalloc` sets it to 1 inside the freelist lock; `kfree` decrements and only returns a page to the freelist when the last reference drops, panicking on underflow. This is the substrate copy-on-write fork needs, and it's what makes shared mappings across `fork` safe from double-frees. `kfreepages()` walks the freelist to report what's left. + +### Anonymous mmap + +Each process gets a region growing *downward* from below the user stack (`TRAPFRAME - USERSTACK * PGSIZE`), tracked by `p->mmap` and `p->mmap_pages`. Each `mmap()` call allocates a page, zeroes it, and maps it `PTE_R | PTE_W | PTE_U` at the next address down. + +`fork` re-maps the child's mapped pages onto the same physical frames and bumps their refcounts, so the region is shared between parent and child rather than copied. `freeproc` unmaps it on exit. Exercised by `mmap` and `mmaptest`. + +### getcwd + +xv6 has no notion of an absolute path for the working directory — the kernel holds only an inode pointer. Mine walks *up* the tree from the cwd inode via `..`, scanning each parent's directory entries to recover the name of the child it came from, building the path backwards from the end of the buffer and then sliding it into place. `diriname()` does the per-directory reverse lookup. + +### Shebang execution + +`kexec` peeks at the first two bytes of the target; on `#!` it reads the interpreter path, rewrites `argv` to put the interpreter first and the script second, releases its inode and ends its log transaction, then re-enters `kexec`. So `#!/crash` at the top of a script works the way it does on Unix. + +### wait2 + +Extends `wait` to also copy out the exiting child's cumulative syscall count, tracked per-process in `syscall()`. This is the primitive `benchmark` is built on. + +### Wall-clock time and power control + +`clock()` reads the goldfish-RTC MMIO registers for nanosecond-resolution time. `shutdown()` and `reboot()` write magic values to QEMU's `virt` test device to actually halt or reset the machine. `freemem()` reports free memory in KB. + +--- + +## User space + +### shell + +Written from scratch, alongside the xv6 `sh`: + +- prompt showing exit status, command number, and absolute working directory: `[0]-[12]-[/a/b/c]$` +- 100-entry ring-buffer history; `history` and `history -t` (per-command wall-clock duration in ms, measured with `clock()`) +- history expansion: `!!` for the last command, `!5` by number, `!prefix` for the most recent match +- comment stripping, blank-line handling, `cd` and `exit` builtins +- script mode — `shell foo.sh` runs non-interactively with prompts suppressed +- a hand-written tokenizer (`strspn`/`strcspn`/`next_token`), since there's no libc to lean on + +`1.sh` through `4.sh` are the conformance scripts it's tested against, covering the edge cases: bad commands still enter history, empty lines don't, comments are stripped before execution. + +I also extended the original `sh` with script-file arguments, `#` comment handling, and a `/`-prefix fallback so bare command names resolve. + +### Heap allocator + +`user/umalloc.c`, a ~500-line rewrite. Doubly-linked block list over page-granular `sbrk` regions: + +- three selectable free-space-management policies — first fit, best fit, worst fit, via `malloc_setfsm` +- the free bit packed into the low bit of the size field, which is free real estate because every size is 16-byte aligned +- block splitting with a minimum-split threshold, and coalescing on free +- `calloc`, `realloc`, plus named allocations (`malloc_name`) and `malloc_print` to dump an annotated map of the heap + +`mt70`, `mt80`, and `mt90` stress it and visualize the resulting layout. + +### Compression + +`compress -c` / `-d` implements run-length encoding over file *and* stream modes, so it composes with pipes and redirection under `sh`. Format is a stream of `[count][value]` pairs with runs split at 255. See [docs/compress.md](docs/compress.md). `compress_test` round-trips empty files, long runs, and full 0x00–0xFF binary data. + +### Utilities + +`about` · `benchmark` (times a command and counts its syscalls) · `catlines` · `clear` · `clock` · `fnr` (stream find-and-replace) · `freemem` · `leetify` · `mmap` · `nice` · `opt_cat` (buffered `cat`, a direct throughput comparison against the naive one) · `pwd` · `reboot` · `shutdown` · `spinner` · `strace` · `tolower` + +Plus `getline` and `fgets` added to the user library. + +### C debugging exercises + +`broken.c` is a set of deliberately buggy C functions — read-only string literals, dangling pointers, uninitialized reads, format overflows, off-by-ones — each fixed with a written explanation of the underlying fault. + +--- + +## Layout + +``` +kernel/ kernel source; strace.h is generated at build time +user/ user programs and the user-space C library +mkfs/ builds the initial filesystem image +docs/ per-feature documentation +*.sh shell conformance scripts, baked into fs.img +test-xv6.py scripted QEMU test driver +``` + +Scripted testing, without typing at the console: + +```sh +./test-xv6.py usertests # full user-space test suite +./test-xv6.py -q usertests # quick pass +./test-xv6.py crash # crash-recovery tests +``` + +## System calls added + +`shutdown` · `reboot` · `clock` · `strace_on` · `wait2` · `nice` · `getcwd` · `freemem` · `mmap` + +--- + +## License +MIT — see [LICENSE](LICENSE). Built on xv6-riscv, © 2006–2022 Frans Kaashoek, Robert Morris, and Russ Cox; see [xv6-LICENSE](xv6-LICENSE) and [xv6-README](xv6-README). diff --git a/docs/compress.md b/docs/compress.md new file mode 100644 index 0000000..c2cae4d --- /dev/null +++ b/docs/compress.md @@ -0,0 +1,50 @@ +# compress — Run-Length Encode / Decode files + +## Usage + +`compress` provides simple Run-Length Encoding (RLE) for any file. It can read from **stdin** and write to **stdout** if file paths are omitted. + +* `-c` : **Compress** input → RLE output +* `-d` : **Decompress** RLE input → original bytes +* `-h` : Show help/usage + +> RLE format: a stream of 2-byte pairs `[count][value]` with `count` in 1–255. Long runs are split. Random data may grow in size. + +### Examples + +```sh +# File → file +$ compress -c README.md README.rle +$ compress -d README.rle README.out + +# Stream mode (stdin/stdout + pipes/redirection) +$ cat README.md | compress -c > foo.rle +$ cat foo.rle | compress -d > out.txt +``` + +--- + +## Testing + +An automated test runner `/compress_test` is included. It creates sample files, runs compress/decompress, and checks round-trip equality. + +### To run tests + +1. Boot the OS and open the shell. +2. Run: + + ``` + /compress_test + ``` +3. You should see `PASS`/`FAIL` lines and a summary, e.g.: + + ``` + [PASS] small text + [PASS] empty file + [PASS] long run 1000x'A' + [PASS] binary 0x00..0xFF + + All tests PASSED + ``` + +If a test fails, the message indicates which stage (compress/decompress/compare) had an issue. diff --git a/generate-traces.sh b/generate-traces.sh new file mode 100755 index 0000000..2207f29 --- /dev/null +++ b/generate-traces.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# This script is designed to be run on a Linux host (i.e., not in xv6) +# You should probably run this script to see what it does first. To do that, you +# may need to make it executable: +# +# chmod +x generate-traces.sh +# +# And then run it from the *root* of your OS directory. +# +# +# For each user space system call stub, this script will print out information +# about its name, arguments, and return values. +# +# You can add information to the stubs that this script will extract. For +# example, instead of: +# +# int write(int, const void *, int); +# +# You can add argument names: +# +# int write(int fd, const void *buf, int count); +# +# And this script will extract them. By default, pointers are assumed to be +# addresses, but if you want the script to treat them like strings you can add a +# bit of metadata with a C comment: +# +# int mkdir(const char* /*str*/ path); +# +# Here's a few examples from a user/user.h file: +# int exit(int status) __attribute__((noreturn)); +# int wait(int *status); +# int pipe(int *pipefd); +# int mkdir(const char* /*str*/ path); + + +# The following code extracts function information from user/user.h based on the +# system calls found in user/usys.pl. + +# Retrieves the type of the argument provided as a printf format specifier +get_type() { + case "${1}" in + *'/*str*/'*) echo "%s" ;; + *"*"*) echo "%p" ;; + *) echo "%ld" ;; # default to int + esac +} + +# Determines which function to call for a given format specifier. +get_conversion() { + case "${1}" in + '%p') echo "read_ptr(${2})" ;; + '%s') echo "read_str(${2})" ;; + '%ld') echo "read_int(${2})" ;; + esac +} + +# Inspects the user/user.h file to extract system call information +syscall_output=$( +for i in $(sed -n 's/entry("\(.*\)");/\1/p' user/usys.pl); do + return_value=$(sed -nE \ + "s:\s*([A-Za-z_][A-Za-z0-9_\* ]+)\s+${i}.*:\1:p;" user/user.h) + args=$(sed 's/const//g; s/void//g; s/struct//g; s/((noreturn))//g' user/user.h \ + | sed -nE "s:.*\s${i}\((.*)\).*:\1:p") + + # Extract argument names and types for printing + arg_list="" + arg_convs="" + arg_count=0 + export IFS="," + for arg in $args; do + arg_name=$(echo "${arg}" | sed -nE 's:.*\b([A-Za-z0-9_]+)\s*:\1:p') + arg_type=$(get_type "${arg}") + arg_conv=$(get_conversion "${arg_type}" "${arg_count}") + arg_list="${arg_list}${arg_name} = ${arg_type}, " + arg_convs="${arg_convs}${arg_conv}, " + (( arg_count++ )) + done + + # Remove extra ', ' + [[ "${arg_count}" -gt 0 ]] && arg_list="${arg_list::-2}" + + # cast the return type, if necessary + return_type="$(get_type "${return_value}")" + cast_ret="" + if [[ "${return_type}" == "%p" ]]; then + cast_ret="(void *)" + fi + + # Print out the C code to display system call information. You will probably + # need to modify this so it can be used to conditionally print out the + # appropriate info. + echo "case SYS_${i}:" + echo " printf(\"[%d|%s] ${i}(${arg_list}) = $(get_type "${return_value}")\\n\", p->pid, p->name, ${arg_convs} ${cast_ret} ret_val);" + echo " break;" +done +) + +# The information we want has been extracted and stored in $syscall_output. +# Using this information, we'll generate C code below. When we run 'cat <sz = sz; p->trapframe->epc = elf.entry; // initial program counter = main p->trapframe->sp = sp; // initial stack pointer + + p->mmap = TRAPFRAME - (USERSTACK * PGSIZE); + p->mmap_pages = 0; + proc_freepagetable(oldpagetable, oldsz); return argc; // this ends up in a0, the first argument to main(argc, argv) diff --git a/kernel/fs.c b/kernel/fs.c index 9274678..19ecc66 100644 --- a/kernel/fs.c +++ b/kernel/fs.c @@ -718,3 +718,96 @@ nameiparent(char *path, char *name) { return namex(path, 1, name); } + +// Get the name of a particular inode in a directory. +int +diriname(struct inode *dp, ushort inum, char name[DIRSIZ], ushort *iparent) +{ + ilock(dp); + + if (dp->type != T_DIR) + panic("diriname not DIR"); + + struct dirent de; + if (iparent) { + // Read second entry ('..', aka parent) + readi(dp, 0, (uint64) &de, sizeof(struct dirent), sizeof(struct dirent)); + *iparent = de.inum; + } + + for (uint off = 0; off < dp->size; off += sizeof(de)){ + if (readi(dp, 0, (uint64) &de, off, sizeof(de)) != sizeof(de)) + panic("diriname read"); + if (de.inum == 0) + continue; + if (de.inum == inum && name) { + // Found the inode + strncpy(name, de.name, DIRSIZ); + iunlockput(dp); + return strlen(name); + } + } + + iunlockput(dp); + return -1; +} + +int +getcwd(char *buf, uint size) +{ + struct inode *ip = myproc()->cwd; + ilock(ip); + + if(ip->type != T_DIR) + panic("getcwd not DIR"); + + if (!buf || size <= 1) { + iunlock(ip); + return -1; + } + + if (ip->inum == 1 && size > 1) { + buf[0] = '/'; + buf[1] = '\0'; + iunlock(ip); + return 0; + } + + // We copy the path to the *end* of the buffer instead of the beginning, then + // work backwards. Start by null terminating the string. + buf[size - 1] = '\0'; + uint bufp = size - 2; + + struct inode *i = iget(ip->dev, ip->inum); + + ushort iparent; + ushort last_parent = ip->inum; + ushort icurrent = ip->inum; + char name[DIRSIZ]; + iunlock(ip); + + // Starting with the CWD inode, go up one level (..), read that directory, and + // search for the CWD inode in it. Repeat the process until we reach /. + while (icurrent != 1 && diriname(i, icurrent, name, &iparent) != -1) { + if (icurrent != last_parent) { + // We found another path element. Add it to the string. + int len = strlen(name); + if (len < bufp) { + bufp -= len; + memmove(buf + bufp, name, len); + buf[--bufp] = '/'; + } else { + // No more space to store the path. + break; + } + } + i = iget(ip->dev, iparent); + icurrent = last_parent; + last_parent = iparent; + } + + // If we didn't use the entire buffer, slide the string over to the beginning. + memmove(buf, buf + bufp, size - bufp); + + return 0; +} diff --git a/kernel/kalloc.c b/kernel/kalloc.c index 0699e7e..da58887 100644 --- a/kernel/kalloc.c +++ b/kernel/kalloc.c @@ -9,6 +9,16 @@ #include "riscv.h" #include "defs.h" +// Store a reference count for each physical page +static int ref_count[(PHYSTOP - KERNBASE) / PGSIZE]; + +// Convert physical address to index in reference count array +static inline int +pa2idx(uint64 pa) +{ + return (pa - KERNBASE) / PGSIZE; +} + void freerange(void *pa_start, void *pa_end); extern char end[]; // first address after kernel. @@ -23,6 +33,15 @@ struct { struct run *freelist; } kmem; +void +incref(uint64 pa) +{ + int idx = pa2idx(pa); + acquire(&kmem.lock); + ref_count[idx]++; + release(&kmem.lock); +} + void kinit() { @@ -34,7 +53,7 @@ void freerange(void *pa_start, void *pa_end) { char *p; - p = (char*)PGROUNDUP((uint64)pa_start); + p = (char*)PGROUNDUP((uint64)pa_start); for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) kfree(p); } @@ -47,18 +66,35 @@ void kfree(void *pa) { struct run *r; + uint64 addr = (uint64)pa; - if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) + if((addr % PGSIZE) != 0 || addr < (uint64)end || addr >= PHYSTOP) panic("kfree"); + int idx = pa2idx(addr); + + acquire(&kmem.lock); + + if(ref_count[idx] < 0) + panic("kfree: ref_count underflow"); + + if(ref_count[idx] > 0){ + // normal case: page was allocated by kalloc + ref_count[idx]--; + if(ref_count[idx] > 0){ + // still in use somewhere else – don't free yet + release(&kmem.lock); + return; + } + } + // Fill with junk to catch dangling refs. memset(pa, 1, PGSIZE); r = (struct run*)pa; - - acquire(&kmem.lock); r->next = kmem.freelist; kmem.freelist = r; + release(&kmem.lock); } @@ -72,11 +108,31 @@ kalloc(void) acquire(&kmem.lock); r = kmem.freelist; - if(r) + if(r){ kmem.freelist = r->next; + ref_count[pa2idx((uint64)r)] = 1; // << MUST be inside lock + } release(&kmem.lock); if(r) - memset((char*)r, 5, PGSIZE); // fill with junk + memset((char*)r, 5, PGSIZE); + return (void*)r; +} + +uint64 +kfreepages(void) +{ + uint64 num_pages = 0; + struct run *r; + + acquire(&kmem.lock); + r = kmem.freelist; + while (r) { + num_pages++; + r = r->next; + } + release(&kmem.lock); + + return num_pages; } diff --git a/kernel/memlayout.h b/kernel/memlayout.h index 9bc9424..3848ff4 100644 --- a/kernel/memlayout.h +++ b/kernel/memlayout.h @@ -25,6 +25,12 @@ #define VIRTIO0 0x10001000 #define VIRTIO0_IRQ 1 +// goldfish_rtc clock +#define GOLDFISH 0x101000 + +// qemu "test" device for signaling exit/status +#define VIRT_TEST 0x100000 + // qemu puts platform-level interrupt controller (PLIC) here. #define PLIC 0x0c000000L #define PLIC_PRIORITY (PLIC + 0x0) diff --git a/kernel/param.h b/kernel/param.h index cd2a41c..abd95e5 100644 --- a/kernel/param.h +++ b/kernel/param.h @@ -9,7 +9,7 @@ #define MAXOPBLOCKS 10 // max # of blocks any FS op writes #define LOGBLOCKS (MAXOPBLOCKS*3) // max data blocks in on-disk log #define NBUF (MAXOPBLOCKS*3) // size of disk block cache -#define FSSIZE 2000 // size of file system in blocks +#define FSSIZE 4000 // size of file system in blocks #define MAXPATH 128 // maximum file path name #define USERSTACK 1 // user stack pages diff --git a/kernel/proc.c b/kernel/proc.c index 9d6cf3f..f1bf4fa 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -125,6 +125,12 @@ allocproc(void) p->pid = allocpid(); p->state = USED; + p->nice = 1; + p->priority = 3 - p->nice; + + p->mmap = 0; + p->mmap_pages = 0; + // Allocate a trapframe page. if((p->trapframe = (struct trapframe *)kalloc()) == 0){ freeproc(p); @@ -158,8 +164,15 @@ freeproc(struct proc *p) if(p->trapframe) kfree((void*)p->trapframe); p->trapframe = 0; - if(p->pagetable) - proc_freepagetable(p->pagetable, p->sz); + if (p->pagetable) { + if (p->mmap_pages > 0) { + uvmunmap(p->pagetable, p->mmap, p->mmap_pages, 1); + p->mmap_pages = 0; + p->mmap = 0; + } + proc_freepagetable(p->pagetable, p->sz); + } + p->pagetable = 0; p->sz = 0; p->pid = 0; @@ -168,6 +181,7 @@ freeproc(struct proc *p) p->chan = 0; p->killed = 0; p->xstate = 0; + p->tracing = 0; p->state = UNUSED; } @@ -279,6 +293,25 @@ kfork(void) // Cause fork to return 0 in the child. np->trapframe->a0 = 0; + np->mmap = p->mmap; + np->mmap_pages = p->mmap_pages; + + for (i = 0; i < p->mmap_pages; i++) { + uint64 va = p->mmap + (uint64)i * PGSIZE; + + uint64 pa = walkaddr(p->pagetable, va); + if (pa == 0) + panic("kfork: mmap walkaddr failed"); + + + if(mappages(np->pagetable, va, PGSIZE, pa, + PTE_R | PTE_W | PTE_U) < 0) + panic("kfrok: mmap mappages failed"); + + + incref(pa); + } + // increment reference counts on open file descriptors. for(i = 0; i < NOFILE; i++) if(p->ofile[i]) @@ -287,6 +320,9 @@ kfork(void) safestrcpy(np->name, p->name, sizeof(p->name)); + np->nice = p->nice; // copy parent's nicenessœ + np->priority = p->priority; + pid = np->pid; release(&np->lock); @@ -411,6 +447,64 @@ kwait(uint64 addr) } } +// Wait for a child process to exit and return its pid. +// Return -1 if this process has no children. +int +kwait2(uint64 addr, uint64 addr2) +{ + struct proc *pp; + int havekids, pid; + struct proc *p = myproc(); + + acquire(&wait_lock); + + for(;;){ + // Scan through table looking for exited children. + havekids = 0; + for(pp = proc; pp < &proc[NPROC]; pp++){ + if(pp->parent == p){ + // make sure the child isn't still in exit() or swtch(). + acquire(&pp->lock); + + havekids = 1; + if(pp->state == ZOMBIE){ + // Found one. + pid = pp->pid; + if(addr != 0 && copyout(p->pagetable, addr, (char *)&pp->xstate, + sizeof(pp->xstate)) < 0) { + release(&pp->lock); + release(&wait_lock); + return -1; + } + + // copy syscall count to user + if(addr2 != 0 && copyout(p->pagetable, addr2, (char *)&pp->counter, + sizeof(pp->counter)) < 0) { + release(&pp->lock); + release(&wait_lock); + return -1; + } + + freeproc(pp); + release(&pp->lock); + release(&wait_lock); + return pid; + } + release(&pp->lock); + } + } + + // No point waiting if we don't have any children. + if(!havekids || killed(p)){ + release(&wait_lock); + return -1; + } + + // Wait for a child to exit. + sleep(p, &wait_lock); //DOC: wait-sleep + } +} + // Per-CPU process scheduler. // Each CPU calls scheduler() after setting itself up. // Scheduler never returns. It loops, doing: @@ -426,39 +520,64 @@ scheduler(void) c->proc = 0; for(;;){ - // The most recent process to run may have had interrupts - // turned off; enable them to avoid a deadlock if all - // processes are waiting. Then turn them back off - // to avoid a possible race between an interrupt - // and wfi. - intr_on(); - intr_off(); - - int found = 0; - for(p = proc; p < &proc[NPROC]; p++) { - acquire(&p->lock); - if(p->state == RUNNABLE) { - // Switch to chosen process. It is the process's job - // to release its lock and then reacquire it - // before jumping back to us. - p->state = RUNNING; - c->proc = p; - swtch(&c->context, &p->context); - - // Process is done running for now. - // It should have changed its p->state before coming back. - c->proc = 0; - found = 1; + // The most recent process to run may have had interrupts + // turned off; enable them to avoid a deadlock if all + // processes are waiting. Then turn them back off + // to avoid a possible race between an interrupt + // and wfi. + intr_on(); + intr_off(); + + int found = 0; + + // Find the highest priority among RUNNABLE processes + // so we can start scanning from there + int max_priority = -1; + for(p = proc; p < &proc[NPROC]; p++){ + acquire(&p->lock); + if(p->state == RUNNABLE && p->priority > max_priority) + max_priority = p->priority; + release(&p->lock); + } + + // Iterate priority "levels" from highest we observed down to 0. + // At each level, run any RUNNABLE process whose priority is >= level. + if(max_priority >= 0){ + for(int level = max_priority; level >= 0; level--){ + int found_runnable = 0; + + for(p = proc; p < &proc[NPROC]; p++){ + acquire(&p->lock); + if(p->state == RUNNABLE && p->priority >= level){ + found_runnable = 1; + + // Switch to chosen process. It is the process's job + // to release its lock and then reacquire it + // before jumping back to us. + p->state = RUNNING; + c->proc = p; + swtch(&c->context, &p->context); + + // Process is done running for now. + // It should have changed its p->state before coming back. + c->proc = 0; + found = 1; + } + release(&p->lock); } - release(&p->lock); - } - if(found == 0) { - // nothing to run; stop running on this core until an interrupt. - asm volatile("wfi"); + + // If no process qualified at this level, just drop to the next one. + if(!found_runnable) + continue; } } -} + if(found == 0){ + // nothing to run; stop running on this core until an interrupt. + asm volatile("wfi"); + } + } +} // Switch to scheduler. Must hold only p->lock // and have changed proc->state. Saves and restores // intena because intena is a property of this @@ -681,7 +800,7 @@ procdump(void) state = states[p->state]; else state = "???"; - printf("%d %s %s", p->pid, state, p->name); + printf("%d %s %s priority=%d nice=%d\n", p->pid, state, p->name, p->priority, p->nice); printf("\n"); } } diff --git a/kernel/proc.h b/kernel/proc.h index d021857..d591306 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -91,6 +91,10 @@ struct proc { int killed; // If non-zero, have been killed int xstate; // Exit status to be returned to parent's wait int pid; // Process ID + int tracing; // Trace Flag + int counter; // Counter for tracking SYS calls + int nice; // (0 = highest priority) + int priority; // (derived: prio = 3 - nice; higher number = higher priority) // wait_lock must be held when using this: struct proc *parent; // Parent process @@ -98,6 +102,10 @@ struct proc { // these are private to the process, so p->lock need not be held. uint64 kstack; // Virtual address of kernel stack uint64 sz; // Size of process memory (bytes) + + uint64 mmap; // lowest virtual address used by mmap region + int mmap_pages; // number of mmap pages currently mapped + pagetable_t pagetable; // User page table struct trapframe *trapframe; // data page for trampoline.S struct context context; // swtch() here to run process diff --git a/kernel/strace.h b/kernel/strace.h new file mode 100644 index 0000000..3fb96c2 --- /dev/null +++ b/kernel/strace.h @@ -0,0 +1,121 @@ +#include "defs.h" +#include "syscall.h" +#pragma GCC diagnostic ignored "-Wunused-function" + +static uint64 +read_int(int n) +{ + int i; + argint(n, &i); + return i; +} + +static void * +read_ptr(int n) +{ + // TODO: read an argument as a pointer. wait() in sysproc.c + // is a good example of doing this. + uint64 addr; + argaddr(n, &addr); + return (void *)addr; +} + +static char * +read_str(int n) +{ + // TODO: read an argument as a string. open() or mkdir() in sysfile.c + // are good examples of doing this. + static char buf[128]; + if (argstr(n, buf, sizeof(buf)) < 0) + return "(null)"; + return buf; +} + +void +strace(struct proc *p, int syscall_num, uint64 ret_val) +{ + // printf("[strace] syscall: %d\n", syscall_num); + // This will drop the raw syscall output here, so it's commented out for + // now... we need to update this function so that it prints system call + // tracing information (based on the syscall_num passed in). + switch(syscall_num){ + case SYS_fork: + printf("[%d|%s] fork() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_exit: + printf("[%d|%s] exit(int = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_wait: + printf("[%d|%s] wait(status = %p) = %ld\n", p->pid, p->name, read_ptr(0), ret_val); + break; +case SYS_pipe: + printf("[%d|%s] pipe(pipefd = %p) = %ld\n", p->pid, p->name, read_ptr(0), ret_val); + break; +case SYS_read: + printf("[%d|%s] read(fd = %ld, buf = %p, count = %ld) = %ld\n", p->pid, p->name, read_int(0), read_ptr(1), read_int(2), ret_val); + break; +case SYS_write: + printf("[%d|%s] write(fd = %ld, buf = %p, count = %ld) = %ld\n", p->pid, p->name, read_int(0), read_ptr(1), read_int(2), ret_val); + break; +case SYS_close: + printf("[%d|%s] close(fd = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_kill: + printf("[%d|%s] kill(pid = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_exec: + printf("[%d|%s] exec(path = %p, argv = %p) = %ld\n", p->pid, p->name, read_ptr(0), read_ptr(1), ret_val); + break; +case SYS_open: + printf("[%d|%s] open(path = %s, mode = %ld) = %ld\n", p->pid, p->name, read_str(0), read_int(1), ret_val); + break; +case SYS_mknod: + printf("[%d|%s] mknod(path = %s, major = %ld, minor = %ld) = %ld\n", p->pid, p->name, read_str(0), read_int(1), read_int(2), ret_val); + break; +case SYS_unlink: + printf("[%d|%s] unlink(path = %s) = %ld\n", p->pid, p->name, read_str(0), ret_val); + break; +case SYS_fstat: + printf("[%d|%s] fstat(fd = %ld, st = %p) = %ld\n", p->pid, p->name, read_int(0), read_ptr(1), ret_val); + break; +case SYS_link: + printf("[%d|%s] link(old_path = %s, new_path = %s) = %ld\n", p->pid, p->name, read_str(0), read_str(1), ret_val); + break; +case SYS_mkdir: + printf("[%d|%s] mkdir(path = %s) = %ld\n", p->pid, p->name, read_str(0), ret_val); + break; +case SYS_chdir: + printf("[%d|%s] chdir(path = %s) = %ld\n", p->pid, p->name, read_str(0), ret_val); + break; +case SYS_dup: + printf("[%d|%s] dup(fd = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_getpid: + printf("[%d|%s] getpid() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_sbrk: + printf("[%d|%s] sbrk(int = %ld) = %p\n", p->pid, p->name, read_int(0), (void *) ret_val); + break; +case SYS_pause: + printf("[%d|%s] pause(int = %ld) = %ld\n", p->pid, p->name, read_int(0), ret_val); + break; +case SYS_uptime: + printf("[%d|%s] uptime() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_shutdown: + printf("[%d|%s] shutdown() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_reboot: + printf("[%d|%s] reboot() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_clock: + printf("[%d|%s] clock() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_strace_on: + printf("[%d|%s] strace_on() = %ld\n", p->pid, p->name, ret_val); + break; +case SYS_wait2: + printf("[%d|%s] wait2(status = %p, counter = %p) = %ld\n", p->pid, p->name, read_ptr(0), read_ptr(1), ret_val); + break; + } +} diff --git a/kernel/syscall.c b/kernel/syscall.c index 076d965..fc2075c 100644 --- a/kernel/syscall.c +++ b/kernel/syscall.c @@ -6,6 +6,7 @@ #include "proc.h" #include "syscall.h" #include "defs.h" +#include "strace.h" // Fetch the uint64 at addr from the current process. int @@ -101,6 +102,15 @@ extern uint64 sys_unlink(void); extern uint64 sys_link(void); extern uint64 sys_mkdir(void); extern uint64 sys_close(void); +extern uint64 sys_shutdown(void); +extern uint64 sys_reboot(void); +extern uint64 sys_clock(void); +extern uint64 sys_strace_on(void); +extern uint64 sys_wait2(void); +extern uint64 sys_nice(void); +extern uint64 sys_getcwd(void); +extern uint64 sys_freemem(void); +extern uint64 sys_mmap(void); // An array mapping syscall numbers from syscall.h // to the function that handles the system call. @@ -126,6 +136,15 @@ static uint64 (*syscalls[])(void) = { [SYS_link] sys_link, [SYS_mkdir] sys_mkdir, [SYS_close] sys_close, +[SYS_shutdown] sys_shutdown, +[SYS_reboot] sys_reboot, +[SYS_clock] sys_clock, +[SYS_strace_on] sys_strace_on, +[SYS_wait2] sys_wait2, +[SYS_nice] sys_nice, +[SYS_getcwd] sys_getcwd, +[SYS_freemem] sys_freemem, +[SYS_mmap] sys_mmap, }; void @@ -138,7 +157,14 @@ syscall(void) if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { // Use num to lookup the system call function for num, call it, // and store its return value in p->trapframe->a0 - p->trapframe->a0 = syscalls[num](); + p->counter++; // increase sys call counter + + uint64 ptr = syscalls[num](); + + if (p->tracing) { + strace(p, num, ptr); + } + p->trapframe->a0 = ptr; } else { printf("%d %s: unknown sys call %d\n", p->pid, p->name, num); diff --git a/kernel/syscall.h b/kernel/syscall.h index 3dd926d..5e11c4c 100644 --- a/kernel/syscall.h +++ b/kernel/syscall.h @@ -20,3 +20,12 @@ #define SYS_link 19 #define SYS_mkdir 20 #define SYS_close 21 +#define SYS_shutdown 22 +#define SYS_reboot 23 +#define SYS_clock 24 +#define SYS_strace_on 25 +#define SYS_wait2 26 +#define SYS_nice 27 +#define SYS_getcwd 28 +#define SYS_freemem 29 +#define SYS_mmap 30 diff --git a/kernel/sysfile.c b/kernel/sysfile.c index d8234ce..e6ca5ba 100644 --- a/kernel/sysfile.c +++ b/kernel/sysfile.c @@ -406,6 +406,34 @@ sys_mknod(void) return 0; } +uint64 +sys_getcwd(void) +{ + uint64 ubuf; + int sz; + + begin_op(); + argaddr(0, &ubuf); + argint(1, &sz); + + if (sz > PGSIZE) { + end_op(); + return -1; + } + + char *buf = kalloc(); + getcwd(buf, sz); + + if (copyout(myproc()->pagetable, ubuf, buf, sz) < 0) { + end_op(); + return -1; + } + + kfree(buf); + end_op(); + return 0; +} + uint64 sys_chdir(void) { diff --git a/kernel/sysproc.c b/kernel/sysproc.c index 3044d00..58038e7 100644 --- a/kernel/sysproc.c +++ b/kernel/sysproc.c @@ -33,7 +33,16 @@ sys_wait(void) { uint64 p; argaddr(0, &p); - return kwait(p); + return kwait2(p, 0); +} + +uint64 +sys_wait2(void) +{ + uint64 p, p2; + argaddr(0, &p); + argaddr(1, &p2); + return kwait2(p, p2); } uint64 @@ -105,3 +114,96 @@ sys_uptime(void) release(&tickslock); return xticks; } + +// shutdown +uint64 +sys_shutdown(void) +{ + volatile uint32 *test_dev = (uint32 *) VIRT_TEST; + *test_dev = 0x5555; + + return 0; +} + +// reboot +uint64 +sys_reboot(void) +{ + volatile uint32 *test_dev = (uint32 *) VIRT_TEST; + *test_dev = 0x7777; + + return 0; +} + +uint64 +sys_clock(void) +{ + volatile uint32 *base = (uint32*)GOLDFISH; + + uint32 first = base[0]; // low 32 bits + uint32 second = base[1]; // high 32 + + return ((uint64)second << 32) | first; +} + +uint64 +sys_strace_on(void) +{ + struct proc *p = myproc(); + p->tracing = 1; + return 0; +} + +uint64 +sys_nice(void) +{ + int n; + argint(0, &n); + + if (n < 0) n = 0; + if (n > 3) n = 3; + + struct proc *p = myproc(); + acquire(&p->lock); + + p->nice = n; + p->priority = 3 - n; + release(&p->lock); + + return p->priority; +} + +uint64 +sys_freemem(void) +{ + uint64 pages = kfreepages(); + return pages * (PGSIZE / 1024); +} + +uint64 +sys_mmap(void) +{ + struct proc *p = myproc(); + + void *pa = kalloc(); + if(pa == 0) + return (uint64)-1; + + memset(pa, 0, PGSIZE); + + uint64 va = p->mmap - PGSIZE; + + if(mappages(p->pagetable, va, PGSIZE, (uint64)pa, + PTE_R | PTE_W | PTE_U) < 0){ + kfree(pa); + return (uint64)-1; + } + + if(walkaddr(p->pagetable, va) != (uint64)pa) + panic("sys_mmap: mapping error"); + + p->mmap = va; + p->mmap_pages++; + + return va; +} diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..9c91a0d --- /dev/null +++ b/test.sh @@ -0,0 +1,9 @@ +#!/crash +ls / +echo hi +mkdir d +cd d +ls +/ls +/echo bye +exit diff --git a/tm.txt b/tm.txt new file mode 100644 index 0000000..2d3f69a --- /dev/null +++ b/tm.txt @@ -0,0 +1,500 @@ + +The Time Machine +An Invention + +by H. G. Wells + + + + +CONTENTS + + I Introduction + II The Machine + III The Time Traveller Returns + IV Time Travelling + V In the Golden Age + VI The Sunset of Mankind + VII A Sudden Shock + VIII Explanation + IX The Morlocks + X When Night Came + XI The Palace of Green Porcelain + XII In the Darkness + XIII The Trap of the White Sphinx + XIV The Further Vision + XV The Time Traveller’s Return + XVI After the Story + Epilogue + + + + + I. + Introduction + + +The Time Traveller (for so it will be convenient to speak of him) was +expounding a recondite matter to us. His pale grey eyes shone and +twinkled, and his usually pale face was flushed and animated. The fire +burnt brightly, and the soft radiance of the incandescent lights in the +lilies of silver caught the bubbles that flashed and passed in our +glasses. Our chairs, being his patents, embraced and caressed us rather +than submitted to be sat upon, and there was that luxurious +after-dinner atmosphere, when thought runs gracefully free of the +trammels of precision. And he put it to us in this way—marking the +points with a lean forefinger—as we sat and lazily admired his +earnestness over this new paradox (as we thought it) and his fecundity. + +“You must follow me carefully. I shall have to controvert one or two +ideas that are almost universally accepted. The geometry, for instance, +they taught you at school is founded on a misconception.” + +“Is not that rather a large thing to expect us to begin upon?” said +Filby, an argumentative person with red hair. + +“I do not mean to ask you to accept anything without reasonable ground +for it. You will soon admit as much as I need from you. You know of +course that a mathematical line, a line of thickness _nil_, has no real +existence. They taught you that? Neither has a mathematical plane. +These things are mere abstractions.” + +“That is all right,” said the Psychologist. + +“Nor, having only length, breadth, and thickness, can a cube have a +real existence.” + +“There I object,” said Filby. “Of course a solid body may exist. All +real things—” + +“So most people think. But wait a moment. Can an _instantaneous_ cube +exist?” + +“Don’t follow you,” said Filby. + +“Can a cube that does not last for any time at all, have a real +existence?” + +Filby became pensive. “Clearly,” the Time Traveller proceeded, “any +real body must have extension in _four_ directions: it must have +Length, Breadth, Thickness, and—Duration. But through a natural +infirmity of the flesh, which I will explain to you in a moment, we +incline to overlook this fact. There are really four dimensions, three +which we call the three planes of Space, and a fourth, Time. There is, +however, a tendency to draw an unreal distinction between the former +three dimensions and the latter, because it happens that our +consciousness moves intermittently in one direction along the latter +from the beginning to the end of our lives.” + +“That,” said a very young man, making spasmodic efforts to relight his +cigar over the lamp; “that . . . very clear indeed.” + +“Now, it is very remarkable that this is so extensively overlooked,” +continued the Time Traveller, with a slight accession of cheerfulness. +“Really this is what is meant by the Fourth Dimension, though some +people who talk about the Fourth Dimension do not know they mean it. It +is only another way of looking at Time. _There is no difference between +Time and any of the three dimensions of Space except that our +consciousness moves along it_. But some foolish people have got hold of +the wrong side of that idea. You have all heard what they have to say +about this Fourth Dimension?” + +“_I_ have not,” said the Provincial Mayor. + +“It is simply this. That Space, as our mathematicians have it, is +spoken of as having three dimensions, which one may call Length, +Breadth, and Thickness, and is always definable by reference to three +planes, each at right angles to the others. But some philosophical +people have been asking why _three_ dimensions particularly—why not +another direction at right angles to the other three?—and have even +tried to construct a Four-Dimensional geometry. Professor Simon Newcomb +was expounding this to the New York Mathematical Society only a month +or so ago. You know how on a flat surface, which has only two +dimensions, we can represent a figure of a three-dimensional solid, and +similarly they think that by models of three dimensions they could +represent one of four—if they could master the perspective of the +thing. See?” + +“I think so,” murmured the Provincial Mayor; and, knitting his brows, +he lapsed into an introspective state, his lips moving as one who +repeats mystic words. “Yes, I think I see it now,” he said after some +time, brightening in a quite transitory manner. + +“Well, I do not mind telling you I have been at work upon this geometry +of Four Dimensions for some time. Some of my results are curious. For +instance, here is a portrait of a man at eight years old, another at +fifteen, another at seventeen, another at twenty-three, and so on. All +these are evidently sections, as it were, Three-Dimensional +representations of his Four-Dimensioned being, which is a fixed and +unalterable thing. + +“Scientific people,” proceeded the Time Traveller, after the pause +required for the proper assimilation of this, “know very well that Time +is only a kind of Space. Here is a popular scientific diagram, a +weather record. This line I trace with my finger shows the movement of +the barometer. Yesterday it was so high, yesterday night it fell, then +this morning it rose again, and so gently upward to here. Surely the +mercury did not trace this line in any of the dimensions of Space +generally recognised? But certainly it traced such a line, and that +line, therefore, we must conclude, was along the Time-Dimension.” + +“But,” said the Medical Man, staring hard at a coal in the fire, “if +Time is really only a fourth dimension of Space, why is it, and why has +it always been, regarded as something different? And why cannot we move +in Time as we move about in the other dimensions of Space?” + +The Time Traveller smiled. “Are you so sure we can move freely in +Space? Right and left we can go, backward and forward freely enough, +and men always have done so. I admit we move freely in two dimensions. +But how about up and down? Gravitation limits us there.” + +“Not exactly,” said the Medical Man. “There are balloons.” + +“But before the balloons, save for spasmodic jumping and the +inequalities of the surface, man had no freedom of vertical movement.” + +“Still they could move a little up and down,” said the Medical Man. + +“Easier, far easier down than up.” + +“And you cannot move at all in Time, you cannot get away from the +present moment.” + +“My dear sir, that is just where you are wrong. That is just where the +whole world has gone wrong. We are always getting away from the present +moment. Our mental existences, which are immaterial and have no +dimensions, are passing along the Time-Dimension with a uniform +velocity from the cradle to the grave. Just as we should travel _down_ +if we began our existence fifty miles above the earth’s surface.” + +“But the great difficulty is this,” interrupted the Psychologist. ’You +_can_ move about in all directions of Space, but you cannot move about +in Time.” + +“That is the germ of my great discovery. But you are wrong to say that +we cannot move about in Time. For instance, if I am recalling an +incident very vividly I go back to the instant of its occurrence: I +become absent-minded, as you say. I jump back for a moment. Of course +we have no means of staying back for any length of Time, any more than +a savage or an animal has of staying six feet above the ground. But a +civilised man is better off than the savage in this respect. He can go +up against gravitation in a balloon, and why should he not hope that +ultimately he may be able to stop or accelerate his drift along the +Time-Dimension, or even turn about and travel the other way?” + +“Oh, _this_,” began Filby, “is all—” + +“Why not?” said the Time Traveller. + +“It’s against reason,” said Filby. + +“What reason?” said the Time Traveller. + +“You can show black is white by argument,” said Filby, “but you will +never convince me.” + +“Possibly not,” said the Time Traveller. “But now you begin to see the +object of my investigations into the geometry of Four Dimensions. Long +ago I had a vague inkling of a machine—” + +“To travel through Time!” exclaimed the Very Young Man. + +“That shall travel indifferently in any direction of Space and Time, as +the driver determines.” + +Filby contented himself with laughter. + +“But I have experimental verification,” said the Time Traveller. + +“It would be remarkably convenient for the historian,” the Psychologist +suggested. “One might travel back and verify the accepted account of +the Battle of Hastings, for instance!” + +“Don’t you think you would attract attention?” said the Medical Man. +“Our ancestors had no great tolerance for anachronisms.” + +“One might get one’s Greek from the very lips of Homer and Plato,” the +Very Young Man thought. + +“In which case they would certainly plough you for the Little-go. The +German scholars have improved Greek so much.” + +“Then there is the future,” said the Very Young Man. “Just think! One +might invest all one’s money, leave it to accumulate at interest, and +hurry on ahead!” + +“To discover a society,” said I, “erected on a strictly communistic +basis.” + +“Of all the wild extravagant theories!” began the Psychologist. + +“Yes, so it seemed to me, and so I never talked of it until—” + +“Experimental verification!” cried I. “You are going to verify _that_?” + +“The experiment!” cried Filby, who was getting brain-weary. + +“Let’s see your experiment anyhow,” said the Psychologist, “though it’s +all humbug, you know.” + +The Time Traveller smiled round at us. Then, still smiling faintly, and +with his hands deep in his trousers pockets, he walked slowly out of +the room, and we heard his slippers shuffling down the long passage to +his laboratory. + +The Psychologist looked at us. “I wonder what he’s got?” + +“Some sleight-of-hand trick or other,” said the Medical Man, and Filby +tried to tell us about a conjuror he had seen at Burslem, but before he +had finished his preface the Time Traveller came back, and Filby’s +anecdote collapsed. + + + + + II. + The Machine + + +The thing the Time Traveller held in his hand was a glittering metallic +framework, scarcely larger than a small clock, and very delicately +made. There was ivory in it, and some transparent crystalline +substance. And now I must be explicit, for this that follows—unless his +explanation is to be accepted—is an absolutely unaccountable thing. He +took one of the small octagonal tables that were scattered about the +room, and set it in front of the fire, with two legs on the hearthrug. +On this table he placed the mechanism. Then he drew up a chair, and sat +down. The only other object on the table was a small shaded lamp, the +bright light of which fell upon the model. There were also perhaps a +dozen candles about, two in brass candlesticks upon the mantel and +several in sconces, so that the room was brilliantly illuminated. I sat +in a low arm-chair nearest the fire, and I drew this forward so as to +be almost between the Time Traveller and the fireplace. Filby sat +behind him, looking over his shoulder. The Medical Man and the +Provincial Mayor watched him in profile from the right, the +Psychologist from the left. The Very Young Man stood behind the +Psychologist. We were all on the alert. It appears incredible to me +that any kind of trick, however subtly conceived and however adroitly +done, could have been played upon us under these conditions. + +The Time Traveller looked at us, and then at the mechanism. “Well?” +said the Psychologist. + +“This little affair,” said the Time Traveller, resting his elbows upon +the table and pressing his hands together above the apparatus, “is only +a model. It is my plan for a machine to travel through time. You will +notice that it looks singularly askew, and that there is an odd +twinkling appearance about this bar, as though it was in some way +unreal.” He pointed to the part with his finger. “Also, here is one +little white lever, and here is another.” + +The Medical Man got up out of his chair and peered into the thing. +“It’s beautifully made,” he said. + +“It took two years to make,” retorted the Time Traveller. Then, when we +had all imitated the action of the Medical Man, he said: “Now I want +you clearly to understand that this lever, being pressed over, sends +the machine gliding into the future, and this other reverses the +motion. This saddle represents the seat of a time traveller. Presently +I am going to press the lever, and off the machine will go. It will +vanish, pass into future Time, and disappear. Have a good look at the +thing. Look at the table too, and satisfy yourselves there is no +trickery. I don’t want to waste this model, and then be told I’m a +quack.” + +There was a minute’s pause perhaps. The Psychologist seemed about to +speak to me, but changed his mind. Then the Time Traveller put forth +his finger towards the lever. “No,” he said suddenly. “Lend me your +hand.” And turning to the Psychologist, he took that individual’s hand +in his own and told him to put out his forefinger. So that it was the +Psychologist himself who sent forth the model Time Machine on its +interminable voyage. We all saw the lever turn. I am absolutely certain +there was no trickery. There was a breath of wind, and the lamp flame +jumped. One of the candles on the mantel was blown out, and the little +machine suddenly swung round, became indistinct, was seen as a ghost +for a second perhaps, as an eddy of faintly glittering brass and ivory; +and it was gone—vanished! Save for the lamp the table was bare. + +Everyone was silent for a minute. Then Filby said he was damned. + +The Psychologist recovered from his stupor, and suddenly looked under +the table. At that the Time Traveller laughed cheerfully. “Well?” he +said, with a reminiscence of the Psychologist. Then, getting up, he +went to the tobacco jar on the mantel, and with his back to us began to +fill his pipe. + +We stared at each other. “Look here,” said the Medical Man, “are you in +earnest about this? Do you seriously believe that that machine has +travelled into time?” + +“Certainly,” said the Time Traveller, stooping to light a spill at the +fire. Then he turned, lighting his pipe, to look at the Psychologist’s +face. (The Psychologist, to show that he was not unhinged, helped +himself to a cigar and tried to light it uncut.) “What is more, I have +a big machine nearly finished in there”—he indicated the +laboratory—“and when that is put together I mean to have a journey on +my own account.” + +“You mean to say that that machine has travelled into the future?” said +Filby. + +“Into the future or the past—I don’t, for certain, know which.” + +After an interval the Psychologist had an inspiration. “It must have +gone into the past if it has gone anywhere,” he said. + +“Why?” said the Time Traveller. + +“Because I presume that it has not moved in space, and if it travelled +into the future it would still be here all this time, since it must +have travelled through this time.” + +“But,” said I, “If it travelled into the past it would have been +visible when we came first into this room; and last Thursday when we +were here; and the Thursday before that; and so forth!” + +“Serious objections,” remarked the Provincial Mayor, with an air of +impartiality, turning towards the Time Traveller. + +“Not a bit,” said the Time Traveller, and, to the Psychologist: “You +think. _You_ can explain that. It’s presentation below the threshold, +you know, diluted presentation.” + +“Of course,” said the Psychologist, and reassured us. “That’s a simple +point of psychology. I should have thought of it. It’s plain enough, +and helps the paradox delightfully. We cannot see it, nor can we +appreciate this machine, any more than we can the spoke of a wheel +spinning, or a bullet flying through the air. If it is travelling +through time fifty times or a hundred times faster than we are, if it +gets through a minute while we get through a second, the impression it +creates will of course be only one-fiftieth or one-hundredth of what it +would make if it were not travelling in time. That’s plain enough.” He +passed his hand through the space in which the machine had been. “You +see?” he said, laughing. + +We sat and stared at the vacant table for a minute or so. Then the Time +Traveller asked us what we thought of it all. + +“It sounds plausible enough tonight,” said the Medical Man; “but wait +until tomorrow. Wait for the common sense of the morning.” + +“Would you like to see the Time Machine itself?” asked the Time +Traveller. And therewith, taking the lamp in his hand, he led the way +down the long, draughty corridor to his laboratory. I remember vividly +the flickering light, his queer, broad head in silhouette, the dance of +the shadows, how we all followed him, puzzled but incredulous, and how +there in the laboratory we beheld a larger edition of the little +mechanism which we had seen vanish from before our eyes. Parts were of +nickel, parts of ivory, parts had certainly been filed or sawn out of +rock crystal. The thing was generally complete, but the twisted +crystalline bars lay unfinished upon the bench beside some sheets of +drawings, and I took one up for a better look at it. Quartz it seemed +to be. + +“Look here,” said the Medical Man, “are you perfectly serious? Or is +this a trick—like that ghost you showed us last Christmas?” + +“Upon that machine,” said the Time Traveller, holding the lamp aloft, +“I intend to explore time. Is that plain? I was never more serious in +my life.” + +None of us quite knew how to take it. + +I caught Filby’s eye over the shoulder of the Medical Man, and he +winked at me solemnly. + + + + + III. + The Time Traveller Returns + + +I think that at that time none of us quite believed in the Time +Machine. The fact is, the Time Traveller was one of those men who are +too clever to be believed: you never felt that you saw all round him; +you always suspected some subtle reserve, some ingenuity in ambush, +behind his lucid frankness. Had Filby shown the model and explained the +matter in the Time Traveller’s words, we should have shown _him_ far +less scepticism. For we should have perceived his motives: a +pork-butcher could understand Filby. But the Time Traveller had more +than a touch of whim among his elements, and we distrusted him. Things +that would have made the fame of a less clever man seemed tricks in his +hands. It is a mistake to do things too easily. The serious people who +took him seriously never felt quite sure of his deportment; they were +somehow aware that trusting their reputations for judgment with him was +like furnishing a nursery with eggshell china. So I don’t think any of +us said very much about time travelling in the interval between that +Thursday and the next, though its odd potentialities ran, no doubt, in +most of our minds: its plausibility, that is, its practical +incredibleness, the curious possibilities of anachronism and of utter +confusion it suggested. For my own part, I was particularly preoccupied +with the trick of the model. That I remember discussing with the +Medical Man, whom I met on Friday at the Linnæan. He said he had seen a +similar thing at Tübingen, and laid considerable stress on the +blowing-out of the candle. But how the trick was done he could not +explain. + +The next Thursday I went again to Richmond—I suppose I was one of the +Time Traveller’s most constant guests—and, arriving late, found four or +five men already assembled in his drawing-room. The Medical Man was +standing before the fire with a sheet of paper in one hand and his +watch in the other. I looked round for the Time Traveller, and—“It’s +half-past seven now,” said the Medical Man. “I suppose we’d better have +dinner?” + +“Where’s——?” said I, naming our host. + +“You’ve just come? It’s rather odd. He’s unavoidably detained. He asks +me in this note to lead off with dinner at seven if he’s not back. Says +he’ll explain when he comes.” + +“It seems a pity to let the dinner spoil,” said the Editor of a +well-known daily paper; and thereupon the Doctor rang the bell. + +The Psychologist was the only person besides the Doctor and myself who +had attended the previous dinner. The other men were Blank, the Editor +aforementioned, a certain journalist, and another—a quiet, shy man with +a beard—whom I didn’t know, and who, as far as my observation went, +never opened his mouth all the evening. There was some speculation at +the dinner-table about the Time Traveller’s absence, and I suggested +time travelling, in a half-jocular spirit. The Editor wanted that +explained to him, and the Psychologist volunteered a wooden account of +the “ingenious paradox and trick” we had witnessed that day week. He +was in the midst of his exposition when the door from the corridor +opened slowly and without noise. I was facing the door, and saw it +first. “Hallo!” I said. “At last!” And the door opened wider, and the +Time Traveller stood before us. I gave a cry of surprise. “Good +heavens! man, what’s the matter?” cried the Medical Man, who saw him +next. And the whole tableful turned towards the door. + +He was in an amazing plight. His coat was dusty and dirty, and smeared +with green down the sleeves; his hair disordered, and as it seemed to +me greyer—either with dust and dirt or because its colour had actually +faded. His face was ghastly pale; his chin had a brown cut on it—a cut +half-healed; his expression was haggard and drawn, as by intense +suffering. For a moment he hesitated in the doorway, as if he had been +dazzled by the light. Then he came into the room. He walked with just +such a limp as I have seen in footsore tramps. We stared at him in +silence, expecting him to speak. + +He said not a word, but came painfully to the table, and made a motion +towards the wine. The Editor filled a glass of champagne, and pushed it +towards him. He drained it, and it seemed to do him good: for he looked +round the table, and the ghost of his old smile flickered across his +face. “What on earth have you been up to, man?” said the Doctor. The +Time Traveller did not seem to hear. “Don’t let me disturb you,” he +said, with a certain faltering articulation. “I’m all right.” He +stopped, held out his glass for more, and took it off at a draught. +“That’s good,” he said. His eyes grew brighter, and a faint colour came +into his cheeks. His glance flickered over our faces with a certain +dull approval, and then went round the warm and comfortable room. Then +he spoke again, still as it were feeling his way among his words. “I’m +going to wash and dress, and then I’ll come down and explain things.... +Save me some of that mutton. I’m starving for a bit of meat.” + +He looked across at the Editor, who was a rare visitor, and hoped he +was all right. The Editor began a question. “Tell you presently,” said +the Time Traveller. “I’m—funny! Be all right in a minute.” + +He put down his glass, and walked towards the staircase door. diff --git a/user/about.c b/user/about.c new file mode 100644 index 0000000..a26e2c5 --- /dev/null +++ b/user/about.c @@ -0,0 +1,13 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int main() { + printf("Welcome to Kailash OS!\n"); + printf("-------------------------------------\n"); + printf("Author: Kailash Turimella\n"); + printf("USF Username: kturimella\n"); + printf("Preferred Name: Kailash\n"); + + exit(0); +} diff --git a/user/benchmark.c b/user/benchmark.c new file mode 100644 index 0000000..ddfd42d --- /dev/null +++ b/user/benchmark.c @@ -0,0 +1,40 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int main(int argc, char *argv[]) { + if (argc < 2) { + fprintf(2, "Usage: %s [args...]\n", argv[0]); + exit(1); + } + + uint64 start = clock(); + + int pid = fork(); + if (pid < 0) { + fprintf(2, "benchmark: fork failed\n"); + exit(1); + } + if (pid == 0) { + exec(argv[1], &argv[1]); + fprintf(2, "benchmark: exec %s failed\n", argv[1]); + exit(1); + } + + int status; + int counter; + + if (wait2(&status, &counter) < 0) { + fprintf(2, "benchmark: wait2 failed\n"); + exit(1); + } + + uint64 end = clock(); + uint64 time_taken = end - start; + uint64 time_taken_ms = time_taken / 1000000ULL; + + printf("Benchmark Complete\n"); + printf("Time Elapsed: %d ms\n", (int)time_taken_ms); + printf("System Calls: %d\n", (int)counter); + exit(0); +} diff --git a/user/broken.c b/user/broken.c new file mode 100644 index 0000000..779f9ed --- /dev/null +++ b/user/broken.c @@ -0,0 +1,324 @@ +/** + * @file broken.c + * @author mmalensek + * + * This program contains a series of buggy, broken, or strange C functions for + * you to ponder. Your job is to analyze each function, fix whatever bugs the + * functions might have, and then explain what went wrong. Sometimes the + * compiler will give you a hint. + * + * ____________ + * < Good luck! > + * ------------ + * \ ^__^ + * \ (oo)\_______ + * (__)\ )\/\ + * ||----w | + * || || + */ + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + + +static int func_counter = 1; +#define FUNC_START() printf("\n\n%d.) %s\n", func_counter++, __func__); + +#pragma GCC diagnostic ignored "-Waggressive-loop-optimizations" +#pragma GCC diagnostic ignored "-Wdangling-pointer" +#pragma GCC diagnostic ignored "-Wformat-overflow" + +/** + * This code example was taken from the book 'Mastering C Pointers,' one of the + * not so good ways to learn pointers. It was trying to demonstrate how to print + * 'BNGULAR'... with pointers...? Maybe? + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * String literals are stored in read only memory + * '66' in this example is 'B'. What we can do is set a[0] = 'B' to replace the first char in the string. + */ +void +angular(void) +{ + FUNC_START(); + + char a[] = "ANGULAR"; + a[0] = 'B'; + printf("%s\n", a); +} + +/** + * This function is the next step after 'Hello world' -- it takes user input and + * prints it back out! (Wow). + * + * But, unfortunately, it doesn't work. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * The main problem for this one is that we have to allocate space + for the string. I tried to change gets to fgets but I don't think + that works because using stdio.h didn't work. + + */ +void +greeter(void) +{ + FUNC_START(); + + char name[128]; + + printf("Please enter your name: "); + gets(name, 128); + + // Remove newline character + char *p = name; + for ( ; *p != '\n' && *p != 0; p++) { } + *p = '\0'; + + printf("Hello, %s!\n", name); +} + +/** + * This code isn't so much broken, but more of an exercise in understanding how + * C data types are represented in memory. + * + * (1) Fill in your guesses below to see if you can get all 6 correct on the + * first try. + * (2) Explain *WHY* you get the sizes you do for each of the 6 entries. + * sizeof(int) and sizeof(float) are fairly straightforward. + + A char is always 1. Both ints and floats are 4. + things is 48 because it is an array of 12 ints. so 12 x 4 = 48 + 'A' is an int so thats why its 4 bytes. + "A" is a string literal so it is 2 bytes. The null terminator "\0" is included in there. + * + */ +#define SIZE_CHECK(sz, guess) (sz), sz == guess ? "Right!" : "Wrong!" +void +sizer(void) +{ + FUNC_START(); + + int guesses[] = {1, 4, 4, 48, 4, 2 }; // fill in 6 guesses here + if (sizeof(guesses) / sizeof(int) != 6) { + printf("Please enter a guess for each of the sizes below.\n"); + printf("Example: guesses[] = { 1, 4, 0, 0, 0, 0 }\n"); + printf("would mean sizeof(char) == 1, sizeof(int) == 4, and so on"); + return; + } + + int things[12] = { 0 }; + printf("sizeof(char) = %ld\t[%s]\n", SIZE_CHECK(sizeof(char), guesses[0])); + printf("sizeof(int) = %ld\t[%s]\n", SIZE_CHECK(sizeof(int), guesses[1])); + printf("sizeof(float) = %ld\t[%s]\n", SIZE_CHECK(sizeof(float), guesses[2])); + printf("sizeof(things) = %ld\t[%s]\n", SIZE_CHECK(sizeof(things), guesses[3])); + printf("sizeof('A') = %ld\t[%s]\n", SIZE_CHECK(sizeof('A'), guesses[4])); + printf("sizeof(\"A\") = %ld\t[%s]\n", SIZE_CHECK(sizeof("A"), guesses[5])); +} + +/** + * This 'useful' function prints out an array of integers with their indexes, or + * at least tries to. It even has a special feature where it adds '12' to the + * array. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * One problem I found is that the loop is until < 1000, which exceeds the size we set for + the stuff array which is 100. + Another problem or inconvinience is the 14[stuff + 1] = 12. At first I didn't understand it, + but after searching online what it means. It basically just sets 12 to index 15. + */ +void +displayer(void) +{ + FUNC_START(); + + int stuff[100] = { 0 }; + + /* Can you guess what the following does without running the program? */ + /* Rewrite it so it's easier to read. */ + stuff[15] = 12; + + for (int i = 0; i < 100; ++i) { + printf("%d: %d\n", i, stuff[i]); + } +} + +/** + * Adds up the contents of an array and prints the total. Unfortunately the + * total is wrong! See main() for the arguments that were passed in. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * The problem is that sizeof(arr) doesn't work as intended. It wouldn't get to the + end of the loop. To fix it I changed the function call so we can get the correct size of + array is use that to loop through. + */ +void +adder(int *arr, int n) +{ + FUNC_START(); + + int total = 0; + + for (int i = 0; i < n; ++i) { + total += arr[i]; + } + + printf("Total is: %d\n", total); +} + +/** + * This function is supposed to be somewhat like strcat, but it doesn't work. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * The problem is that it returns a pointer to a local array. When the function returns, it finishes. + To fix we use malloc to allocate it on the heap. + * + */ +char * +suffixer(char *a, char *b) +{ + FUNC_START(); + + char *buf = malloc(strlen(a) + strlen(b) + 1); + if (!buf) return 0; + + char *buf_start = buf; + + strcpy(buf, a); + strcpy(buf + strlen(a), b); + return buf_start; + +} + +/** + * This is an excerpt of Elon Musk's Full Self Driving code. Unfortunately, it + * keeps telling people to take the wrong turn. Figure out how to fix it, and + * explain what's going on so Elon can get back to work on leaving Earth for + * good. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * One problem is that in the if statement we aren't checking if its actually + true or not. So we have to add == 0 for it to work. The whole string comparison + part is also not needed. + + There is also 2 overflow issues. + */ +void +driver(void) +{ + FUNC_START(); + + char street1[10] = { "fulton" }; + char street2[10] = { "gomery" }; + char street3[10] = { "piedmont" }; + char street4[10] = { "baker" }; + char street5[10] = { "haight" }; + + if (strcmp(street1, street2) == 0) { + char *new_name = "saint agnew "; + memcpy(street4, new_name, strlen(new_name)); + } + + printf("Welcome to TeslaOS 0.1!\n"); + printf("Calculating route...\n"); + printf("Turn left at the intersection of %s and %s.\n", street5, street3); +} + +/** + * This function tokenizes a string by space, sort of like a basic strtok or + * strsep. It has two subtle memory bugs for you to find. + * + * (1) Fix the problem. + * (2) Explain what's wrong with this code: + * + * One problem was is a size bug. the malloc allocates a byte too few + so strcpy(line, str) overflows. + Once the scan hits '\0', if it advances it would go past the buffer. + + */ +void +tokenizer(void) +{ + FUNC_START(); + + char *str = "Hope was a letter I never could send"; + char *line = malloc(strlen(str) + 1); + char *base = line; + char *c = line; + + strcpy(line, str); + + while (*c != '\0') { + + for ( ; *c != ' ' && *c != '\0'; c++) { + // find the next space (or end of string) + } + + if (*c == '\0') { + printf("%s\n", line); + break; + } + + *c = '\0'; + printf("%s\n", line); + + line = c + 1; + c = line; + } + + free(base); +} + +/** +* This function should print one thing you like about C, and one thing you +* dislike about it. Assuming you don't mess up, there will be no bugs to find +* here! +*/ +void +finisher(void) +{ + FUNC_START(); + + // TODO + printf("Something I like about C is how you can control much more about how everything works.\n"); + printf("Something I don't like is that having a lot more control means more stuff can go wrong :( "); +} + +int +main(void) +{ + printf("Starting up!"); + + + angular(); + + greeter(); + + sizer(); + + displayer(); + + int nums[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 }; + adder(nums, sizeof(nums) / sizeof(nums[0])); + + char *result = suffixer("kayak", "ing"); + printf("Suffixed: %s\n", result); + + driver(); + + tokenizer(); + + finisher(); + + return 0; +} diff --git a/user/catlines.c b/user/catlines.c new file mode 100644 index 0000000..9de4315 --- /dev/null +++ b/user/catlines.c @@ -0,0 +1,40 @@ +#include "kernel/fcntl.h" +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +fgets7(char *buf, int max, int fd) +{ + int i, cc; + char c; + + for (i = 0; i + 1 < max; ) { + cc = read(fd, &c, 1); + if(cc < 1) + break; + buf[i++] = c; + if(c == '\n' || c == '\r') + break; + } + buf[i] = '\0'; + return i; +} + +int +main(int argc, char *argv[]) +{ + if (argc <= 1) { + fprintf(2, "Usage: %s filename\n", argv[0]); + return 1; + } + + int fd = open(argv[1], O_RDONLY); + char buf[128]; + int line_count = 0; + while (fgets7(buf, 128, fd) > 0 ) { + printf("Line %d: %s", line_count++, buf); + } + + return 0; +} diff --git a/user/clear.c b/user/clear.c new file mode 100644 index 0000000..b54b651 --- /dev/null +++ b/user/clear.c @@ -0,0 +1,8 @@ +#include "kernel/types.h" +#include "user/user.h" + +int +main(void) { + write(1, "\x1b[2J\x1b[H", 7); + exit(0); +} diff --git a/user/clock.c b/user/clock.c new file mode 100644 index 0000000..627a700 --- /dev/null +++ b/user/clock.c @@ -0,0 +1,13 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(void) +{ + uint64 ns = clock(); // call the clock function + uint64 sec64 = ns / 1000000000ULL; // convert to seconds + printf("%d\n", (int)sec64); + exit(0); + +} diff --git a/user/compress.c b/user/compress.c new file mode 100644 index 0000000..ee64323 --- /dev/null +++ b/user/compress.c @@ -0,0 +1,178 @@ +// user/compress.c — Run-Length Encoding (RLE) compressor/decompressor for FogOSv2 +// Format: stream of pairs [count:1 byte][value:1 byte], repeated to EOF. +// Runs >255 are split into multiple pairs. + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "kernel/fcntl.h" +#include "user/user.h" + +#define INBUF 512 +#define OUTBUF 128 + +static void usage(void) { + printf( + "usage:\n" + " compress -c [in] [out] # compress\n" + " compress -d [in] [out] # decompress\n" + "If in/out omitted, stdin/stdout are used.\n" + ); +} + +static int open_in(const char *p) { + if (!p) return 0; // stdin + int fd = open(p, O_RDONLY); + if (fd < 0) { + printf("compress: cannot open %s\n", p); + } + return fd; +} + +static int open_out(const char *p) { + if (!p) return 1; // stdout + int fd = open(p, O_CREATE | O_WRONLY); + if (fd < 0) { + printf("compress: cannot create %s\n", p); + } + return fd; +} + +/* -------------------- RLE compress -------------------- */ +static int rle_compress_fd(int in, int out) { + uchar buf[INBUF]; + int have_prev = 0; + uchar prev = 0; + int cnt = 0; + + for (;;) { + int n = read(in, buf, sizeof(buf)); + if (n < 0) { printf("compress: read error\n"); return -1; } + if (n == 0) break; + + for (int i = 0; i < n; i++) { + uchar b = buf[i]; + if (!have_prev) { + prev = b; cnt = 1; have_prev = 1; + } else if (b == prev && cnt < 255) { + cnt++; + } else { + uchar pair[2] = { (uchar)cnt, prev }; + if (write(out, pair, 2) != 2) { printf("compress: write error\n"); return -1; } + prev = b; cnt = 1; + } + } + } + + if (have_prev) { + uchar pair[2] = { (uchar)cnt, prev }; + if (write(out, pair, 2) != 2) { printf("compress: write error\n"); return -1; } + } + return 0; +} + +/* ------------------ RLE decompress -------------------- */ +static int rle_decompress_fd(int in, int out) { + uchar buf[INBUF]; + int carry = -1; // -1 = no carry; otherwise holds a pending count byte + uchar outbuf[OUTBUF]; + + for (int k = 0; k < OUTBUF; k++) outbuf[k] = 0; // init once + + for (;;) { + int n = read(in, buf, sizeof(buf)); + if (n < 0) { printf("compress: read error\n"); return -1; } + if (n == 0) break; + + int i = 0; + + // If we had an odd byte (count) from the previous read, pair it now. + if (carry != -1) { + if (n < 1) { printf("compress: corrupt input (missing value)\n"); return -1; } + int cnt = carry; + uchar val = buf[0]; + if (cnt <= 0) { printf("compress: corrupt input (zero/neg count)\n"); return -1; } + + // emit cnt copies of val + for (int j = 0; j < OUTBUF; j++) outbuf[j] = val; + int left = cnt; + while (left > 0) { + int chunk = left < OUTBUF ? left : OUTBUF; + if (write(out, outbuf, chunk) != chunk) { printf("compress: write error\n"); return -1; } + left -= chunk; + } + i = 1; // consumed one byte (value) + carry = -1; // cleared + } + + // Process full pairs from this buffer + for (; i + 1 < n; i += 2) { + int cnt = buf[i]; + uchar val = buf[i+1]; + if (cnt <= 0) { printf("compress: corrupt input (zero/neg count)\n"); return -1; } + + for (int j = 0; j < OUTBUF; j++) outbuf[j] = val; + int left = cnt; + while (left > 0) { + int chunk = left < OUTBUF ? left : OUTBUF; + if (write(out, outbuf, chunk) != chunk) { printf("compress: write error\n"); return -1; } + left -= chunk; + } + } + + // If there's one byte left, it's a dangling count; carry it to next read + if (i < n) { + carry = buf[n-1]; + } + } + + if (carry != -1) { // ended with odd number of bytes → corrupt + printf("compress: corrupt input (odd byte count)\n"); + return -1; + } + return 0; +} + +/* ------------------------ main ------------------------ */ +int main(int argc, char *argv[]) { + if (argc < 2) { usage(); exit(1); } + + int mode = 0; // 1 = compress, 2 = decompress + const char *inpath = 0; // NULL -> stdin + const char *outpath = 0; // NULL -> stdout + + // Parse: first flag (-c/-d), then up to 2 positional args + for (int i = 1; i < argc; i++) { + char *a = argv[i]; + if (strcmp(a, "-c") == 0) { + mode = 1; + } else if (strcmp(a, "-d") == 0) { + mode = 2; + } else if (!inpath) { + inpath = a; + } else if (!outpath) { + outpath = a; + } else { + printf("compress: too many arguments\n"); + usage(); + exit(1); + } + } + + if (mode == 0) { + printf("compress: must specify -c or -d\n"); + usage(); + exit(1); + } + + int in = open_in(inpath); + if (in < 0) exit(1); + int out = open_out(outpath); + if (out < 0) { if (in > 1) close(in); exit(1); } + + int rc = (mode == 1) ? rle_compress_fd(in, out) : rle_decompress_fd(in, out); + + if (in > 1) close(in); + if (out > 1) close(out); + if (rc < 0) exit(1); + exit(0); +} diff --git a/user/compress_test.c b/user/compress_test.c new file mode 100644 index 0000000..f4b4b6f --- /dev/null +++ b/user/compress_test.c @@ -0,0 +1,130 @@ +// It creates small input files, runs `compress -c` then `compress -d` via +// fork/exec, and verifies the round-trip output matches the original. + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "kernel/fcntl.h" +#include "user/user.h" + +#define IN_CHUNK 512 + +static int write_file(const char *path, const void *buf, int len) { + int fd = open(path, O_CREATE | O_WRONLY); + if (fd < 0) { printf("compress_test: open(O_CREATE) %s failed\n", path); return -1; } + const char *p = (const char*)buf; + int left = len; + while (left > 0) { + int n = write(fd, p, left); + if (n < 0) { printf("compress_test: write %s failed\n", path); close(fd); return -1; } + left -= n; p += n; + } + close(fd); + return 0; +} + +static int files_equal(const char *a, const char *b) { + int fa = open(a, O_RDONLY); + int fb = open(b, O_RDONLY); + if (fa < 0 || fb < 0) { if (fa>=0) close(fa); if (fb>=0) close(fb); return 0; } + + char ba[IN_CHUNK], bb[IN_CHUNK]; + for (;;) { + int na = read(fa, ba, sizeof(ba)); + int nb = read(fb, bb, sizeof(bb)); + if (na < 0 || nb < 0) { close(fa); close(fb); return 0; } + if (na != nb) { close(fa); close(fb); return 0; } + if (na == 0) { close(fa); close(fb); return 1; } // both EOF + for (int i = 0; i < na; i++) if (ba[i] != bb[i]) { close(fa); close(fb); return 0; } + } +} + +static int run_prog(char *prog, char *argv[]) { + int pid = fork(); + if (pid < 0) { printf("compress_test: fork failed\n"); return -1; } + if (pid == 0) { + // child + exec(prog, argv); + printf("compress_test: exec %s failed\n", prog); + exit(127); + } + int status = 0; + if (wait(&status) < 0) { + // Some FogOS variants support wait(&status); others allow wait(0). + // If your toolchain complains, change to: wait(0); + } + return status; +} + +static int do_roundtrip(const char *label, const char *infile, + const char *rlefile, const char *outfile) +{ + // compress -c infile rlefile + char *argv_c[] = { "compress", "-c", (char*)infile, (char*)rlefile, 0 }; + int st1 = run_prog("compress", argv_c); + if (st1 != 0) { printf("[FAIL] %s: compress step failed (status=%d)\n", label, st1); return -1; } + + // decompress -d rlefile outfile + char *argv_d[] = { "compress", "-d", (char*)rlefile, (char*)outfile, 0 }; + int st2 = run_prog("compress", argv_d); + if (st2 != 0) { printf("[FAIL] %s: decompress step failed (status=%d)\n", label, st2); return -1; } + + // compare + if (!files_equal(infile, outfile)) { + printf("[FAIL] %s: round-trip mismatch\n", label); + return -1; + } + + printf("[PASS] %s\n", label); + return 0; +} + +static void make_repeating(char *buf, int len, char byte) { + for (int i = 0; i < len; i++) buf[i] = byte; +} + +int +main(void) +{ + int rc = 0; + + // --- Test 1: small text --- + const char *t1_in = "t1_in.txt"; + const char *t1_rle = "t1.rle"; + const char *t1_out = "t1_out.txt"; + const char *msg = "Hello RLE!\nHello RLE!\nAAABBBCCCDDD\n"; + if (write_file(t1_in, msg, strlen(msg)) < 0) rc = -1; + if (rc == 0 && do_roundtrip("small text", t1_in, t1_rle, t1_out) < 0) rc = -1; + + // --- Test 2: empty file --- + const char *t2_in = "t2_in.bin"; + const char *t2_rle = "t2.rle"; + const char *t2_out = "t2_out.bin"; + if (rc == 0 && write_file(t2_in, "", 0) < 0) rc = -1; + if (rc == 0 && do_roundtrip("empty file", t2_in, t2_rle, t2_out) < 0) rc = -1; + + // --- Test 3: long run (>255) --- + const char *t3_in = "t3_in.bin"; + const char *t3_rle = "t3.rle"; + const char *t3_out = "t3_out.bin"; + char big[1000]; + make_repeating(big, sizeof(big), 'A'); + if (rc == 0 && write_file(t3_in, big, sizeof(big)) < 0) rc = -1; + if (rc == 0 && do_roundtrip("long run 1000x'A'", t3_in, t3_rle, t3_out) < 0) rc = -1; + + // --- Test 4: binary with zero bytes --- + const char *t4_in = "t4_in.bin"; + const char *t4_rle = "t4.rle"; + const char *t4_out = "t4_out.bin"; + char bin[256]; + for (int i = 0; i < 256; i++) bin[i] = (char)i; // 0x00..0xFF once + if (rc == 0 && write_file(t4_in, bin, sizeof(bin)) < 0) rc = -1; + if (rc == 0 && do_roundtrip("binary 0x00..0xFF", t4_in, t4_rle, t4_out) < 0) rc = -1; + + if (rc == 0) { + printf("\nAll tests PASSED\n"); + exit(0); + } else { + printf("\nSome tests FAILED\n"); + exit(1); + } +} diff --git a/user/fnr.c b/user/fnr.c new file mode 100644 index 0000000..140ecb2 --- /dev/null +++ b/user/fnr.c @@ -0,0 +1,29 @@ +#include "kernel/types.h" +#include "user/user.h" + +int main(int argc, char *argv[]) { + char *line = malloc(512); + while (getline(&line, 512, 0) > 0) { + for (int i = 1; i < argc; i += 2) { + char *find = argv[i]; + char *repl = argv[i + 1]; + uint find_len = strlen(find); + if (strlen(line) < find_len) { + continue; + } + for (int j = 0; j < strlen(line) - find_len; ++j) { + char *p = line + j; + char *q = find; + int k; + for (k = 0; *p && *p == *q && k < find_len; ++k) { + p++, q++; + } + if (k == find_len) { + memcpy(line + j, repl, find_len); + } + } + } + printf("%s", line); + } + return 0; +} diff --git a/user/freemem.c b/user/freemem.c new file mode 100644 index 0000000..0c87fb5 --- /dev/null +++ b/user/freemem.c @@ -0,0 +1,11 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(void) +{ + int k = freemem(); + printf("%d KiB\n", k); + exit(0); +} diff --git a/user/leetify.c b/user/leetify.c new file mode 100644 index 0000000..b18fc2b --- /dev/null +++ b/user/leetify.c @@ -0,0 +1,154 @@ +/** + * @file leetify.c + * + * Scaffolding to create an amazing l337speak generator using only external + * commands combined with pipelines. + */ + +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fcntl.h" + +/** + * Represents a command in a pipeline. + * Note that EITHER stdout_pipe can be true, or a stdout_file can be set. + * + * - tokens Array of strings that describe the command to run + * - stdout_pipe set to true if this command's output should be written to pipe + * - stdout_file set to a file name if this command's output should be written + * to a file + */ +struct command { + char **tokens; + int stdout_pipe; + char *stdout_file; +}; + +void +execute_pipeline(struct command *cmds, int n) +{ + + // go through all the commands + for (int i = 0; i < n - 1; i++) { + + int p[2]; // p[0] stdin, p[1] stdout + if (pipe(p) < 0) { + fprintf(2, "pipe failed\n"); + exit(1); + } + + int pid = fork(); + if (pid < 0) { + fprintf(2, "fork failed\n"); + exit(1); + } + + if (pid == 0) { + // child + + // redirect stdout to write end of pipe + close(p[0]); // close read + close(1); // redirect stdout -> pipe write end + dup(p[1]); + close(p[1]); + + // exec + exec(cmds[i].tokens[0], cmds[i].tokens); + + fprintf(2, "exec %s failed\n", cmds[i].tokens[0]); + exit(1); + } else { + // parent + + // close write end of pipe + close(p[1]); + + // redirect stdin + close(0); + dup(p[0]); + + // close copy of the read end + close(p[0]); + + } + } + struct command *final_command = &cmds[n-1]; + if (final_command->stdout_file) { + int fd = open(final_command->stdout_file, O_CREATE | O_WRONLY | O_TRUNC); + if (fd < 0) { + fprintf(2, "open failed for %s\n", final_command->tokens[0]); + exit(1); + } + close(1); + dup(fd); + close(fd); + } + + exec(final_command->tokens[0], final_command->tokens); + fprintf(2, "exec failed: %s\n", final_command->tokens[0]); + exit(1); + +} + +int +main(int argc, char *argv[]) +{ + char *input_file = (char *)0; + char *output_file = (char *) 0; + + if (argc < 2 || argc > 3) { + printf("Usage: %s file-to-leetify [output-file]\n", argv[0]); + return 1; + } + + input_file = argv[1]; + + if (argc == 3) { + output_file = argv[2]; + } + + printf("Input file: %s\n", input_file); + if (output_file) { + printf("Writing to output file: %s\n", output_file); + } + + // cat input file + char *command1[] = { "cat", input_file, (char *)0 }; + // tolower + char *command2[] = { "tolower", (char *)0 }; + // fnr + char *command3[] = { "fnr", "the", "teh", "a", "4", "e", "3", "i", "!", (char *)0 }; + // fnr 2 + char *command4[] = { "fnr", "l", "1", "o", "0", "s", "5", (char *)0 }; + + struct command cmds[4] = { 0 }; + cmds[0].tokens = command1; /* What this command runs */ + cmds[0].stdout_pipe = 1; + cmds[0].stdout_file = (char *)0; /* This command is not writing to a file. */ + + cmds[1].tokens = command2; + cmds[1].stdout_pipe = 1; + cmds[1].stdout_file = (char *)0; /* This command's output goes to a pipe. */ + + cmds[2].tokens = command3; + cmds[2].stdout_pipe = 1; + cmds[2].stdout_file = (char *)0; + + cmds[3].tokens = command4; + cmds[3].stdout_pipe = 0; /* Last command so set stdout_pipe = false */ + cmds[3].stdout_file = output_file; + + int pid = fork(); + if (pid == -1) { + fprintf(2, "Fork failed\n"); + return 1; + } else if (pid == 0) { + execute_pipeline(cmds, 4); + } else { + int status; + wait(&status); + } + + return 0; +} diff --git a/user/mmap.c b/user/mmap.c new file mode 100644 index 0000000..d3f8523 --- /dev/null +++ b/user/mmap.c @@ -0,0 +1,17 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(void) +{ + char *p = (char*)mmap(); + if (!p) { + printf("mmap failed\n"); + exit(1); + } + + strcpy(p, "hello from mmap"); + printf("%s\n", p); + exit(0); +} diff --git a/user/mmaptest.c b/user/mmaptest.c new file mode 100644 index 0000000..f3477d9 --- /dev/null +++ b/user/mmaptest.c @@ -0,0 +1,53 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(void) +{ + char *a = mmap(); + char *b = mmap(); + char *c = mmap(); + + strcpy(a, "a: hello"); + strcpy(b, "b: world"); + strcpy(c, "c: original"); + + printf("parent original:\n%s\n%s\n%s\n\n", a, b, c); + + + int pid = fork(); + + if(pid == 0){ + + printf("child sees after fork:\n%s\n%s\n%s\n\n", a, b, c); + + // child modifies first two pages + strcpy(a, "a: spurs"); + strcpy(b, "b: child changed world"); + + // forks and makes another child (grandchild) + int pid2 = fork(); + if(pid2 == 0){ + printf("grandchild sees:\n%s\n%s\n%s\n\n", a, b, c); + + // grandchild modifies the 3rd page + strcpy(c, "C: grandchild updated this"); + + printf("grandchild done.\n"); + exit(0); + } + + // child waits for grandchild to finish updating page C + wait(0); + + printf("child after grandchild:\n%s\n%s\n%s\n\n", a, b, c); + exit(0); + } + + wait(0); + + printf("parent final:\n%s\n%s\n%s\n\n", a, b, c); + + exit(0); +} diff --git a/user/mt70.c b/user/mt70.c new file mode 100644 index 0000000..4776483 --- /dev/null +++ b/user/mt70.c @@ -0,0 +1,49 @@ +#include "kernel/types.h" +#include "user/user.h" + +#pragma GCC diagnostic ignored "-Wunused-variable" + +int main(void) +{ +// malloc_setfsm(FSM_FIRST_FIT); + + char *a = malloc(132); + char *b = malloc(42); /* Will be deleted */ + char *c = malloc(132); + char *d = malloc(132); /* Will be deleted */ + char *e = malloc(132); + char *f = malloc(3132); + + /* These allocations total 4096 bytes after alignment is included. */ + + malloc_name(a, "A"); + malloc_name(b, "B"); + malloc_name(c, "C"); + malloc_name(d, "D"); + malloc_name(e, "E"); + malloc_name(f, "F"); + + free(b); + free(d); + + /** + * The following allocation will choose: + * - First fit: free space at the end of the page (unnamed, no variable) + * - Best fit: B + * - Worst fit: D + */ + char *g = malloc(42); + + malloc_name(g, "G"); + + malloc_print(); + + /** + * Things to check: + * - correct alignment (evenly divisible by 16) + * - free block sizes are correct (make sure not off by one) + * - end of last block's memory address - first address = 4096 + */ + + return 0; +} diff --git a/user/mt80.c b/user/mt80.c new file mode 100644 index 0000000..4776483 --- /dev/null +++ b/user/mt80.c @@ -0,0 +1,49 @@ +#include "kernel/types.h" +#include "user/user.h" + +#pragma GCC diagnostic ignored "-Wunused-variable" + +int main(void) +{ +// malloc_setfsm(FSM_FIRST_FIT); + + char *a = malloc(132); + char *b = malloc(42); /* Will be deleted */ + char *c = malloc(132); + char *d = malloc(132); /* Will be deleted */ + char *e = malloc(132); + char *f = malloc(3132); + + /* These allocations total 4096 bytes after alignment is included. */ + + malloc_name(a, "A"); + malloc_name(b, "B"); + malloc_name(c, "C"); + malloc_name(d, "D"); + malloc_name(e, "E"); + malloc_name(f, "F"); + + free(b); + free(d); + + /** + * The following allocation will choose: + * - First fit: free space at the end of the page (unnamed, no variable) + * - Best fit: B + * - Worst fit: D + */ + char *g = malloc(42); + + malloc_name(g, "G"); + + malloc_print(); + + /** + * Things to check: + * - correct alignment (evenly divisible by 16) + * - free block sizes are correct (make sure not off by one) + * - end of last block's memory address - first address = 4096 + */ + + return 0; +} diff --git a/user/mt90.c b/user/mt90.c new file mode 100644 index 0000000..c126462 --- /dev/null +++ b/user/mt90.c @@ -0,0 +1,90 @@ +#include "kernel/types.h" +#include "user/user.h" + +#pragma GCC diagnostic ignored "-Wunused-variable" + +void +merge_test(void) +{ + char *start = sbrk(0); + printf("Starting address: %p\n\n", start); + + void *a = malloc(532); malloc_name(a, "A"); + void *b = malloc(326); malloc_name(b, "B"); + void *c = malloc(282); malloc_name(c, "C"); + void *d = malloc(1032); malloc_name(d, "D"); + void *e = malloc(432); malloc_name(e, "E"); + + free(b); + free(c); + free(a); + free(d); + free(e); + + malloc_print(); + + printf("\n\n"); + + void *f = malloc(632); malloc_name(f, "F"); + void *g = malloc(182); malloc_name(f, "G"); + void *h = malloc(9928); malloc_name(f, "H"); + + free(f); + free(g); + free(h); + + malloc_print(); + + char *end = sbrk(0); + printf("\n\nEnding address: %p\n", end); + + if (start == end) { + printf("Success! start == end!\n"); + } +} + +void +realloc_test(void) +{ + char *a = malloc(532); malloc_name(a, "A"); + void *b = malloc(326); malloc_name(b, "B"); + void *c = malloc(282); malloc_name(c, "C"); + void *d = malloc(1032); malloc_name(d, "D"); + void *e = malloc(432); malloc_name(e, "E"); + + /* Does not actually need to be resized */ + a = realloc(a, 542); malloc_name(a, "R1"); + + /* No room, must free + allocate new */ + c = realloc(c, 9632); malloc_name(c, "R2"); + + /* Now that 'c' is free, we can expand 'b' into it */ + b = realloc(b, 632); malloc_name(b, "R3"); + + /* Expand into free space at the end of the region. This will produce a new + * free block out of the remaining space */ + e = realloc(e, 1312); malloc_name(e, "R4"); + + /* Shrink 'd' down to make a new free block */ + d = realloc(d, 832); malloc_name(d, "R5"); + + /* Shrink 'd' one more time to make sure that the two neighboring free + * blocks get merged back together */ + d = realloc(d, 632); malloc_name(d, "R6"); + + malloc_print(); +} + +int +main(void) +{ + printf("---- MERGE TEST ----\n"); + merge_test(); + printf("\n\n"); + + printf("---- REALLOC TEST ----\n"); + realloc_test(); + printf("\n\n"); + + return 0; +} diff --git a/user/nice.c b/user/nice.c new file mode 100644 index 0000000..fe0b3e6 --- /dev/null +++ b/user/nice.c @@ -0,0 +1,32 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int main(int argc, char *argv[]) { + if (argc < 3) { + fprintf(2, "usage: nice <0..3> cmd [args...]\n"); + exit(1); + } + int n = atoi(argv[1]); + if (n < 0) + n = 0; + if (n > 3) + n = 3; + + int pid = fork(); + + if (pid < 0) { + fprintf(2, "nice: fork failed\n"); + exit(1); + } + + if (pid == 0) { + nice(n); + exec(argv[2], &argv[2]); + fprintf(2, "nice: exec %s failed\n", argv[2]); + exit(1); + } + + wait(0); + exit(0); +} diff --git a/user/opt_cat.c b/user/opt_cat.c new file mode 100644 index 0000000..ed4b763 --- /dev/null +++ b/user/opt_cat.c @@ -0,0 +1,59 @@ +#include "kernel/fcntl.h" +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +static int +fgets3(char *out, int max, int fd) +{ + enum { CAP = 8192 }; + static char storage[CAP]; + static int r = 0, w = 0; + int outi = 0; + + for (;;) { + if (r >= w) { + r = w = 0; + int n = read(fd, storage, CAP); + if (n <= 0) { + if (outi == 0) return 0; + out[outi] = '\0'; + return outi; + } + w = n; + } + + while (r < w && outi + 1 < max) { + char c = storage[r++]; + out[outi++] = c; + if (c == '\n' || c == '\r') { + out[outi] = '\0'; + return outi; + } + } + + if (outi + 1 >= max) { + out[outi] = '\0'; + return outi; + } + } +} + + +int +main(int argc, char *argv[]) +{ + if (argc <= 1) { + fprintf(2, "Usage: %s filename\n", argv[0]); + return 1; + } + + int fd = open(argv[1], O_RDONLY); + char buf[128]; + int line_count = 0; + while (fgets3(buf, 128, fd) > 0 ) { + printf("Line %d: %s", line_count++, buf); + } + + return 0; +} diff --git a/user/pwd.c b/user/pwd.c new file mode 100644 index 0000000..165ab8b --- /dev/null +++ b/user/pwd.c @@ -0,0 +1,11 @@ +#include "kernel/types.h" +#include "user/user.h" + +int +main(void) +{ + char buf[128]; + getcwd(buf, 128); + printf("%s\n", buf); + return 0; +} diff --git a/user/reboot.c b/user/reboot.c new file mode 100644 index 0000000..a44da64 --- /dev/null +++ b/user/reboot.c @@ -0,0 +1,14 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char **argv) +{ + printf("Rebooting...\n"); + reboot(); + + // error message just in case + fprintf(2, "reboot failed!\n"); + exit(1); +} diff --git a/user/sh.c b/user/sh.c index 278fb37..f5454c6 100644 --- a/user/sh.c +++ b/user/sh.c @@ -77,6 +77,14 @@ runcmd(struct cmd *cmd) if(ecmd->argv[0] == 0) exit(1); exec(ecmd->argv[0], ecmd->argv); + + if (strchr(ecmd->argv[0], '/') == 0) { + char buf[128]; + buf[0] = '/'; + strcpy(buf+1, ecmd->argv[0]); + exec(buf, ecmd->argv); + } + fprintf(2, "exec %s failed\n", ecmd->argv[0]); break; @@ -132,9 +140,12 @@ runcmd(struct cmd *cmd) } int -getcmd(char *buf, int nbuf) +getcmd(char *buf, int nbuf, int is_script) { - write(2, "$ ", 2); + if (!is_script) { + write(2, "$ ", 2); + } + memset(buf, 0, nbuf); gets(buf, nbuf); if(buf[0] == 0) // EOF @@ -143,10 +154,11 @@ getcmd(char *buf, int nbuf) } int -main(void) +main(int argc, char *argv[]) { static char buf[100]; int fd; + int is_script = 0; // flag to check if we are running a script // Ensure that three file descriptors are open. while((fd = open("console", O_RDWR)) >= 0){ @@ -156,12 +168,22 @@ main(void) } } + if (argc > 1) { + is_script = 1; + close(0); // we close stdin + if (open(argv[1], O_RDONLY) < 0) { + fprintf(2, "sh: cannot open %s\n", argv[1]); + exit(1); + } + } + + // Read and run input commands. - while(getcmd(buf, sizeof(buf)) >= 0){ + while(getcmd(buf, sizeof(buf), is_script) >= 0){ char *cmd = buf; while (*cmd == ' ' || *cmd == '\t') - cmd++; - if (*cmd == '\n') // is a blank command + cmd++; + if (*cmd == '\n' || *cmd == '#') // is a blank command continue; if(cmd[0] == 'c' && cmd[1] == 'd' && cmd[2] == ' '){ // Chdir must be called by the parent, not the child. diff --git a/user/shell.c b/user/shell.c new file mode 100644 index 0000000..f315f55 --- /dev/null +++ b/user/shell.c @@ -0,0 +1,389 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" +#include "kernel/fcntl.h" + +int fgets(char *buf, unsigned int max, int fd); + +uint +strspn(const char *str, const char *chars) +{ + uint i, j; + for (i = 0; str[i] != '\0'; i++) { + for (j = 0; chars[j] != str[i]; j++) { + if (chars[j] == '\0') + return i; + } + } + return i; +} + +uint +strcspn(const char *str, const char *chars) +{ + const char *p, *sp; + char c, sc; + for (p = str;;) { + c = *p++; + sp = chars; + do { + if ((sc = *sp++) == c) { + return (p - 1 - str); + } + } while (sc != 0); + } +} + +char +*next_token(char **str_ptr, const char *delim) +{ + if (*str_ptr == 0) { + return 0; + } + + uint tok_start = strspn(*str_ptr, delim); + uint tok_end = strcspn(*str_ptr + tok_start, delim); + + /* Zero length token. We must be finished. */ + if (tok_end == 0) { + *str_ptr = 0; + return 0; + } + + /* Take note of the start of the current token. We'll return it later. */ + char *current_ptr = *str_ptr + tok_start; + + /* Shift pointer forward (to the end of the current token) */ + *str_ptr += tok_start + tok_end; + + if (**str_ptr == '\0') { + /* If the end of the current token is also the end of the string, we + * must be at the last token. */ + *str_ptr = 0; + } else { + /* Replace the matching delimiter with a NUL character to terminate the + * token string. */ + **str_ptr = '\0'; + + /* Shift forward one character over the newly-placed NUL so that + * next_pointer now points at the first character of the next token. */ + (*str_ptr)++; + } + + return current_ptr; +} + +// helper func to just print the basic prompt +static void print_prompt(int last_status, int cmd_num) { + char path[128]; + int n = getcwd(path, sizeof(path)); + if (n < 0) { + path[0] = '/'; path[1] = 0; + } + printf("[%d]-[%d]-[%s]$ ", last_status, cmd_num, path); +} + +static int is_blank(const char *s) { + for (; *s; s++) { + if (*s!= ' ' && *s!= '\t' && *s!= '\n' && *s!= '\r') return 0; + } + return 1; +} + +// helper fun to remove remove newliens and return characters +static void strip_trailing_newline(char *buf, int *len_io) { + int len = *len_io; + while (len > 0 && (buf[len-1] == '\n' || buf[len-1] == '\r')) { + buf[--len] = 0; + } + *len_io = len; +} + +// helper to check for comments and remove them +static void remove_comment(char *buf) { + for (int i = 0; buf[i]; i++) { + if (buf[i] == '#') { + buf[i] = 0; + break; + } + } +} + +#define MAX_HIST 100 +struct history_entry { + int num; + char line[256]; + int duration_ms; +}; + +static struct history_entry hist[MAX_HIST]; + +// method to add to the history +static void add_history(const char *line, int cmd_num) { + if (!line || !*line) return; + int slot = (cmd_num - 1) % MAX_HIST; + hist[slot].num = cmd_num; + + int i = 0; + for (; i < (int)sizeof(hist[slot].line) - 1 && line[i]; i++) { + hist[slot].line[i] = line[i]; + } + hist[slot].line[i] = 0; +} + +// actual history command +static void history_command(int current_cmd_num, int show_time) { + int last_done = current_cmd_num - 1; // last completed command + if (last_done < 1) return; + + int first = last_done - MAX_HIST + 1; + if (first < 1) first = 1; + + for (int n = first; n <= last_done; n++) { + int slot = (n - 1) % MAX_HIST; + if (hist[slot].num == n && hist[slot].line[0] != 0) { + if (show_time) { + printf("[%d|%dms] %s\n", hist[slot].num, hist[slot].duration_ms, hist[slot].line); + } else { + printf("%d %s\n", hist[slot].num, hist[slot].line); + } + } + } +} + +// lookup in history with number arg +static const char* hist_num(int num) { + if (num < 1) return 0; + int slot = (num - 1) % MAX_HIST; + + // make sure that everything matches and line isn't empty + if (hist[slot].num == num && hist[slot].line[0] != 0) return hist[slot].line; + + return 0; +} + +// lookup in history with prefix arg +static const char* hist_prefix(const char *prefix, int last_done) { + int prefix_len = 0; + while(prefix[prefix_len]) + prefix_len++; + + // go through all the commands + // from newest to oldest + for (int n = last_done; n >= 1; n--) { + int slot = (n - 1) % MAX_HIST; + + if (hist[slot].num != n || hist[slot].line[0] == 0) + continue; + + const char *s = hist[slot].line; + + // compare prefix against start of the command + int i = 0; + while (i < prefix_len && s[i] == prefix[i]) + i++; + + // if they match return the string + if (i == prefix_len) + return s; + } + + return 0; +} + +int +main(int argc, char *argv[]) +{ + + // -1. print a prompt + // 0. get the user's command (stdin) + // 1. fork + // 2. exec + + + int last_status = 0; // 0 = success, 1 = fail + int cmd_num = 1; + int scripting = 0; + int script_fd = -1; + + if (argc > 1) { + script_fd = open(argv[1], O_RDONLY); + if (script_fd < 0) { + fprintf(2, "shell: cannot open %s", argv[1]); + exit(1); + } + scripting = 1; + } + + + while (1) { + if (!scripting) { + print_prompt(last_status, cmd_num); + } + + char buf[256]; + + int r; + if (scripting) { + int i = 0; + while (i + 1 < sizeof(buf)) { + char c; + r = read(script_fd, &c, 1); + if (r < 1) { + break; + } + buf[i++] = c; + if (c == '\n') + break; + } + buf[i] = 0; + + if (i == 0) { + //fprintf(2, "shell: script EOF, exiting\n"); + exit(0); + } + } else { + if (gets(buf, sizeof(buf)) == 0) { + printf("\n"); + exit(0); + } + } + + + int got = strlen(buf); + + // remove "\n" and comments + strip_trailing_newline(buf, &got); + remove_comment(buf); + + // skip blank lines + if (is_blank(buf)) { + continue; + } + + /* + Extra history commands: + 1. "!" -> last command + 2. "!5" -> command number 5 + 3. "!prefix" -> most recent command with the prefix + */ + + if (buf[0] == '!') { + int last_done = cmd_num - 1; + const char *expanded = 0; + + // the prev command + if (buf[1] == '!' && buf[2] == 0) { + expanded = hist_num(last_done); + } else if (buf[1] >= '0' && buf[1] <= '9') { + int n = 0; + for (int i = 1; buf[i] >= '0' && buf[i] <= '9'; i++) { + n = n * 10 + (buf[i] - '0'); + } + expanded = hist_num(n); + } else { + // !prefix + expanded = hist_prefix(buf + 1, last_done); + } + + if (!expanded) { + fprintf(2, "history: no match for '%s'\n", buf); + last_status = 1; + cmd_num++; + continue; + } + + + int i = 0; + while (i < (int)sizeof(buf) - 1 && expanded[i]) { + buf[i] = expanded[i]; + i++; + } + buf[i] = 0; + + printf("%s\n", buf); + } + + // record this command before executing so numbers align + add_history(buf, cmd_num); + + + char *args[32]; + int tokens = 0; + char *next_tok = buf; + char *curr_tok; + /* Tokenize. */ + while ((curr_tok = next_token(&next_tok, " \n\t\r")) != 0 && tokens < 31) { + // printf("Token: '%s'\n", curr_tok); + args[tokens++] = curr_tok; + } + args[tokens] = 0; + if (tokens == 0) { + continue; + } + + + // built in commands + if (strcmp(args[0], "exit") == 0) { + exit(0); + } + + // history command + if (strcmp(args[0], "history") == 0) { + int show_time = 0; + if (tokens > 1 && strcmp(args[1], "-t") == 0) + show_time = 1; + history_command(cmd_num, show_time); + last_status = 0; + cmd_num++; + continue; + } + + + if (strcmp(args[0], "cd") == 0) { + if (tokens < 2) { + fprintf(2, "chdir: missing arg\n"); + last_status = 1; + } else if (chdir(args[1]) < 0) { + fprintf(2, "chdir: no file or directory found: %s\n", args[1]); + last_status = 1; + } else { + last_status = 0; + } + cmd_num++; + continue; + } + + uint64 start = clock(); + + int pid = fork(); + if (pid < 0 ) { + fprintf(2, "Fork failed"); + last_status = 1; + cmd_num++; + continue; + + } else if (pid == 0) { + // child + // execute the command + exec(args[0], args); + + fprintf(2, "exec failed: %s\n", args[0]); + exit(1); + + } else { + wait(0); + + uint64 end = clock(); + uint64 diff = end - start; + int diff_ms = (int)(diff / 1000000ULL); + + int slot = (cmd_num - 1) % MAX_HIST; + hist[slot].duration_ms = diff_ms; + + cmd_num++; + } + } + + return 0; +} diff --git a/user/shutdown.c b/user/shutdown.c new file mode 100644 index 0000000..418275a --- /dev/null +++ b/user/shutdown.c @@ -0,0 +1,14 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char **argv) +{ + printf("Shutting down...\n"); + shutdown(); + + // error message just in case + fprintf(2, "shutdown failed!\n"); + exit(1); +} diff --git a/user/spinner.c b/user/spinner.c new file mode 100644 index 0000000..0c70fd8 --- /dev/null +++ b/user/spinner.c @@ -0,0 +1,28 @@ +#include "kernel/types.h" +#include "user/user.h" + +int +main(int argc, char *argv[]) +{ + if (argc <= 2) { + fprintf(2, "Usage: spinner name amount\n"); + exit(1); + } + + char *name = argv[1]; + int amount = atoi(argv[2]); + + int start = uptime(); + for (int i = 0; i < amount; ++i) { + printf("%s", name); + for (int j = 0; j < 10000000; ++j) { + // wheee :-) + } + } + int end = uptime(); + + printf("\n[%s] has finished, %d work in %d ticks.\n", + name, amount, end - start); + + exit(0); +} diff --git a/user/strace.c b/user/strace.c new file mode 100644 index 0000000..f7a6cb8 --- /dev/null +++ b/user/strace.c @@ -0,0 +1,21 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +int +main(int argc, char *argv[]) { + if(argc < 2) { + fprintf(2, "Usage: tracer command...\n"); + exit(1); + } + + if (fork() == 0) { + strace_on(); + exec(argv[1], argv + 1); + fprintf(2, "tracer: exec %s failed\n", argv[1]); + exit(1); + } + wait(0); + + exit(0); +} diff --git a/user/tolower.c b/user/tolower.c new file mode 100644 index 0000000..55eb0a5 --- /dev/null +++ b/user/tolower.c @@ -0,0 +1,12 @@ +#include "kernel/types.h" +#include "user/user.h" + +int main() { + for (int c = 0; read(0, &c, 1) > 0; ) { + if (c >= 'A' && c <= 'Z') { + c = c - 'A' + 'a'; + } + write(1, &c, 1); + } + return 0; +} diff --git a/user/ulib.c b/user/ulib.c index a2f5a20..82b1b3a 100644 --- a/user/ulib.c +++ b/user/ulib.c @@ -158,3 +158,69 @@ sbrklazy(int n) { return sys_sbrk(n, SBRK_LAZY); } +// Reads contents of given file ('fd') to 'buf' of size 'max'. +// Returns character's read, excluding null terminator. +int +fgets(char *buf, unsigned int max, int fd) +{ + int i, cc; + char c; + + for(i=0; i+1 < max; ){ + cc = read(fd, &c, 1); + if(cc < 1) { + return -1; + } + buf[i++] = c; + if(c == '\n' || c == '\r'){ + break; + } + } + + buf[i] = '\0'; + return i; +} + +// Like fgets, but dynamically allocates correct memory +// to store the string. +// Caller provides pointer to a malloced 'buf' and capacity ('size'). +// 'buf' points to NULL, a new buffer is allocated. +// Returns characters read, excluding null terminator. +int +getline(char **buf, int size, int fd) +{ + int chars_read = 0; + int curr_read; + + // initilize buffer if not already initilized (size = 2, malloc space for a char + /0) + if (*buf == 0 || size < 2) { + free(*buf); // in case buf was 1 (if null, does nothing) + *buf = malloc(2); + size = 2; + } + + while ((curr_read = fgets(*buf + chars_read, size - chars_read, fd))) { + + if (curr_read < 0) { // error in fgets + return - 1; + } + + chars_read += curr_read; + + //resize + char *temp = malloc(size * 2); + memcpy(temp, *buf, chars_read + 1); // include null term + free(*buf); + *buf = temp; + size = size * 2; + + // break if end of line/eof + char *last_char = *buf + (chars_read - 1); + if (strcmp(last_char, "\n") == 0 || strcmp(last_char, "\r") == 0) { + break; + } + } + + return chars_read; +} + diff --git a/user/umalloc.c b/user/umalloc.c index 2ac6894..16c4bf3 100644 --- a/user/umalloc.c +++ b/user/umalloc.c @@ -3,88 +3,423 @@ #include "user/user.h" #include "kernel/param.h" -// Memory allocator by Kernighan and Ritchie, -// The C programming Language, 2nd ed. Section 8.7. +#define NULL 0 +#define PAGE_SIZE 4096 +#define MIN_SPLIT_BLOCK_SIZE (sizeof(struct mem_block) + 16) -typedef long Align; -union header { - struct { - union header *ptr; - uint size; - } s; - Align x; +#define NAME_LEN 16 + +#define BLOCK_HEADER_SIZE sizeof(struct mem_block) + + +/* If we haven't passed -DDEBUG=1 to gcc, then this will be set to 0: */ +#ifndef DEBUG +#define DEBUG 1 +#endif + + +// Real allocator block metadata +struct mem_block { + char name[8]; + uint64 size; + struct mem_block *next; + struct mem_block *prev; }; -typedef union header Header; -static Header base; -static Header *freep; +struct mem_block *head; +struct mem_block *tail; + +// Free space management algorithms +enum { + FSM_FIRST_FIT = 0, + FSM_BEST_FIT = 1, + FSM_WORST_FIT = 2, +}; + +static int current_fsm = FSM_FIRST_FIT; + +// Our memory: +// +----------+ <--- base +// | | +// | | +// | | <--- bound +// | | +// | | +// | | +// | | +// | | +// | | +// | | +// +----------+ +// +// malloc -> ask kernel for more memory +// free -> tell kernel to shrink our memory +// +// "Hello Kernel, can I have 32 bytes of space please?" (sbrk) +// -> allocate 1 page of memory +// -> hand us 32 bytes of it +// "Hello Kernel, can I have 32 bytes of space please?" (sbrk) +// -> it would be bad to allocate another page here +// maybe the previous thing is not used anymore +// maybe we want to make use of the 4096 - 32 bytes left from the first +// page +// +// +// So, our allocator is going to allocate *pages* of memory at a time. +// One page can hold multiple blocks of memory: +// +-------------------------------------------+ +// | | | | | | | 1 page (4096 bytes) +// +-------------------------------------------+ +// ^ ^ ^ ^ ^ + +// The 'size' of a block: +// 1101 .... 0010 0000 0001 <- free +// 1101 .... 1110 0000 0000 <- used +// +// Why we can do this: +// - We are going to round everything up so that sizes are multiples of 16 +// - That means the smallest size we can have is 16 (0001 0000) +// - Note how the last 4 bits are never used... so we store the free bit there! + +// Get the block header from a data pointer +struct mem_block* get_header(void* ptr) { + return (struct mem_block*)((char*)ptr - BLOCK_HEADER_SIZE); +} + +static uint +align16(uint size) +{ + return (size + 15) & ~0x0F; +} + +// +void set_used(struct mem_block *block) +{ + // turn off the least significant bit + block->size = block->size & (~0x01); +} + +void set_free(struct mem_block *block) +{ + // turn on the least significant bit + block->size = block->size | 0x01; +} + +int is_free(struct mem_block *block) +{ + // check whether the last bit is set or not + return block->size & 0x01; +} + +uint get_size(struct mem_block *block) +{ + // ignore the free bit when retrieving the size + return block->size & (~0x01); +} + +/* Given an existing block, this function should split it in two */ +// How this works: +// Maybe we allocate 1 byte +// That gets turned into 16 (that's our minimum size) +// But then that gets turned into 4096 (our page size) +struct mem_block * +split(struct mem_block *block, uint total_sz) +{ + if (block == NULL) { + return NULL; + } + + if (!is_free(block)) { + return NULL; + } + + uint old_size = get_size(block); + + // Don't split if remainder would be too small + if (old_size <= total_sz + MIN_SPLIT_BLOCK_SIZE) { + return NULL; + } + + char *base = (char *) block; + struct mem_block *new_block = (struct mem_block *) (base + total_sz); + + uint remainder_size = old_size - total_sz; + + new_block->size = remainder_size; + set_free(new_block); + + new_block->next = block->next; + new_block->prev = block; + + if (block->next != NULL) { + block->next->prev = new_block; + } else { + // if block was tail, new_block becomes new tail + tail = new_block; + } + + block->size = total_sz; + set_used(block); + block->next = new_block; + + return new_block; +} + +// We have some space like this: +// +-----------------------------+ +// | mem_block | data | +// +-----------------------------+ +// ^ ^ +// | | +// | +--- actual data pointer (for user space programs) +// | +// +----- header +// + +// find first free whose size is big enough +static struct mem_block* +find_free_first_fit(uint total_sz) +{ + struct mem_block *curr = head; + + while (curr != NULL) { + if (is_free(curr) && get_size(curr) >= total_sz) { + return curr; + } + curr = curr->next; + } + return NULL; +} + +// find free block with smallest size >= total_sz +static struct mem_block* +find_free_best_fit(uint total_sz) +{ + struct mem_block *curr = head; + struct mem_block *best = NULL; + + while (curr != NULL) { + if (is_free(curr) && get_size(curr) >= total_sz) { + if (best == NULL || get_size(curr) < get_size(best)) { + best = curr; + } + } + curr = curr->next; + } + + return best; +} + +static struct mem_block* +find_free_worst_fit(uint total_sz) +{ + struct mem_block *curr = head; + struct mem_block *worst = NULL; + + while (curr != NULL) { + if (is_free(curr) && get_size(curr) >= total_sz) { + if (worst == NULL || get_size(curr) > get_size(worst)) { + worst = curr; + } + } + curr = curr->next; + } + return worst; +} + +static struct mem_block * +find_free_block(uint total_sz) +{ + switch (current_fsm) { + case FSM_BEST_FIT: + return find_free_best_fit(total_sz); + case FSM_WORST_FIT: + return find_free_worst_fit(total_sz); + case FSM_FIRST_FIT: + default: + return find_free_first_fit(total_sz); + } +} + + +void * +malloc(uint size) +{ + if (size == 0) { + return NULL; + } + + uint aligned = align16(size); + uint total_sz = BLOCK_HEADER_SIZE + aligned; + + // try to find a suitable free block first + struct mem_block *block = find_free_block(total_sz); + if (block != NULL) { + // take space from this free block + split(block, total_sz); + set_used(block); + return (void *)(block + 1); + } + + // no suitable free block, request a new chunk from the OS + uint request = total_sz; + if (request < PAGE_SIZE) { + request = PAGE_SIZE; + } + + block = (struct mem_block *) sbrk(request); + if ((long)block == -1) { + return NULL; + } + + // treat the whole new region as a single free block + block->size = request; + set_free(block); + block->next = NULL; + block->prev = tail; + + if (head == NULL) { + head = block; + tail = block; + } else { + tail->next = block; + tail = block; + } + + // now carve out the requested block from this new free block + struct mem_block *alloc_block = block; + split(alloc_block, total_sz); + set_used(alloc_block); + + return (void *)(alloc_block + 1); +} +// linked list helpers: +// * add a new block +// * remove a block +// * AND make sure you can handle both the (1) block list, and (2) free list void -free(void *ap) -{ - Header *bp, *p; - - bp = (Header*)ap - 1; - for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) - if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) - break; - if(bp + bp->s.size == p->s.ptr){ - bp->s.size += p->s.ptr->s.size; - bp->s.ptr = p->s.ptr->s.ptr; - } else - bp->s.ptr = p->s.ptr; - if(p + p->s.size == bp){ - p->s.size += bp->s.size; - p->s.ptr = bp->s.ptr; - } else - p->s.ptr = bp; - freep = p; -} - -static Header* -morecore(uint nu) -{ - char *p; - Header *hp; - - if(nu < 4096) - nu = 4096; - p = sbrk(nu * sizeof(Header)); - if(p == SBRK_ERROR) - return 0; - hp = (Header*)p; - hp->s.size = nu; - free((void*)(hp + 1)); - return freep; -} - -void* -malloc(uint nbytes) -{ - Header *p, *prevp; - uint nunits; - - nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; - if((prevp = freep) == 0){ - base.s.ptr = freep = prevp = &base; - base.s.size = 0; +free(void *ptr) +{ + + if (ptr == NULL) { + return; } - for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){ - if(p->s.size >= nunits){ - if(p->s.size == nunits) - prevp->s.ptr = p->s.ptr; - else { - p->s.size -= nunits; - p += p->s.size; - p->s.size = nunits; - } - freep = prevp; - return (void*)(p + 1); - } - if(p == freep) - if((p = morecore(nunits)) == 0) - return 0; + + // ptr points to the user data; header is right before that + struct mem_block *block = ((struct mem_block *)ptr) -1; + + if (is_free(block)) { + return; } + + set_free(block); + +} + +// This is great because we can allocate arrays of things easily +// And they're zeroed out +// This function is perfect and never needs to be changed +void * +calloc(uint nmemb, uint size) +{ + char *mem = malloc(nmemb * size); + if (mem == NULL) { + return NULL; + } + memset(mem, 0, nmemb * size); + return mem; +} + +// takes a pointer to a previous allocation and resizes it (shrink / grow) +void * +realloc(void *ptr, uint size) +{ + if (ptr == NULL) { + return malloc(size); + } + + if (size == 0) { + free(ptr); + return NULL; + } + + struct mem_block *old_block = ((struct mem_block *) ptr) - 1; + uint old_total_size = old_block->size; + uint old_payload_size = old_total_size - sizeof(struct mem_block); + + void *new_ptr = malloc(size); + if (new_ptr == NULL) { + return NULL; + } + + uint copy_size = old_payload_size < size ? old_payload_size : size; + memmove(new_ptr, ptr, copy_size); + + free(ptr); + + return new_ptr; +} + +void +malloc_print(void) +{ + struct mem_block *curr; + + printf("--Current Memory State --\n"); + + curr = head; + while (curr != NULL) { + char *start = (char *)curr; + char *end = start + get_size(curr); + const char *status = is_free(curr) ? "FREE" : "USED"; + + printf("[BLOCK %p-%p] %d\t[%s]\n", + start, + end, + get_size(curr), + status); + + curr = curr->next; + } + + printf("\n-- Free List --\n"); + + curr = head; + int first = 1; + while (curr != NULL) { + if (is_free(curr)) { + if (!first) { + printf(" -> "); + } + printf("[%p]", curr); + first = 0; + } + curr = curr->next; + } + if (!first) { + printf(" -> "); + } + printf("NULL\n"); +} + +void +malloc_setfsm(int algo) +{ + if (algo == FSM_FIRST_FIT || + algo == FSM_BEST_FIT || + algo == FSM_WORST_FIT) { + current_fsm = algo; + } +} + +void malloc_name(void *ptr, const char *name) { + struct mem_block *block = get_header(ptr); + int i; + for (i = 0; i < 7 && name[i] != 0; i++) { + block->name[i] = name[i]; + } + block->name[i] = 0; } diff --git a/user/user.h b/user/user.h index ac84de9..2a2b379 100644 --- a/user/user.h +++ b/user/user.h @@ -5,25 +5,34 @@ struct stat; // system calls int fork(void); int exit(int) __attribute__((noreturn)); -int wait(int*); -int pipe(int*); -int write(int, const void*, int); -int read(int, void*, int); -int close(int); -int kill(int); -int exec(const char*, char**); -int open(const char*, int); -int mknod(const char*, short, short); -int unlink(const char*); -int fstat(int fd, struct stat*); -int link(const char*, const char*); -int mkdir(const char*); -int chdir(const char*); -int dup(int); +int wait(int *status); +int pipe(int *pipefd); +int write(int fd, const void *buf, int count); +int read(int fd, void *buf, int count); +int close(int fd); +int kill(int pid); +int exec(const char* path, char **argv); +int open(const char*/*str*/ path, int mode); +int mknod(const char* /*str*/ path, short major, short minor); +int unlink(const char* /*str*/ path); +int fstat(int fd, struct stat* st); +int link(const char* /*str*/old_path, const char* /*str*/ new_path); +int mkdir(const char* /*str*/ path); +int chdir(const char* /*str*/ path); +int dup(int fd); int getpid(void); -char* sys_sbrk(int,int); +char* sys_sbrk(int n, int incr); int pause(int); int uptime(void); +int shutdown(void); +int reboot(void); +int clock(void); +int strace_on(void); +int wait2(int *status, int *counter); +int nice(int n); +int getcwd(char *, int); +int freemem(void); +void* mmap(void); // ulib.c int stat(const char*, struct stat*); @@ -39,6 +48,7 @@ int memcmp(const void *, const void *, uint); void *memcpy(void *, const void *, uint); char* sbrk(int); char* sbrklazy(int); +int getline(char **buf, int sizep, int fd); // printf.c void fprintf(int, const char*, ...) __attribute__ ((format (printf, 2, 3))); @@ -47,3 +57,8 @@ void printf(const char*, ...) __attribute__ ((format (printf, 1, 2))); // umalloc.c void* malloc(uint); void free(void*); +void* calloc(uint, uint); +void* realloc(void*, uint); +void malloc_name(void *ptr, const char *name); +void malloc_print(void); +void malloc_setfsm(int); diff --git a/user/usys.pl b/user/usys.pl old mode 100755 new mode 100644 index c5d4c3a..983c857 --- a/user/usys.pl +++ b/user/usys.pl @@ -42,3 +42,12 @@ sub entry { entry("sbrk"); entry("pause"); entry("uptime"); +entry("shutdown"); +entry("reboot"); +entry("clock"); +entry("strace_on"); +entry("wait2"); +entry("nice"); +entry("getcwd"); +entry("freemem"); +entry("mmap");