| | 359 | |
| | 360 | == Lezione 4 == |
| | 361 | |
| | 362 | === Slide === |
| | 363 | |
| | 364 | * '''Turno 1 (25 Marzo 2009)''': [http://homes.dico.unimi.it/sisop/lucidi0809/solab04.pdf slide] ([http://homes.dico.unimi.it/sisop/lucidi0809/solab04-handout.pdf Versione stampa]) |
| | 365 | |
| | 366 | === Programmi === |
| | 367 | |
| | 368 | ==== Esempi con la syscall `clone`, specifica di Linux ==== |
| | 369 | |
| | 370 | * Thread '''senza''' memoria condivisa `threads-isolated.c` |
| | 371 | |
| | 372 | {{{ |
| | 373 | #!c |
| | 374 | #include <unistd.h> |
| | 375 | #include <stdio.h> |
| | 376 | #include <stdlib.h> |
| | 377 | #include <signal.h> |
| | 378 | #include <sched.h> |
| | 379 | |
| | 380 | int run(void* s) |
| | 381 | { |
| | 382 | int* shared = (int*)s; /* alias per comodita` */ |
| | 383 | while (shared[0] < 10) { |
| | 384 | sleep(1); |
| | 385 | printf("Processo figlio (%d). s = %d\n", |
| | 386 | getpid(), shared[0]); |
| | 387 | if (!(shared[0] < 10)){ |
| | 388 | printf("Corsa critica!!!!\n"); |
| | 389 | abort(); |
| | 390 | } |
| | 391 | shared[0] += 1; |
| | 392 | } |
| | 393 | return 0; |
| | 394 | } |
| | 395 | |
| | 396 | |
| | 397 | int main(void){ |
| | 398 | |
| | 399 | int shared[2] = {0 , 0}; |
| | 400 | |
| | 401 | /* int clone(int (*fn)(void *), |
| | 402 | * void *child_stack, |
| | 403 | * int flags, |
| | 404 | * void *arg); |
| | 405 | * crea una copia del chiamante (con le caratteristiche |
| | 406 | * specificate da flags) e lo esegue partendo da fn */ |
| | 407 | if (clone(run, /* il nuovo |
| | 408 | * processo esegue run(shared), vedi quarto |
| | 409 | * parametro */ |
| | 410 | malloc(4096)+4096, /* lo stack del nuovo processo |
| | 411 | * (cresce verso il basso!) */ |
| | 412 | SIGCHLD, /* in questo caso la clone e` analoga alla fork */ |
| | 413 | shared) < 0){ |
| | 414 | perror("Errore nella creazione"); |
| | 415 | exit(1); |
| | 416 | } |
| | 417 | |
| | 418 | if (clone(run, malloc(4096)+4096, SIGCHLD, shared) < 0){ |
| | 419 | perror("Errore nella creazione"); |
| | 420 | exit(1); |
| | 421 | } |
| | 422 | |
| | 423 | /* Isolati: ciascuno dei figli esegue 10 volte. Per il padre |
| | 424 | * shared[0] e` sempre 0 */ |
| | 425 | |
| | 426 | while(1) { |
| | 427 | sleep(1); |
| | 428 | printf("Processo padre. s = %d\n", shared[0]); |
| | 429 | } |
| | 430 | return 0; |
| | 431 | } |
| | 432 | }}} |
| | 433 | |
| | 434 | * Thread '''con''' memoria condivisa `threads-shared.c` |
| | 435 | |
| | 436 | {{{ |
| | 437 | #!c |
| | 438 | #include <unistd.h> |
| | 439 | #include <stdio.h> |
| | 440 | #include <stdlib.h> |
| | 441 | #include <signal.h> |
| | 442 | #include <sched.h> |
| | 443 | |
| | 444 | int run(void* s) |
| | 445 | { |
| | 446 | int* shared = (int*)s; /* alias per comodita` */ |
| | 447 | while (shared[0] < 10) { |
| | 448 | sleep(1); |
| | 449 | printf("Processo figlio (%d). s = %d\n", |
| | 450 | getpid(), shared[0]); |
| | 451 | if (!(shared[0] < 10)){ |
| | 452 | printf("Corsa critica!!!!\n"); |
| | 453 | abort(); |
| | 454 | } |
| | 455 | shared[0] += 1; |
| | 456 | } |
| | 457 | return 0; |
| | 458 | } |
| | 459 | |
| | 460 | |
| | 461 | int main(void){ |
| | 462 | |
| | 463 | int shared[2] = {0 , 0}; |
| | 464 | |
| | 465 | /* int clone(int (*fn)(void *), |
| | 466 | * void *child_stack, |
| | 467 | * int flags, |
| | 468 | * void *arg); |
| | 469 | * crea una copia del chiamante (con le caratteristiche |
| | 470 | * specificate da flags) e lo esegue partendo da fn */ |
| | 471 | if (clone(run, /* il nuovo |
| | 472 | * processo esegue run(shared), vedi quarto |
| | 473 | * parametro */ |
| | 474 | malloc(4096)+4096, /* lo stack del nuovo processo |
| | 475 | * (cresce verso il basso!) */ |
| | 476 | CLONE_VM | SIGCHLD, /* la (virtual) memory e` condivisa */ |
| | 477 | shared) < 0){ |
| | 478 | perror("Errore nella creazione"); |
| | 479 | exit(1); |
| | 480 | } |
| | 481 | |
| | 482 | if (clone(run, malloc(4096)+4096, CLONE_VM | SIGCHLD, shared) < 0){ |
| | 483 | perror("Errore nella creazione"); |
| | 484 | exit(1); |
| | 485 | } |
| | 486 | |
| | 487 | /* Memoria condivisa: i due figli nell'insieme eseguono 10 o |
| | 488 | * 11 volte: e` possibile una corsa critica. Il padre |
| | 489 | * condivide shared[0] con i figli */ |
| | 490 | |
| | 491 | while(1) { |
| | 492 | sleep(1); |
| | 493 | printf("Processo padre. s = %d\n", shared[0]); |
| | 494 | } |
| | 495 | return 0; |
| | 496 | } |
| | 497 | }}} |
| | 498 | |
| | 499 | * Thread '''con''' memoria condivisa, mutua esclusione ottenuta con Peterson `threads-peterson.c` |
| | 500 | |
| | 501 | {{{ |
| | 502 | #!c#include <unistd.h> |
| | 503 | #include <stdio.h> |
| | 504 | #include <stdlib.h> |
| | 505 | #include <signal.h> |
| | 506 | #include <sched.h> |
| | 507 | |
| | 508 | |
| | 509 | void enter_section(int process, int* turn, int* interested) |
| | 510 | { |
| | 511 | int other = 1 - process; |
| | 512 | interested[process] = 1; |
| | 513 | *turn = process; |
| | 514 | while (*turn == process && interested[other]){ |
| | 515 | printf("Busy waiting di %d\n", process); |
| | 516 | } |
| | 517 | } |
| | 518 | |
| | 519 | void leave_section(int process, int* interested) |
| | 520 | { |
| | 521 | interested[process] = 0; |
| | 522 | } |
| | 523 | |
| | 524 | int run(const int p, void* s) |
| | 525 | { |
| | 526 | int* shared = (int*)s; /* alias per comodita` */ |
| | 527 | while (enter_section(p, &shared[1], &shared[2]), |
| | 528 | shared[0] < 10) { |
| | 529 | sleep(1); |
| | 530 | printf("Processo figlio (%d). s = %d\n", |
| | 531 | getpid(), shared[0]); |
| | 532 | if (!(shared[0] < 10)){ |
| | 533 | printf("Corsa critica!!!!\n"); |
| | 534 | abort(); |
| | 535 | } |
| | 536 | shared[0] += 1; |
| | 537 | leave_section(p, &shared[2]); |
| | 538 | } |
| | 539 | return 0; |
| | 540 | } |
| | 541 | }}} |
| | 542 | |
| | 543 | * Thread '''con''' memoria condivisa, mutua esclusione con TSL `threads-tsl.c`, `enter.asm` |
| | 544 | |
| | 545 | {{{ |
| | 546 | #!c |
| | 547 | #include <unistd.h> |
| | 548 | #include <stdio.h> |
| | 549 | #include <stdlib.h> |
| | 550 | #include <signal.h> |
| | 551 | #include <sched.h> |
| | 552 | |
| | 553 | |
| | 554 | void enter_section(int *s); /* in enter.asm */ |
| | 555 | |
| | 556 | void leave_section(int *s){ |
| | 557 | *s = 0; |
| | 558 | } |
| | 559 | |
| | 560 | int run(const int p, void* s) |
| | 561 | { |
| | 562 | int* shared = (int*)s; /* alias per comodita` */ |
| | 563 | while (enter_section(&shared[1]), |
| | 564 | shared[0] < 10) { |
| | 565 | sleep(rand() % 3); |
| | 566 | printf("Processo figlio (%d). s = %d\n", |
| | 567 | getpid(), shared[0]); |
| | 568 | if (!(shared[0] < 10)){ |
| | 569 | printf("Corsa critica!!!!\n"); |
| | 570 | abort(); |
| | 571 | } |
| | 572 | shared[0] += 1; |
| | 573 | leave_section(&shared[1]); |
| | 574 | } |
| | 575 | return 0; |
| | 576 | } |
| | 577 | |
| | 578 | int run0(void*s){ return run(0, s); } |
| | 579 | int run1(void*s){ return run(1, s); } |
| | 580 | |
| | 581 | |
| | 582 | int main(void){ |
| | 583 | |
| | 584 | int shared[4] = {0 , 0, 0, 0}; |
| | 585 | |
| | 586 | /* int clone(int (*fn)(void *), |
| | 587 | * void *child_stack, |
| | 588 | * int flags, |
| | 589 | * void *arg); |
| | 590 | * crea una copia del chiamante (con le caratteristiche |
| | 591 | * specificate da flags) e lo esegue partendo da fn */ |
| | 592 | if (clone(run0, /* il nuovo |
| | 593 | * processo esegue run(shared), vedi quarto |
| | 594 | * parametro */ |
| | 595 | malloc(4096)+4096, /* lo stack del nuovo processo |
| | 596 | * (cresce verso il basso!) */ |
| | 597 | CLONE_VM | SIGCHLD, /* la (virtual) memory e` condivisa */ |
| | 598 | shared) < 0){ |
| | 599 | perror("Errore nella creazione"); |
| | 600 | exit(1); |
| | 601 | } |
| | 602 | |
| | 603 | if (clone(run1, malloc(4096)+4096, CLONE_VM | SIGCHLD, shared) < 0){ |
| | 604 | perror("Errore nella creazione"); |
| | 605 | exit(1); |
| | 606 | } |
| | 607 | |
| | 608 | /* Memoria condivisa: i due figli nell'insieme eseguono 10 o |
| | 609 | * 11 volte: e` possibile una corsa critica. Il padre |
| | 610 | * condivide shared[0] con i figli */ |
| | 611 | |
| | 612 | while(shared[0] < 10) { |
| | 613 | sleep(1); |
| | 614 | printf("Processo padre. s = %d %d %d %d\n", |
| | 615 | shared[0], |
| | 616 | shared[1], |
| | 617 | shared[2], |
| | 618 | shared[3]); |
| | 619 | } |
| | 620 | return 0; |
| | 621 | } |
| | 622 | }}} |
| | 623 | |
| | 624 | {{{ |
| | 625 | section .text |
| | 626 | global enter_section |
| | 627 | |
| | 628 | enter_section: |
| | 629 | enter 0, 0 ; 0 bytes of local stack space |
| | 630 | mov ebx,[ebp+8] ; first parameter to function |
| | 631 | |
| | 632 | spin: lock bts dword [ebx], 0 |
| | 633 | jc spin |
| | 634 | |
| | 635 | leave ; mov esp,ebp / pop ebp |
| | 636 | ret |
| | 637 | }}} |
| | 638 | |
| | 639 | {{{ |
| | 640 | #!sh |
| | 641 | nasm -felf enter.asm |
| | 642 | cc threads-tsl.c enter.o -o threads-tsl |
| | 643 | }}} |
| | 644 | |
| | 645 | ==== Esempi in Java ==== |
| | 646 | |
| | 647 | * Creazione di thread `Basic.java` |
| | 648 | |
| | 649 | {{{ |
| | 650 | #!java |
| | 651 | class ClasseAttiva extends Thread{ |
| | 652 | public void run(){ |
| | 653 | while (true) { |
| | 654 | try { |
| | 655 | Thread.sleep(100); |
| | 656 | } |
| | 657 | catch(InterruptedException e){ |
| | 658 | System.err.println(e); |
| | 659 | } |
| | 660 | System.out.println(this.getName()); |
| | 661 | } |
| | 662 | } |
| | 663 | } |
| | 664 | |
| | 665 | public class Basic { |
| | 666 | public static final void main(final String[] args) { |
| | 667 | ClasseAttiva o1 = new ClasseAttiva(); |
| | 668 | ClasseAttiva o2 = new ClasseAttiva(); |
| | 669 | o1.start(); |
| | 670 | o2.start(); |
| | 671 | while (true){ |
| | 672 | try { |
| | 673 | Thread.sleep(100); |
| | 674 | } |
| | 675 | catch(InterruptedException e){ |
| | 676 | System.err.println(e); |
| | 677 | } |
| | 678 | |
| | 679 | System.out.println("Main thread"); |
| | 680 | } |
| | 681 | |
| | 682 | } |
| | 683 | } |
| | 684 | }}} |
| | 685 | |
| | 686 | * Memoria condivisa `Shared.java` |
| | 687 | |
| | 688 | {{{ |
| | 689 | #!java |
| | 690 | |
| | 691 | class ClasseAttiva extends Thread{ |
| | 692 | protected static int shared = 0; |
| | 693 | public void run() { |
| | 694 | while (true) { |
| | 695 | if (shared >= 10) break; |
| | 696 | try { |
| | 697 | Thread.sleep(1000); |
| | 698 | } |
| | 699 | catch(InterruptedException e){ |
| | 700 | System.err.println(e); |
| | 701 | } |
| | 702 | if (shared >= 10){ |
| | 703 | throw new Error("Corsa critica!!!"); |
| | 704 | } |
| | 705 | System.out.println(this.getName() + " s = " + shared); |
| | 706 | shared += 1; |
| | 707 | } |
| | 708 | } |
| | 709 | } |
| | 710 | |
| | 711 | |
| | 712 | |
| | 713 | public class Shared { |
| | 714 | public static final void main(final String[] args) { |
| | 715 | ClasseAttiva o1 = new ClasseAttiva(); |
| | 716 | ClasseAttiva o2 = new ClasseAttiva(); |
| | 717 | o1.start(); |
| | 718 | o2.start(); |
| | 719 | while (true){ |
| | 720 | try { |
| | 721 | Thread.sleep(1000); |
| | 722 | } |
| | 723 | catch(InterruptedException e){ |
| | 724 | System.err.println(e); |
| | 725 | } |
| | 726 | |
| | 727 | System.out.println("Main thread"); |
| | 728 | } |
| | 729 | |
| | 730 | } |
| | 731 | } |
| | 732 | }}} |
| | 733 | |
| | 734 | * Memoria condivisa, mutua esclusione ottenuta con `synchronized`, `Shared2.java` |
| | 735 | |
| | 736 | {{{ |
| | 737 | #!java |
| | 738 | class ClasseAttiva extends Thread{ |
| | 739 | protected static int shared = 0; |
| | 740 | protected static Object lock = new Object(); |
| | 741 | public void run() { |
| | 742 | while (true) { |
| | 743 | synchronized(lock){ |
| | 744 | if (shared >= 10) break; |
| | 745 | try { |
| | 746 | Thread.sleep(1000); |
| | 747 | } |
| | 748 | catch(InterruptedException e){ |
| | 749 | System.err.println(e); |
| | 750 | } |
| | 751 | if (shared >= 10){ |
| | 752 | throw new Error("Corsa critica!!!"); |
| | 753 | } |
| | 754 | System.out.println(this.getName() + " s = " + shared); |
| | 755 | shared += 1; |
| | 756 | } |
| | 757 | Thread.yield(); // non necessaria, ma favorisce lo scheduling di entrambi |
| | 758 | } |
| | 759 | } |
| | 760 | } |
| | 761 | |
| | 762 | |
| | 763 | |
| | 764 | public class Shared2 { |
| | 765 | public static final void main(final String[] args) { |
| | 766 | ClasseAttiva o1 = new ClasseAttiva(); |
| | 767 | ClasseAttiva o2 = new ClasseAttiva(); |
| | 768 | o1.start(); |
| | 769 | o2.start(); |
| | 770 | while (true){ |
| | 771 | try { |
| | 772 | Thread.sleep(1000); |
| | 773 | } |
| | 774 | catch(InterruptedException e){ |
| | 775 | System.err.println(e); |
| | 776 | } |
| | 777 | |
| | 778 | System.out.println("Main thread"); |
| | 779 | } |
| | 780 | |
| | 781 | } |
| | 782 | } |
| | 783 | }}} |
| | 784 | |
| | 785 | * Produttore e consumatore |
| | 786 | |
| | 787 | {{{ |
| | 788 | #!java |
| | 789 | |
| | 790 | class Actor extends Thread |
| | 791 | { |
| | 792 | public Actor(String nome){ |
| | 793 | super(nome); |
| | 794 | } |
| | 795 | |
| | 796 | private Magazzino shared; |
| | 797 | public final Magazzino getShared() { |
| | 798 | return shared; |
| | 799 | } |
| | 800 | public final void setShared(final Magazzino newShared) { |
| | 801 | this.shared = newShared; |
| | 802 | } |
| | 803 | |
| | 804 | } |
| | 805 | |
| | 806 | |
| | 807 | class Produttore extends Actor { |
| | 808 | public Produttore(String nome, Magazzino b) { |
| | 809 | super(nome); |
| | 810 | setShared(b); |
| | 811 | } |
| | 812 | |
| | 813 | public void run(){ |
| | 814 | int i = 0; |
| | 815 | while(true){ |
| | 816 | System.out.println(getName() + ": Inserisco " + i + " nel buffer"); |
| | 817 | getShared().put(i); |
| | 818 | i += 1; |
| | 819 | } |
| | 820 | } |
| | 821 | } |
| | 822 | |
| | 823 | class Consumatore extends Actor{ |
| | 824 | public Consumatore(String nome, Magazzino b) { |
| | 825 | super(nome); |
| | 826 | setShared(b); |
| | 827 | } |
| | 828 | public void run(){ |
| | 829 | while(true){ |
| | 830 | int i = getShared().get(); |
| | 831 | System.out.println(getName() + ": Estraggo " + i + " dal buffer"); |
| | 832 | |
| | 833 | } |
| | 834 | } |
| | 835 | } |
| | 836 | |
| | 837 | class Magazzino{ |
| | 838 | public final static int SIZE = 10; |
| | 839 | private int[] memory = new int[SIZE]; |
| | 840 | private int quanti = 0; |
| | 841 | |
| | 842 | public final int get() { |
| | 843 | String n = Thread.currentThread().getName(); |
| | 844 | synchronized(syncPC){ |
| | 845 | while(isVuoto()){ |
| | 846 | System.out.println(n + " ha tentato di leggere"); |
| | 847 | try { |
| | 848 | syncPC.wait(); |
| | 849 | } catch (InterruptedException e) { |
| | 850 | System.err.println(e); |
| | 851 | } |
| | 852 | } |
| | 853 | int ris = memory[--quanti]; |
| | 854 | if (quanti == SIZE - 1) syncPC.notifyAll(); |
| | 855 | System.out.println(n + " ha letto"); |
| | 856 | return ris; |
| | 857 | } |
| | 858 | } |
| | 859 | |
| | 860 | public final void put(final int newMemory) { |
| | 861 | String n = Thread.currentThread().getName(); |
| | 862 | synchronized(syncPC){ |
| | 863 | while (isPieno()){ |
| | 864 | System.out.println(n + " ha tentato di scrivere"); |
| | 865 | try { |
| | 866 | syncPC.wait(); |
| | 867 | } catch (InterruptedException e) { |
| | 868 | e.printStackTrace(System.err); |
| | 869 | } |
| | 870 | } |
| | 871 | memory[quanti++] = newMemory; |
| | 872 | if (quanti == 1) syncPC.notifyAll(); |
| | 873 | System.out.println(n + " ha scritto"); |
| | 874 | } |
| | 875 | } |
| | 876 | |
| | 877 | public final boolean isVuoto() { |
| | 878 | return quanti == 0; |
| | 879 | } |
| | 880 | |
| | 881 | public final boolean isPieno() { |
| | 882 | return quanti == SIZE; |
| | 883 | } |
| | 884 | |
| | 885 | private Object syncPC = new Object(); |
| | 886 | } |
| | 887 | |
| | 888 | |
| | 889 | public class PC { |
| | 890 | |
| | 891 | public static final void main(final String[] args) { |
| | 892 | Magazzino x = new Magazzino(); |
| | 893 | Produttore a1 = new Produttore("Aldo", x); |
| | 894 | Produttore a2 = new Produttore("Alberto", x); |
| | 895 | Consumatore b = new Consumatore("Barbara", x); |
| | 896 | a1.start(); |
| | 897 | b.start(); |
| | 898 | a2.start(); |
| | 899 | } |
| | 900 | |
| | 901 | } |
| | 902 | }}} |
| | 903 | |
| | 904 | |