| | 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 | |