| 413 | Le thread **`T0`** demande des lectures, `T1` prend le temps qui lui est donné jusqu'à l'IRQ du TIMER, je vous propose de lire le code en détail. |
| 414 | |
| 415 | **uapp/main.c** |
| 416 | |
| 417 | * Le code ci-dessous contient l'application. Nous avons 3 threads : `main`, `t0` et `t1`. |
| 418 | * `main` et `t1` se contente d'afficher des messages sur le TTY0 et d'attendre (une attente active pour ralentir l'affichage des messages). |
| 419 | * `t0` lit le clavier de TTY1 |
| 420 | |
| 421 | {{{#!c |
| 422 | /*--------------------------------------------------------------------------------*\ |
| 423 | _ ___ __ |
| 424 | | |__ /'v'\ / / \date 2022-02-22 |
| 425 | | / /( )/ _ \ \copyright 2021-2022 Sorbonne University |
| 426 | |_\_\ x___x \___/ https://opensource.org/licenses/MIT |
| 427 | |
| 428 | \*--------------------------------------------------------------------------------*/ |
| 429 | |
| 430 | #include <libc.h> |
| 431 | #include <thread.h> |
| 432 | |
| 433 | #define DELAY(n) for(int i=n;i--;) __asm__("nop"); |
| 434 | |
| 435 | thread_t t0, t1; |
| 436 | |
| 437 | void * t0_fun (void * arg) |
| 438 | { |
| 439 | char buf[64]; |
| 440 | for (int i = 0;; i++) { |
| 441 | fprintf (1, "entrez un truc : "); |
| 442 | fgets (buf, sizeof(buf), 1); |
| 443 | fprintf (1, "%s\n", buf); |
| 444 | } |
| 445 | return NULL; |
| 446 | } |
| 447 | |
| 448 | void * t1_fun (void * arg) |
| 449 | { |
| 450 | for (int i = 0;; i++) { |
| 451 | fprintf (0, "[%d] t1 is alive (%d) : %s\n", clock(), i, (char *)arg); |
| 452 | DELAY(1000000); |
| 453 | } |
| 454 | return NULL; |
| 455 | } |
| 456 | |
| 457 | int main (void) |
| 458 | { |
| 459 | thread_create (&t1, t1_fun, "bonjour"); |
| 460 | thread_create (&t2, t2_fun, NULL); |
| 461 | for (int i = 0;; i++) { |
| 462 | fprintf (0, "[%d] app is alive (%d)\n", clock(), i); |
| 463 | DELAY(1000000); |
| 464 | } |
| 465 | return 0; |
| 466 | } |
| 467 | }}} |
| 468 | |
| 469 | **ulib/libc.c** |
| 470 | |
| 471 | * La fonction `fgets()` est dans la **libc**, c'est une fonction bloquante du point de vue de l'utilisateur. Il l'appelle pour lire `count` caractères sur le TTY n°`tty` et les enregistre dans `buf`. |
| 472 | * `fgets()` demande 1 caractère à la fois et elle le revoit sur l'écran (c'est un ''loopback'') pour que l'utilisateur sache que son caractère a été pris en compte. |
| 473 | * Il y a quelques subtilités dues au fait que lorsque vous taper sur ''**enter''**, le clavier envoie deux caractères '\r' (`13` = ''carriage return'') et '\n' (`10` = ''line feed''), on jette `\r`. En outre, on gère le `back sapce` et le `delete` (à qui on donne le même comportement pour simplifier). Je vous laisse essayer de comprendre pour le plaisir. |
| 474 | * Quand `fgets()` appelle `read()`, cela fait l'appel système `SYSCALL_READ`. |
| 475 | |
| 476 | {{{#!c |
| 477 | int read(int fd, void *buf, int count) |
| 478 | { |
| 479 | return syscall_fct( fd, (int)buf, count, 0, SYSCALL_READ); |
| 480 | } |
| 481 | |
| 482 | int write(int fd, void *buf, int count) |
| 483 | { |
| 484 | return syscall_fct( fd, (int)buf, count, 0, SYSCALL_WRITE); |
| 485 | } |
| 486 | |
| 487 | int fgets (char *buf, int count, int tty) |
| 488 | { |
| 489 | // to make the backspace, we use ANSI codes : https://www.wikiwand.com/en/ANSI_escape_code |
| 490 | char *DEL = "\033[D \033[D"; // move left, then write ' ' and move left |
| 491 | int res = 0; |
| 492 | count--; // we need to add a NUL (0) char at the end |
| 493 | char c=0; |
| 494 | |
| 495 | while ((count != 0) && (c != '\n')) { // as long as we can or need to get a char |
| 496 | |
| 497 | read (tty, &c, 1); // read only 1 char |
| 498 | if (c == '\r') // if c is the carriage return (13) |
| 499 | read (tty, &c, 1); // get the next that is line feed (10) |
| 500 | |
| 501 | if ((c == 8)||(c == 127)) { // 8 = backspace, 127 = delete |
| 502 | if (res) { // go back in the buffer if possible |
| 503 | write (tty, DEL, 7); // erase current char |
| 504 | count++; // count is the remaining place |
| 505 | buf--; // but is the next address in buffer |
| 506 | res--; |
| 507 | } |
| 508 | continue; // ask for another key |
| 509 | } else |
| 510 | write (tty, &c, 1); // loop back to the tty |
| 511 | |
| 512 | *buf = c; // write the char into the buffer |
| 513 | buf++; // increments the writing pointer |
| 514 | count--; // decrements the remaining space |
| 515 | res++; // increments the read char |
| 516 | } |
| 517 | *buf = 0; // add a last 0 to end the string |
| 518 | |
| 519 | return res; // returns the number of char read |
| 520 | } |
| 521 | }}} |
| 522 | |
| 523 | **kernel/ksyscall.c** |
| 524 | |
| 525 | * Je ne met pas toutes les étapes de l'appel du gestionnaire de syscall, vous avez ici le vecteur de syscall qui montre bien que l'on appelle la fonction du noyau `tty_read()` dont vous avez le code un peu plus haut. |
| 526 | |
| 527 | {{{#!c |
| 528 | void *syscall_vector[] = { |
| 529 | [0 ... SYSCALL_NR - 1 ] = unknown_syscall, /* default function */ |
| 530 | [SYSCALL_EXIT ] = exit, |
| 531 | [SYSCALL_READ ] = tty_read, |
| 532 | [SYSCALL_WRITE ] = tty_write, |
| 533 | [SYSCALL_CLOCK ] = clock, |
| 534 | [SYSCALL_THREAD_CREATE] = thread_create_kernel, |
| 535 | [SYSCALL_THREAD_YIELD ] = thread_yield, |
| 536 | [SYSCALL_THREAD_EXIT ] = thread_exit, |
| 537 | [SYSCALL_SCHED_DUMP ] = sched_dump, |
| 538 | }; |
| 539 | }}} |
| 540 | |