Cours 5



next up previous
Next: Chapitre 6 Up: Cours systèmes Previous: Chapitre 4

Plan

  • initialisations, Typedef en C, lint
  • variables d'environnement en shell
  • Les devices très spéciaux: mem, kmem, null
  • Le processeur MIPS R2000
  • Mémoire virtuelle sur MIPS R2000
  • Pages: stratégies de remplacement
  • Changements d'adressages
  • Processus résidents ou swappés
  • Allocation des pages
  • Structures proc et user
  • Mode superviseur sur MIPS R2000
  • Appels système et commutation de contexte
  • Exercices.

  • Cours 5

    Les devices très spéciaux: mem, kmem, null

  • /dev/mem, /dev/kmem représentent la mémoire physique et la mémoire avec l'adressage du noyau. Exemple (dans ps.c):
            kmem = open("/dev/kmem"; 0);
            if (kmem < 0) {
                perror(kmemf);
                exit(1);
            }
    ...
    kget(dest, loc, size)
            char *dest;
            register unsigned loc;
            register unsigned size;
    {
            lseek(kmem, (off_t)loc, 0);
            if (read(kmem, dest, size) != size)
                printf("error reading kmem at %x\n", loc);
    
    }
    
  • /dev/null $==$ la poubelle
    % mf "\mode:=$MODE; mag:=$MAG; scrollmode; input $NAMË </dev/null
    

  • Cours 5

    Une architecture standard (von Neumann)

  • la mémoire contient instructions et données

  • les calculs se font à l'intérieur de registres de l'unité arithmétique et logique ULA (ALU en anglais)

  • le traffic entre mémoire et registres se fait par des instructions load et store

  • le compteur ordinal représente l'adresse mémoire de l'instruction en cours d'exécution (pc program counter)

  • un code condition CC donne le résultat de chaque instruction

  • des branchements (conditionnels en fonction du CC) font des ruptures de séquence dans l'exécution des intructions

  • un des registres sert de pointeur vers une pile qui sert au contrôle des appels de procédures ou fonctions

  • un mot d'état SR (status register) donne des informations sur le mode d'exécution (système ou utilisateur)

  • chaque instruction a un grand nombre de modes d'adressage possibles (immédiat, indirect à travers un registre, indirect avec déplacement court, indirect avec déplacement long, idem avec post incrémentation, etc).

  • Cours 5

    Un exemple d'architecture: le processeur R2000/R3000 des Dec Stations 3100 ou 5000

    TIT     .mips, RISC Architecture
    SLA     .Gerry Kane - MIPS Computer Systems, Inc.
            .Prentice Hall, 1987. -
    
  • temps d'une tâche = C * T * I où
    C = nombre de cycles par instruction,
    T = temps par cycle (vitesse de l'horloge),
    I = nombre d'intructions dans la tâche.

  • Le temps mis par une intruction dépend donc de C et T.

  • Le R2000 est une machine RISC (Reduced Instruction Set Computer). On veut donc C = 1. Pour y arriver:

    1. pipeline des instructions
    2. architecture des load et store
    3. load retardés
    4. branchements retardés

  • les seules opérations ALU ne peuvent être que des 2 formes:
        rd = rs op rt
        rd = rs op CONSTANTE(16 bits)
    
    rs, rt, rd sont 32 registres. Toute instruction tient sur 32 bits; 6 bits sont réservés pour désigner l'opération (code-op).

  • les seules instructions load et store font communiquer la mémoire et les registres. (On a toujours r0 = 0).

  • les branchements et sauts sont de la forme
        beq    rs, rt, OFFSET(16 bits)
        jump   CONSTANTE(26 bits)
    
    où pour les branchements, l'adresse est obtenue par pc + OFFSET * 4, et pour les sauts par
    pc & 0xf0000000) + (CONSTANTE} * 4).

  • Il y a 32 registres. Un exemple (obtenu par cc -S ps.c):
     # 238                 argc--, argv++;
        lw   $15, 72($sp)
        addu $24, $15, -1
        sw   $24, 72($sp)
        lw   $25, 76($sp)
        addu $8, $25, 4
        sw   $8, 76($sp)
    
     # 275                 if ((tptr = index(mytty,'y')) != 0)
        la   $4, mytty
        li   $5, 121
        jal  index
        sw   $2, tptr
        lw   $10, tptr
        beq  $10, 0, $44
    

  • Cours 5

    R2000/R3000

  • Pipeline: 1 instruction de retard sur load et branchements ou sauts ...

  • Ces instructions sont implantées en mémoire comme suit
    # 238                 argc--, argv++;
    0x40020c : lw $t7,72($sp)
    0x400210 : lw $t9,76($sp)
    0x400214 : addiu $t8,$t7,-1
    0x400218 : addiu $t0,$t9,4
    0x40021c : sw $t0,76($sp)
    0x400220 : sw $t8,72($sp)
    0x400224 : lw $t1,72($sp)
    
    # 275                 if ((tptr = index(mytty,'y')) != 0)
    0x40037c : lui $a0,4096
    0x400380 : addiu $a0,$a0,32272
    0x400384 : jal 0x405bf0 
    0x400388 : li $a1,121
    0x40038c : sw $v0,-30568($gp)
    0x400390 : lw $t4,-30568($gp)
    0x400394 : nop 
    0x400398 : beq $t4,$zero,0x4003b0 
    
    qu'on obtient sous gdb en tapant
    (gdb) info line 238
    (gdb) x/i
    

  • Cours 5

    Espaces d'adressage sur ds5000

  • sys/mips/mips/vmparam.h décrit les paramètres de la mémoire virtuelle dépendant de la ds5000

  • pour l'utilisateur
    kpr% nm -gn a.out
    00400170 T __start
    004001b4 T moncontrol
    ...
    100006c0 D nl
    10000b04 D _iob
    10000b60 D _ctype__
    100010a4 D sys_errlist
    ...
    10010768 B rpc_createerr
    10010780 B end
    
  • pour le système Unix
    kpr% nm -gn /vmunix
    8000007c A kstackflag
    80030000 T eprol
    80030000 T start
    ...
    80150f00 B end
    c0000000 A Sysbase
    c2a00000 A usrpt
    c363c000 A cabase
    c3640000 A kmembase
    c3640000 A calimit
    c4640000 A kmemlimit
    c4640000 A VA_forkutl
    ...
    ffffc000 A u
    

  • Cours 5

    Adressage -- bis

  • On remarque d'abord que la table des pages est matérielle dans le Vax, mais est, sur Mips, dans la mémoire normale à l'adresse usrpt (User Page table) dans l'espace du système. Sur Vax, comme les pages sont petites (512 octets), la table des pages est à 2 niveaux. Il y a aussi des TLBs pour accélerer la traduction.

  • Le système est résident en mémoire, 1.5 MO. (C'est trop) Il ne se sert pas du MMU (zone kseg0).

  • Les processus utilisateurs ont une partie en mémoire physique et une autre sur disque. Tout processus a une partie dans l'espace mémoire réservé aux utilisateurs (0 à 0x7fffffff) et une autre dans le système (table des pages, structures proc et u dans la zone kseg2).

  • Un processus utilisateur a sa zone text en 0x400000, sa zone donnée en 10000000, sa pile décroissant à partir de 0x7fffffff. Au bout de la pile, il y a une page de zone rouge pour tester les débordements.

  • Cours 5

    Protection des pages -- Partage

  • Les pages de text sont protégées en écriture.

  • Les pages de données et de pile sont modifiables. Sur la première modification, on positionne le bit dirty pour cette page, sur l'exception du TLB.

  • Toutes les autres pages (autres processus, système) sont inaccessibles.

  • On peut partager des pages entre processus, en les faisant correspondre à la même adresse physique. Il faut toutefois faire bien attention que les instructions ou données, stockées dans la mémoire physique, peuvent contenir des adresses virtuelles qui devront être valables dans les 2 espaces d'adressage. Ce partage est le régime normal pour la partie text qui peut être partagé (table des pages incluse).

  • Cours 5

    Adressage -- ter

    La structure pour les page table entries dépend de l'endianité du Mips. Big endian met le bit le plus significatif en tête, little endian le moins significatif d'abord. On remarque aussi qu'on peut désigner des champs de bits dans les structures de C.
    struct pte
    {
    #ifdef MIPSEB
    unsigned int pg_pfnum:20,  /* HW: core page frame number or 0 */
                 pg_n:1,       /* HW: non-cacheable bit */
                 pg_m:1,       /* HW: modified (dirty) bit */
                 pg_v:1,       /* HW: valid bit */
                 pg_g:1,       /* HW: ignore pid bit */
                 :4,
                 pg_swapm:1,   /* SW: page must be forced to swap */
                 pg_fod:1,     /* SW: is fill on demand (=0) */
                 pg_prot:2;	   /* SW: access control */
    #endif MIPSEB
    #ifdef MIPSEL
    unsigned int pg_prot:2,    /* SW: access control */
                 pg_fod:1,     /* SW: is fill on demand (=0) */
                 pg_swapm:1,   /* SW: page must be forced to swap */
                 :4,
                 pg_g:1,       /* HW: ignore pid bit */
                 pg_v:1,       /* HW: valid bit */
                 pg_m:1,       /* HW: modified (dirty) bit */
                 pg_n:1,       /* HW: non-cacheable bit */
                 pg_pfnum:20;  /* HW: core page frame number or 0 */
    #endif MIPSEL
    };
    

    Cours 5

    Swapping -- Pagination

  • le swap copie complètement un processus en mémoire ou le remet complètement sur disque. La partie disque qui correspond au disque de swap n'est pas un système de fichier, mais une partition disque où on écrit directement (en mode raw). Les zones de swap sont alloués par la structure dmap, où l'adresse donne la taille.

  • la pagination permet d'avoir partiellement un processus en mémoire. Par example, au chargement d'un fichier lors d'exec, le chargement peut se faire à la demande (page par page). Le principe de la pagination est que les processus ont une certaine localité dans les références mémoire, et ne se servent pas de toute leur mémoire de manière aléatoire. Plus, l'espace de travail ( working set) est petit, meilleure sera le comportement paginé.

  • Unix-BSD utilise les 2 techniques. Le swapper, processus 0, indissociable du noyau, veille au nombre de pages libres qui doivent être suffisantes, au processus endormis depuis plus de 20s. Alors un processus est swappé out.

  • Cours 5

    Pagination -- Allocation de la mémoire physique

  • Le tableau cmap[] (core map) décrit l'état des pages physiques.

  • Le voleur de page (processus système 2) fait l'algorithme de remplacement de pages. Il applique dans la procédure pageout() l'algorithme CLOCK (Corbato) qui consiste à faire le tour de la mémoire de tous les processus et repérer les pages LRU. On fait le tour de la mémoire en invalidant les pages, puis on regarde au 2ème tour si les pages sont toujours invalides. Si oui, la page est marquée comme candidate à la libération. Le surcoût du à l'invalidation des pages est faible. D'autres algorithmes à plusieurs mains sont plus efficaces.

  • Cours 5

    Pages: stratégies de remplacements

    TIT     .OPERATING SYSTEMS THEORY
    AUT     .Coffman, Edward G. / Denning, Peter J.
    COL     .Prentice Hall series in automatic computation
    DAT     .1973
    
  • Soit $m$ la taille de la mémoire, et une séquence (dans le temps) de références mémoire $r_1$, $r_1$, ... $r_n$. Quelle est la meilleure stratégie de remplacement, puisqu'on peut avoir $r_i > m$?

  • Exemple en FIFO, $r = 1 2 3 4 1 2 5 1 2 3 4 5$ et $m = 3, 4$. FIFO se conduit mal avec $m$.
    1 1 1 2 3 4 1 2 5
      2 2 3 4 1 2 5 3
        3 4 1 2 5 3 4
    
    1 1 1 1 2 3 4 5 1 2
      2 2 2 3 4 5 1 2 3
        3 3 4 5 1 2 3 4
          4 5 1 2 3 4 5
    

  • LRU (least recently used)

  • $B_0$ Belady (largest forwardly used)

  • LFU (least frequently used)

  • Cours 5

    Structure proc de tcsh

        p_link = 0x801853e0, /* linked list of running processes */
        p_rlink = 0x0,
        p_nxt = 0x801862a0,  /* linked list of allocated proc slots */
        p_prev = 0x801856ac, /* also zombies, and free proc's */
        p_addr = 0x801933d8, /* u-area kernel map address */
        p_tlbpid = -1,       /* tlb context of this proc */
        p_tlbinfo = 0x801940d8, /* per process tlb mappings */
        p_tlbindx = 3,       /* index of next wired tlb entry to use */
        p_usrpri = 57,       /* user-priority based on p_cpu and p_nice */
        p_pri = 40,          /* priority, negative is high */
        p_cpu = 31,          /* cpu usage for scheduling */
        p_stat = 1,
        p_time = 127,        /* resident time for scheduling */
        p_nice = 20,         /* nice for cpu usage */
        p_slptime = 127,     /* time since last block */
        p_cursig = 0,
        p_sig = 0,           /* signals pending to this process */
        p_sigmask = 2,       /* current signal mask */
        p_sigignore = 0x324004, /* signals being ignored */
        p_sigcatch = 0x9882003, /* signals being caught by user */
        p_flag = 0x13008201,
        p_uid = 20059,       /* signals being caught by user */
        p_suid = 20059,      /* saved set uid, used to direct tty signals */
        p_sgid = 0,          /* saved set group id, used by setgid */
        p_pgrp = 3855,       /* name of process group leader */
        p_pid = 3855,        /* unique process id */
        p_ppid = 3850,       /* process id of parent */
        p_xstat = 0,         /* Exit status for wait */
        p_progenv = 0,       /* process compatibility mode */
        p_sid = 0,           /* session id (for POSIX job control) */
        p_ru = 0x0,          /* mbuf holding exit information */
        p_poip = 0,          /* page outs in progress */
        p_tsize = 51,        /* size of text (clicks) */
        p_dsize = 21,        /* size of data space (clicks) */
        p_textstart = 0x400000,   /* starting address of user text */
        p_datastart = 0x10000000, /* starting address of user data */
        p_textpt = 1,        /* copy of text  page table size */
        p_datapt = 1,        /* copy of data  page table size */
        p_stakpt = 1,        /* copy of stack page table size */
        p_ssize = 9,         /* copy of stack size (clicks) */
        p_rssize = 9,        /* current resident set size in clicks */
        p_maxrss = 3187,     /* copy of u.u_limit[MAXRSS] */
        p_swrss = 0,         /* resident set size before last swap */
        p_swaddr = 5856,     /* disk address of u area when swapped */
                             /* event process is awaiting */
        p_wchan = 0xffffc000<Address 0xffffc000 out of bounds>, 
        p_textp = 0x801976e8, /* pointer to text structure */
        p_textbr = 0xc2a0e000,/* text  page table base */
        p_databr = 0xc2a51000,/* data  page table base */
        p_stakbr = 0xc2a52000,/* stack page table base */
        p_xlink = 0x801853e0, /* linked list of procs sharing same text */
        p_cpticks = 0,       /* ticks of cpu time */
        p_pctcpu = 0,        /* %cpu for this process during p_time */
        p_fp = 0,            /* generate SIGFPE on all fp interrupts */
        p_puac = 1,          /* print/don't print unaligned access msgs */
        p_ndx = 28,          /* proc index for memall (because of vfork) */
        p_idhash = 0,        /* hashed based on p_pid for kill+exit+... */
        p_pptr = 0x8018460c, /* pointer to process structure of parent */
        p_cptr = 0x80184aa8, /* pointer to youngest living child */
        p_osptr = 0x0,       /* pointer to older sibling processes */
        p_ysptr = 0x0,       /* pointer to younger siblings */
        p_realtimer = { 
        it_interval = {tv_sec = 0, tv_usec = 0},
        it_value = {tv_sec = 0, tv_usec = 0}},
        p_quota = 0x801bd0c0, /* quotas for this process */
        p_ttyp = 0x8014cab0,  /* controlling tty pointer  */
        p_smsize = 0,         /* size of SM space (clicks) */
        p_smbeg = 0,          /* page table offset of first SMS */
        p_smend = 0,          /* page table offset of end of */
                              /*       last attached SMS. */
        p_smcount = 0,        /* count of SMS attached to proc */
        p_sm = 0x0}           /* shared memory related elements */
    

    Cours 5

    Structure proc de tcsh

    (gdb) p jjl_mproc_p_tlbinfo
    $4 = {
        {
        lo = {
        tl_struct = {
        tls_g = 0,       /* match any pid */
        tls_v = 1,       /* valid */
        tls_d = 1,       /* dirty (actually writeable) */
        tls_n = 0,       /* non-cacheable */
        tls_pfn = 3424}},/* physical page frame number */
        hi = {
        th_struct = {
        ths_pid = 0,
        ths_vpn = 0xffc01}}}, /* virtual page number */
    
        {
        lo = {
        tl_struct = {tls_g = 0, tls_v = 1, tls_d = 1,
                     tls_n = 0, tls_pfn = 2759}},
        hi = {
        th_struct = {ths_pid = 0, ths_vpn = 0xffdff}}},
    
        {
        lo = {
        tl_struct = {tls_g = 0, tls_v = 1, tls_d = 1,
                     tls_n = 0, tls_pfn = 2756}},
        hi = {
        th_struct = {ths_pid = 0, ths_vpn = 0xffc40}}},
    
        0 ptes <repeats 5 times>}
    

    Cours 5

    Structures proc de tcsh

    (gdb) p jjl_mproc_p_textp
    $5 = {
        x_free = {            /* linked list of free text structures */
        xun_freef = 0x80196f58,
        xun_freeb = 0x80197a2c},
        x_daddr = 0xc37c9c00, /* disk addresses of DMTEXT-page segments */
        x_ptdaddr = 1208,     /* disk address of page table */
        x_size = 51,          /* size (clicks) */
        x_caddr = 0x80185a54, /* ptr to linked proc, if loaded */
        x_gptr = 0x80170210,  /* gnode of prototype */
        x_rssize = 39,        /* size in clicks */
        x_swrss = 0,          /* swapped rss */
        x_count = 7,          /* reference count */
        x_ccount = 5,         /* number of loaded references */
        x_lcount = 0,         /* number of processes locking segment */
        x_poip = 0,           /* page out in progress count */
        x_flag = 34}          /* traced, written flags */
    
    

    Cours 5

    Structures proc de tcsh

    (gdb) p jjl_mproc_p_textbr
    $6 = {
        {
        pg_prot = 2, pg_fod = 0, pg_swapm = 1, pg_g = 0, 
        pg_v = 1, pg_m = 1, pg_n = 0, pg_pfnum = 1980},
        { 
        pg_prot = 2, pg_fod = 0, pg_swapm = 1, pg_g = 0,
        pg_v = 1, pg_m = 1, pg_n = 0, pg_pfnum = 1277},
        {
        pg_prot = 2, pg_fod = 0, pg_swapm = 1, pg_g = 0,
        pg_v = 1, pg_m = 1, pg_n = 0, pg_pfnum = 2270},
        {
        pg_prot = 2, pg_fod = 0, pg_swapm = 1, pg_g = 0,
        pg_v = 1, pg_m = 1, pg_n = 0, pg_pfnum = 3800},
        {
        pg_prot = 2, pg_fod = 0, pg_swapm = 1, pg_g = 0,
        pg_v = 1, pg_m = 1, pg_n = 0, pg_pfnum = 3168},
        {
        pg_prot = 2, pg_fod = 0, pg_swapm = 1, pg_g = 0,
        pg_v = 1, pg_m = 1, pg_n = 0, pg_pfnum = 1153},
        {
        pg_prot = 2, pg_fod = 0, pg_swapm = 1, pg_g = 0,
        pg_v = 1, pg_m = 1, pg_n = 0, pg_pfnum = 978},
        {
        pg_prot = 2, pg_fod = 0, pg_swapm = 1, pg_g = 0,
        pg_v = 1, pg_m = 1, pg_n = 0, pg_pfnum = 3939},
    ...
        {
        pg_prot = 0, pg_fod = 0, pg_swapm = 0, pg_g = 0,
        pg_v = 0, pg_m = 0, pg_n = 0, pg_pfnum = 0} <repeats 49 times>}
    

    Cours 5

    Structures proc de tcsh

    (gdb) p jjl_mproc_p_databr
    $7 = {
        {
        pg_prot = 3, pg_fod = 0, pg_swapm = 0, pg_g = 0,
        pg_v = 0, pg_m = 0, pg_n = 0, pg_pfnum = 2036},
        {
        pg_prot = 3, pg_fod = 0, pg_swapm = 0, pg_g = 0,
        pg_v = 0, pg_m = 0, pg_n = 0, pg_pfnum = 0},
        {
        pg_prot = 3, pg_fod = 0, pg_swapm = 0, pg_g = 0,
        pg_v = 0, pg_m = 0, pg_n = 0, pg_pfnum = 3201},
        {
        pg_prot = 3, pg_fod = 0, pg_swapm = 0, pg_g = 0,
        pg_v = 0, pg_m = 0, pg_n = 0, pg_pfnum = 3474},
    ...
    
        {
        pg_prot = 3, pg_fod = 0, pg_swapm = 0, pg_g = 0,
        pg_v = 0, pg_m = 0, pg_n = 0, pg_pfnum = 0} <repeats 32 times>,
        {
        pg_prot = 0, pg_fod = 0, pg_swapm = 0, pg_g = 0,
        pg_v = 0, pg_m = 0, pg_n = 0, pg_pfnum = 0} <repeats 49 times>}
    (gdb) p jjl_mproc_p_stakbr
    $8 = {
        {
        pg_prot = 0, pg_fod = 0, pg_swapm = 0, pg_g = 0,
        pg_v = 0, pg_m = 0, pg_n = 0, pg_pfnum = 0} <repeats 100 times>}
    

    Cours 5

    Structure user de tcsh

    $1 = {
        u_pcb = {  /* General purpose registers saved at context switch time. */
        pcb_regs = {
        -2145890728, 257, 268516740, 2,
        13, 26, 2147473756, 0,
        -8532, 0, -2146586220, 65281,
        0 <repeats 20 times>},
        pcb_pc = 0,
        pcb_sr = 0,
        /* Misc. */
        pcb_sswap = 0x0, /* ptr for non-local resume */
        pcb_resched = 1, /* non-zero if time to resched */
        pcb_sstep = 0,   /* non-zero if single stepping */
        /* single step state info */
        pcb_ssi = {
        ssi_cnt = 0,
        ssi_bp = {
        {bp_addr = 0x0, bp_inst = 0},
        {bp_addr = 0x0, bp_inst = 0}}},
        /* These are use in branch delay instruction emulation *
        pcb_bd_epc = 0,   /* epc register */
        pcb_bd_cause = 0, /* cause register */
        pcb_bd_ra = 0,    /* address to return to if doing bd emulation */  
        pcb_bd_instr = 0, /* the branch instr for the bd emulation */
        /* This is use in fp instruction emulation */
        pcb_softfp_pc = 0,/* resulting pc after fp emulation */
        /* Space for the state of all the potential coprocessors. WASTEFUL!*/
        pcb_fpregs = {
        0 <repeats 32 times>}, /* floating point */    
        pcb_fpc_csr = 0,      /* floating point control and status reg */
        pcb_fpc_eir = 789,    /* floating point exception instruction reg */
        pcb_ownedfp = -2146258800, /* has owned fp at one time */
        pcb_c2regs = {0 <repeats 32 times>},  /* TBD */
        pcb_c3regs = {0 <repeats 32 times>}}, /* TBD */
    
    }

    Cours 5

    Structure user de tcsh

        u_procp = 0x80184e58, /* pointer to proc structure */
        u_ar0 = 0xffffdf5c,   /* pointer to proc structure */
        u_comm = {"tcsh", '\000' <repeats 252 times>},
        /* syscall parameters, results and catches */
        u_arg = { /* arguments to current system call */
        2, 0, 2147477928, 4353624,
        0, 0, 0, 0},
        u_ap = 0xffffc350,
        u_qsave = { /* for non-local gotos on interrupts */
        val = {
        536936252, 257, 268516740, 2,
        13, 26, 2147473756, 0,
        -8428, 0, -2146578536, 536936253}},
        u_r = { /* syscall return values */
        u_rv = {
        R_val1 = 0,
        R_val2 = 4},
        r_off = 0,
        r_time = 0},
        u_error = 0 '\000',   /* return error code */
        u_eosys = 3 '\003', /* special action on end of syscall */
        u_narg = 0 '\000',  /* number of arguments in u_arg above */
    
    /* 1.1 - processes and protection */
        u_cred = 0xc3790f00, /* user credentials (uid, gid, etc) */
    
    /* 1.2 memory management */
        u_tsize = 51, /* text size (clicks) */
        u_dsize = 21, /* data size (clicks) */
        u_ssize = 9,  /* stack size (clicks) */
    

    Cours 5

    Structure user de tcsh

        /* disk map for data segment */
        u_dmap = {
        dm_size = 168, /* current size used by process */
        dm_alloc = 224,/* amount of physical swap space allocated */
        dm_map = {     /* first disk block number in each chunk */
        6752,
        25968,
        44016, 
        0 <repeats 45 times>}},
        u_smap = {     /* disk map for stack segment */
        dm_size = 72,  /* current size used by process */
        dm_alloc = 96, /* amount of physical swap space allocated */
        dm_map = {     /* first disk block number in each chunk */  
                  21800, 45136,
                  0 <repeats 46 times>}},
        u_cdmap = { /* shadows of u_dmap, u_smap, for use of parent during fork */
        dm_size = 168,  
        dm_alloc = 224,
        dm_map = {44240, 44272, 44336,
                  0 <repeats 45 times>}},
        u_csmap = {
        dm_size = 72,
        dm_alloc = 96,
        dm_map = {45040, 45072,
                  0 <repeats 46 times>}},
        /* label variable for swapping */
        u_ssave = { /* label variable for swapping */
        val = {-2146809824, -2145890728, 5, 268490372,
               0, 26, 2147473492, 0,
               -8684, 0, -2146779520, 65341}},
        u_odsize = 63, u_ossize = 2, /* for (clumsy) expansion swaps */
        u_outime = 0, /* user time at last sample */
    

    Cours 5

    Structure user de tcsh

    /* 1.3 - signal management */
        u_signal = {}, /* disposition of signals */ 
        u_sigmask = {  /* signals to be blocked */ 
        0,
        16384,
        0 <repeats 13 times>,
        1,
        0 <repeats 16 times>},
        u_sigonstack = 0, /* signals to take on sigstack */
        u_sigintr = 0,    /* signals that interrupt syscalls */
        u_oldsig = 0,     /* POSIX "old stylë signals */
        u_oldmask = 524290, /* saved mask from before sigpause */ 
        u_code = 0,       /* ``code'' to trap */
        u_sigstack = {    /* sp & on stack state variable */ 
        ss_sp = 0x0,
        ss_onstack = 0},
        u_sigtramp = 0x426e58 <_atod+51800>, /* signal trampoline code */
        u_trapcause = 0,  /* cause for SIGTRAP */
        u_trapinfo = 0,   /* extra info concerning SIGTRAP */
    
    /* 1.4 - descriptor management */
        u_ofile = {},     /* file structures for open files */
        u_pofile = {      /* per-process flags of open files */
        '\000' <repeats 15 times>,
        "\001\001\001\001\001",
        '\000' <repeats 44 times>},
        u_lastfile = 19,  /* highest numbered open file */
        u_cdir = 0x8017d314, /* current directory */
        u_rdir = 0x0,     /* root directory of current process */
        u_ttyd = 1543,    /* controlling tty dev */
        u_cmask = 18,     /* mask for file creation */
    

    Cours 5

    /* 1.5 - timing and statistics */
        u_ru = {          /* stats for this proc */
        ru_utime = {      /* user time used */
        tv_sec = 0, tv_usec = 187488},
        ru_stime = {      /* system time used */
        tv_sec = 0, tv_usec = 507780},
        ru_maxrss = 148,
        ru_ixrss = 8462, /* integral shared text size */
        ru_ismrss = 0,   /* integral shared memory size*/
        ru_idrss = 4825, /* integral unshared data " */
        ru_isrss = 0,    /* integral unshared stack " */
        ru_minflt = 15,  /* page reclaims */
        ru_majflt = 3,   /* page faults */
        ru_nswap = 0,    /* swaps */
        ru_inblock = 2,  /* block input operations */
        ru_oublock = 1,  /* block output operations */
        ru_msgsnd = 0,   /* messages sent */
        ru_msgrcv = 0,   /* messages received */
        ru_nsignals = 7, /* signals received */
        ru_nvcsw = 120,  /* voluntary context switches */
        ru_nivcsw = 50}, /* involuntary " */
        u_cru = {        /* sum of stats for reaped children */
        ru_utime = {
        tv_sec = 0, tv_usec = 31248},
        ru_stime = {
        tv_sec = 0, tv_usec = 320292},
        ru_maxrss = 64,
        ru_ixrss = 1505,
        ru_ismrss = 0,
        ru_idrss = 930,
        ru_isrss = 0,
        ru_minflt = 20,
        ru_majflt = 13,
        ru_nswap = 0,
        ru_inblock = 0,
        ru_oublock = 0,
        ru_msgsnd = 0,
        ru_msgrcv = 0,
        ru_nsignals = 2,
        ru_nvcsw = 27,
        ru_nivcsw = 22},
    

    Cours 5

        u_timer = {
        {it_interval = {tv_sec = 0, tv_usec = 0},
         it_value = {tv_sec = 0, tv_usec = 0}},
        {it_interval = {tv_sec = 0, tv_usec = 0},
         it_value = {tv_sec = 0, tv_usec = 0}},
        {it_interval = {tv_sec = 0, tv_usec = 0},
         it_value = {tv_sec = 0, tv_usec = 0}}},
        u_tracedev = 0, /* for syscall tracer */
        u_XXX = {0, 0, 0},
        u_start = {    /* whole timeval instead of secs */
        tv_sec = 700477846, tv_usec = 72145},
        u_acflag = 2,
    
    /* 1.6 - resource controls */
        
        u_rlimit = {
        {rlim_cur = 2147483647,
         rlim_max = 2147483647},
        {rlim_cur = 2147483647,
         rlim_max = 2147483647},
        {rlim_cur = 88051712,
         rlim_max = 88051712},
        {rlim_cur = 524288,
         rlim_max = 88051712},
        {rlim_cur = 2147483647,
         rlim_max = 2147483647},
        {rlim_cur = 13053952,
         rlim_max = 13053952}},
        u_quota = 0x801bd0c0,
        u_qflags = 0,
        u_prof = {
        pr_base = 0x0,
        pr_size = 0,
        pr_off = 0,
        pr_scale = 0},
    
    /* 1.7 - System V related elements */
        u_smsize = 0,  /* size of SM space (clicks) */
        u_osmsize = 0, /* old SM size for expansion swaps */ 
        u_lock = 0,    /* memory locking flags (see ../h/lock.h) */
    
    

    Cours 5

    Structure user de tcsh

    /* 1.9 - Namei caching */
        u_ncache = {   /* last successful directory search */
        nc_prevoffset = 4564, /* offset at which last entry found */
        nc_inumber = 16384,   /* inum of cached directory */
        nc_dev = 2062,        /* dev of cached directory */
        nc_time = 700477853}, /* not used */
        u_nd = {
        ni_dirp = 0xc373ac00<Address 0xc373ac00 out of bounds>,
        ni_nameiop = 64,
        ni_error = 0,
        ni_endoff = 0,
        ni_slcnt = 1,
        ni_pdir = 0x8016eeec,
        ni_iovec = {
        iov_base = 0x0,
        iov_len = 0},
        ni_uio = {
        uio_iov = 0x0,
        uio_iovcnt = 0,
        uio_offset = 24,
        uio_segflg = 0,
        uio_resid = 0,
        uio_flag = 0},
        ni_dent = {
        gd_ino = 2019,
        gd_reclen = 16,
        gd_namelen = 3,
        gd_name = {
        "a7\000a\000lly\000s\000kpriss",
        '\000' <repeats 239 times>}},
        ni_bufp = 0x0,
        ni_cp = 0xc373ac1a
    }, u_stack = { 0}}

    Cours 5

    Entrées dans le noyau (Rappel)

  • Appels système (à partir de la librairie C) cf
    libc{/mips/sys/*.s}

  • Exceptions et interruptions (complètement asynchrones). Par exemple, TLB_MISS ou interruption horloge toutes les 10ms.

  • on arrive à des adresses fixes (0x800000xx) en mode mâitre du processeur

  • on va regarder l'exemple détaillé d'un appel-système.

  • Cours 5

    Appels système

    1- dans sys/mips/asm.h et dans libc{/gen/getpid.s}
    /*
     * SYSCALL -- standard system call sequence
     * The kernel expects arguments to be passed with the normal C calling
     * sequence.  v0 should contain the system call number.  On return from
     * the kernel mode, a3 will be 0 to indicate no error and non-zero to
     * indicate an error; if an error occurred v0 will contain an errno.
     */
    #define SYSCALL(x)                                      \
    LEAF(x);                                                \
            li      v0,SYS_/**/x;                           \
            syscall;                                        \
            beq     a3,zero,9f;                             \
            j       _cerror;                                \
    9:
    
    ...
    SYSCALL(getpid)
            RET
    .end getpid
    
    2- on reçoit avec des tables toutes prêtes des numéros d'appels-système et l'entrée 0x800000xx qui contient le début du programme exception (cf sys/h/systm.h, sys/init_sysent.c, sys/mips/locore.s.
    extern struct sysent
    {
            int     sy_narg;                /* total number of arguments */
            int     (*sy_call)();           /* handler */
            int     sy_mpsafe;              /* safe for multiprocessing use */
    } sysent[];
    ...
    /*
     * System call switch table.
     */
    struct sysent sysent[] = {
            0, nosys, 0,           /*   0 = indir */
            1, rexit, 0,           /*   1 = exit */
    ...
            0, getpid, 1,          /*  20 = getpid */
    };
    ...
    VECTOR(exception, M_EXCEPT)    # Copied down to 0x80000080
            .set    noreorder
            .set    noat
    
            /*
             * WARNING!!!!
             * Address calculation here assumes that 16 bit offset on lw
             * do es NOT have sign bit set! Do not use PUTMSG or any other
             * message routines from here (can destroy stack).
            */
            lui    k0,kstackflag>>16
            lw     k0,+kstackflag&0xffff(k0)
            nop
            beq    k0,zero,1f
            move   k0,AT            # BDSLOT: save at
            .set   at
            .set   reorder
            sw     sp,EF_SP*4-EF_SIZE(sp)
            subu   sp,EF_SIZE
            b      2f
    
            /*
             * Came from user mode or utlbmiss, initialize kernel stack
             */
    1:      sw     sp,KERNELSTACK-EF_SIZE+EF_SP*4
            la     sp,KERNELSTACK-EF_SIZE
            sw     gp,EF_GP*4(sp)
            la     gp,_gp
            sw     gp,kstackflag        # now on kernel stack (gp always != 0)
            /*
             * This instruction stream can be cleaned up somewhat for write stalls,
             * but for now, left as is so its readable when debugging
            */
    2:      sw     k0,EF_AT*4(sp)
            sw     k1,EF_K1*4(sp)       # in case we came from utlbmiss
            sw     a0,EF_A0*4(sp)
            mfc0   a0,C0_EPC
            sw     a1,EF_A1*4(sp)
            sw     a2,EF_A2*4(sp)
            sw     a3,EF_A3*4(sp)
            mfc0   a3,C0_CAUSE
            sw     a3,EF_CAUSE*4(sp)
            sw     s0,EF_S0*4(sp)
            sw     ra,EF_RA*4(sp)
            mfc0   s0,C0_SR
            sw     a0,EF_EPC*4(sp)
            /*
             * Dispatch to appropriate exception handler
             * Register setup:
             *    s0 -- SR register at time of exception
             *    a0 -- exception frame pointer
             *    a1 -- cause code
             *    a3 -- cause register
            */
            and    a1,a3,CAUSE_EXCMASK
            lw     a2,causevec(a1)
            move   a0,sp
            .set   noreorder
            j      a2
            sw     s0,EF_SR*4(sp)        # should clear PE in s0 after here
            .set   reorder
    EXPORT(eexception)
            END(exception)
    
    3- on a trouvé qu'il s'agissait d'un appel-système grâce à cette table de sys/mips/trap.c.
    int  (*causevec[16])() = {
            /*  0: EXC_INT */         VEC_int,
            /*  1: EXC_MOD */         VEC_tlbmod,
            /*  2: EXC_RMISS */       VEC_tlbmiss,
            /*  3: EXC_WMISS */       VEC_tlbmiss,
            /*  4: EXC_RADE */        VEC_addrerr,
            /*  5: EXC_WADE */        VEC_addrerr,
            /*  6: EXC_IBE */         VEC_ibe,
            /*  7: EXC_DBE */         VEC_dbe,
            /*  8: EXC_SYSCALL */     VEC_syscall,
            /*  9: EXC_BREAK */       VEC_breakpoint,
            /* 10: EXC_II */          VEC_trap,
            /* 11: EXC_CPU */         VEC_cpfault,
            /* 12: EXC_OV */          VEC_trap,
            /* 13: undefined */       VEC_unexp,
            /* 14: undefined */       VEC_unexp,
            /* 15: undefined */       VEC_unexp
    };
    
    4- on part exécuter le code des appels-système dans sys/mips/locore.s et dans {sys/mips/trap.c}.
    VECTOR(VEC_syscall, M_SYSCALLSAVE)
    #endif
            or      a1,s0,SR_IEC            # enable interrupts
            mtc0    a1,C0_SR
            sw      v0,EF_V0*4(sp)          # u_rval1
            sw      v1,EF_V1*4(sp)          # u_rval2
            move    a1,v0                   # arg2 -- syscall number
            move    a2,s0                   # arg3 -- sr
            jal     syscall                 # syscall(ef_ptr, sysnum, sr, cause)
            bne     v0,zero,full_restore    # doing a sigreturn
            lw      v0,EF_V0*4(sp)          # u_rval1
            lw      v1,EF_V1*4(sp)          # u_rval2
            mtc0    s0,C0_SR                # disable interrupts
            b       exception_exit
            END(VEC_syscall)
    
    ...
    syscall(ep, code, sr, cause)
    register u_int *ep;
    u_int code;
    {
        register regparams, nargs;
        register struct sysent *callp;
        register struct proc *p;
        register int *params;
        int opc;
        struct timeval syst;
        extern char *syscallnames[];
    
        cnt.v_syscall++;
        syst = u.u_ru.ru_stime;
        u.u_error = 0;
        if (!USERMODE(sr))
             panic("syscall");
    
        opc = ep[EF_EPC];
        ep[EF_EPC] += 4;        /* normal return executes next inst */
    
        XPRINTF(XPR_SYSCALL, "syscall %n upc 0x%x\n", code, syscall_values,
            opc, 0);
    
        regparams = EF_A0;
        if (code >= nsysent) {
            callp = &sysent[0];    /* indir (illegal) */
            ep[EF_EPC] = opc;    /* just leave pc at the syscall inst */
        }
        else {
            callp = &sysent[code];
            if (callp == sysent) {
                /*
                 * indirect system call (syscall), first param is
                 * sys call number
                 */
                code = ep[EF_A0];
                regparams++;
                if (code >= nsysent)
                    callp = &sysent[0];
                else
                    callp = &sysent[code];
            }
        }
        u.u_eosys = NORMALRETURN;
        params = (int *)u.u_arg;
        for (nargs = callp->sy_narg; nargs && regparams <= EF_A3; nargs--)
            *params++ = ep[regparams++];
        if (nargs) {
            u.u_error = copyin(ep[EF_SP]+4*sizeof(int), params,
                               nargs*sizeof(int));
            if (u.u_error)
                goto done;
        }
    
        u.u_r.r_val1 = 0;
        u.u_r.r_val2 = ep[EF_V1];
    
        if (setjmp(&u.u_qsave)) {
            if (u.u_error == 0 && u.u_eosys != RESTARTSYS)
                u.u_error = EINTR;
        } else {
            (*(callp->sy_call))(u.u_arg);
        }
    
    done:
        /*
         * a3 is returned to user 0 if indicate no errors on syscall,
         * non-zero otherwise
         */
        if (u.u_eosys == NORMALRETURN) {
            if (u.u_error) {
                ep[EF_V0] = u.u_error;
                ep[EF_A3] = 1;
            } else {
                ep[EF_V0] = u.u_r.r_val1;
                ep[EF_V1] = u.u_r.r_val2;
                ep[EF_A3] = 0;
            }
        } else if (u.u_eosys == RESTARTSYS)
            ep[EF_EPC] = opc;
        /* else if (u.u_eosys == FULLRESTORE) */
            /* returning from sigreturn, force full state restore */
    
        p = u.u_procp;
        if (p->p_cursig || ISSIG(p))
            psig();
        p->p_pri = p->p_usrpri;
        if (runrun) {
            /*
             * Since we are u.u_procp, clock will normally just change
             * our priority without moving us from one queue to another
             * (since the running process is not on a queue.)
             * If that happened after we setrq ourselves but before we
             * swtch()'ed, we might not be on the queue indicated by
             * our priority.
             */
            (void) splclock();
            setrq(p);
            u.u_ru.ru_nivcsw++;
            swtch();
        }
        /*
         * if single stepping this process, install breakpoints before
         * returning to user mode.  Do this here rather than in procxmt
         * so single stepping will work when signals are delivered.
         */
        if (u.u_pcb.pcb_sstep)
            install_bp();
        if (u.u_prof.pr_scale) {
            int ticks = times_to_ticks(&u.u_ru.ru_stime,&syst);
            if (ticks)
                addupc(ep[EF_EPC], &u.u_prof, ticks);
        }
        cpudata[0].c_curpri = p->p_pri;
        /*
         * if u_eosys == FULLRESTORE, then force full state restore
         */
        return (u.u_eosys == FULLRESTORE);
    }
    
    5- on fait enfin la chose demandée dans l'appel-système! cf sys/sys/kern_prot.c
    getpid()
    {
    
            u.u_r.r_val1 = u.u_procp->p_pid;
            u.u_r.r_val2 = u.u_procp->p_ppid;
    }
    
    6- on repart dans sys/mips/locore.s
    /*
     * End of exception processing.  Interrupts should be disabled.
     */
    VECTOR(exception_exit, M_EXCEPT)
            /*
            * ENTRY CONDITIONS:
            *    Interrupts Disabled
            *    s0 contains sr at time of exception
            *
            * If we are returning to user mode, check to see if a resched is
            * desired.  If so, fake a RESCHED cause bit and let trap save/restore
            * our state for us.
            */
            and    k0,s0,SR_KUP
            beq    k0,zero,2f            # returning to kernel mode
            lw     k0,u+PCB_RESCHED
            beq    k0,zero,1f            # no resched requested
            move   a0,sp
            li     a1,SEXC_RESCHED       # software exception
            lw     a3,EF_CAUSE*4(sp)
            b      VEC_trap
    
    1:      sw     zero,kstackflag        # switching to user stack
            lw     gp,EF_GP*4(sp)
    2:      lw     a0,EF_A0*4(sp)
            lw     a1,EF_A1*4(sp)
            lw     a2,EF_A2*4(sp)
            lw     a3,EF_A3*4(sp)
            lw     s0,EF_S0*4(sp)
            lw     ra,EF_RA*4(sp)
            lw     k0,EF_EPC*4(sp)
            lw     k1,EF_SR*4(sp)
            mtc0   k1,C0_SR               # PE BIT
            .set   noreorder
            .set   noat
            lw     AT,EF_AT*4(sp)
            lw     sp,EF_SP*4(sp)
            j      k0
            c0     C0_RFE
            .set   at
            .set   reorder
            END(exception_exit)
    
    7- Si l'appel système a plus de 4 arguments, les arguments supplémentaires sont comme en C passés dans la pile (utilisateur). Or on est maintenant en mode maître du processeur avec une autre pile. Il faut donc recopier des choses de l'espace utilisateur vers l'espace noyau. C'est fait par copyin() de sys/mips/usercopy.s. Cette procédure est incroyablement compliquée, car elle peut avoir à copier de gros objets et est super optimisée.
    /*
     * copyin(user_src, kernel_dst, bcount)
     */
    COPYIOFRM=    (4*4)+4             # 4 arg saves plus ra
    NESTED(copyin, COPYIOFRM, zero)
            subu    sp,COPYIOFRM
            sw      ra,COPYIOFRM-4(sp)
            bltz    a0,cerror
            .set    noreorder
            li      v0,NF_COPYIO
            sw      v0,nofault        # branch delay slot
            jal     bcopy
            nop
            sw      zero,nofault
            .set    reorder
            move    v0,zero
            lw      ra,COPYIOFRM-4(sp)
            addu    sp,COPYIOFRM
            j       ra
            END(copyin)
    
    /*
     * copyout(kernel_src, user_dst, bcount)
     */
    NESTED(copyout, COPYIOFRM, zero)
            subu    sp,COPYIOFRM
            sw      ra,COPYIOFRM-4(sp)
            bltz    a1,cerror
            .set    noreorder
            li      v0,NF_COPYIO
            sw      v0,nofault        # branch delay slot
            jal     bcopy
            nop
            sw      zero,nofault
            .set    reorder
            move    v0,zero
            lw      ra,COPYIOFRM-4(sp)
            addu    sp,COPYIOFRM
            j       ra
            END(copyout)
    
    NESTED(cerror, COPYIOFRM, zero)
            li      v0,EFAULT
            lw      ra,COPYIOFRM-4(sp)
            addu    sp,COPYIOFRM
            j       ra
            END(cerror)
    
    /*
     * bcopy(src, dst, bcount)
     *
     * NOTE: the optimal copy here is somewhat different than for the user-level
     * equivalents (bcopy in 4.2, memcpy in V), because:
     * 1) it frequently acts on uncached data, especially since copying from
     * (uncached) disk buffers into user pgms is high runner.
     * This means one must be careful with lwl/lwr/lb - don't expect cache help.
     * 2) the distribution of usage is very different: there are a large number
     * of bcopies for small, aligned structures (like for ioctl, for example),
     * a reasonable number of randomly-sized copies for user I/O, and many
     * bcopies of large (page-size) blocks for stdio; the latter must be
     * well-tuned, hence the use of 32-byte loops.
     * 3) this is much more frequently-used code inside the kernel than outside
     *
     * Overall copy-loop speeds, by amount of loop-unrolling: assumptions:
     * a) low icache miss rate (this code gets used a bunch)
     * b) large transfers, especially, will be word-alignable.
     * c) Copying speeds (steady state, 0% I-cache-miss, 100% D-cache Miss):
     * d) 100% D-Cache Miss (but cacheable, so that lwl/lwr/lb work well)
     *      Config  Bytes/  Cycles/ Speed (VAX/780 = 1)
     *              Loop    Word
     *      08V111  35      0.71X   (8MHz, BUS, 1-Deep WB, 1-way ILV)
     *              4       15      1.67X
     *              8/16    13.5    1.85X
     *              32/up   13.25   1.89X
     *      08MM44  1       26      0.96X   (8MHz, MEM, 4-Deep WB, 4-way ILV)
     *              4       9       2.78X
     *              8       7.5     3.33X
     *              16      6.75    3.70X
     *              32      6.375   3.92X   (diminishing returns thereafter)
     *
     * MINCOPY is minimum number of byte that its worthwhile to try and
     * align copy into word transactions.  Calculations below are for 8 bytes:
     * Estimating MINCOPY (C = Cacheable, NC = Noncacheable):
     * Assumes 100% D-cache miss on first reference, then 0% (100%) for C (NC):
     * (Warning: these are gross numbers, and the code has changed slightly):
     *      Case            08V11                   08M44
     *      MINCOPY         C       NC              C       NC
     *      9 (1 byte loop) 75      133             57      93
     *      8 (complex logic)
     *      Aligned         51      51              40      40
     *      Alignable,
     *      worst (1+4+3)   69      96              53      80
     *      Unalignable     66      93              60      72
     * MINCOPY should be lower for lower cache miss rates, lower cache miss
     * penalties, better alignment properties, or if src and dst alias in
     * cache. For this particular case, it seems very important to minimize the
     * number of lb/sb pairs: a) frequent non-cacheable references are used,
     * b) when i-cache miss rate approaches zero, even the 4-deep WB can't
     * put successive sb's together in any useful way, so few references are saved.
     * To summarize, even as low as 8 bytes, avoiding the single-byte loop seems
     * worthwhile; some assumptions are probably optimistic, so there is not quite
     * as much disadvantage.  However, the optimal number is almost certainly in
     * the range 7-12.
     *
     *      a0      src addr
     *      a1      dst addr
     *      a2      length remaining
     */
    #define MINCOPY 8
    
    LEAF(bcopy)
            xor     v0,a0,a1            # bash src & dst for align chk; BDSLOT
            blt     a2,MINCOPY,bytecopy # too short, just byte copy
            and     v0,NBPW-1           # low-order bits for align chk
            subu    v1,zero,a0          # -src; BDSLOT
            bne     v0,zero,unaligncopy # src and dst not alignable
    /*
     * src and dst can be simultaneously word aligned
     */
            and     v1,NBPW-1           # number of bytes til aligned
            subu    a2,v1               # bcount -= alignment
            beq     v1,zero,blkcopy     # already aligned
            lwr     v0,0(a0)
            swr     v0,0(a1)
            addu    a0,v1               # src += alignment
            addu    a1,v1               # dst += alignment
    
    /*
     * 32 byte block, aligned copy loop (for big reads/writes)
     */
    blkcopy:
            and      a3,a2,~31         # total space in 32 byte chunks
            subu     a2,a3             # count after by-32 byte loop done
            beq      a3,zero,wordcopy  # less than 32 bytes to copy
            addu     a3,a0             # source endpoint
    1:      lw       v0,0(a0)
            lw       v1,4(a0)
            lw       t0,8(a0)
            lw       t1,12(a0)
            sw       v0,0(a1)
            sw       v1,4(a1)
            sw       t0,8(a1)
            sw       t1,12(a1)
            addu     a0,32             # src+= 32; here to ease loop end
            lw       v0,-16(a0)
            lw       v1,-12(a0)
            lw       t0,-8(a0)
            lw       t1,-4(a0)
            sw       v0,16(a1)
            sw       v1,20(a1)
            sw       t0,24(a1)
            sw       t1,28(a1)
            addu     a1,32             # dst+= 32; fills BD slot
            bne      a0,a3,1b
    
    /*
     * word copy loop
     */
    wordcopy:
            and      a3,a2,~(NBPW-1)   # word chunks
            subu     a2,a3             # count after by word loop
            beq      a3,zero,bytecopy  # less than a word to copy
            addu     a3,a0             # source endpoint
    1:      lw       v0,0(a0)
            addu     a0,NBPW
            sw       v0,0(a1)
            addu     a1,NBPW           # dst += 4; BD slot
            bne      a0,a3,1b
            b        bytecopy
    
    /*
     * deal with simultaneously unalignable copy by aligning dst
     */
    unaligncopy:
            subu     a3,zero,a1         # calc byte cnt to get dst aligned
            and      a3,NBPW-1          # alignment = 0..3
            subu     a2,a3              # bcount -= alignment
            beq      a3,zero,partaligncopy     # already aligned
            lwr      v0,0(a0)          # get whole word
            lwl      v0,3(a0)          # for sure
            swr      v0,0(a1)          # store right piece (1-3 bytes)
            addu     a0,a3             # src += alignment (will fill LD slot)
            addu     a1,a3             # dst += alignment
    
    /*
     * src unaligned, dst aligned loop
     * NOTE: if MINCOPY >= 7, will always do 1 loop iteration or more
     * if we get here at all
     */
    partaligncopy:
            and      a3,a2,~(NBPW-1)    # space in word chunks
            subu     a2,a3              # count after by word loop
            addu     a3,a0              # source endpoint
    1:
            lwr      v0,0(a0)
            lwl      v0,3(a0)
            addu     a0,NBPW
            sw       v0,0(a1)
            addu     a1,NBPW
            bne      a0,a3,1b
    
    
    /*
     * brute force byte copy loop, for bcount < MINCOPY + tail of unaligned dst
     * note that lwl, lwr, swr CANNOT be used for tail, since the lwr might
     * cross page boundary and give spurious address exception
     */
    bytecopy:
            addu     a3,a2,a0          # source endpoint; BDSLOT
            ble      a2,zero,copydone  # nothing left to copy, or bad length
    1:      lb       v0,0(a0)
            addu     a0,1
            sb       v0,0(a1)
            addu     a1,1              # BDSLOT: incr dst address
            bne      a0,a3,1b
    copydone:
            j     ra
            END(bcopy)
    

    Cours 5

    Appels système -- Quelques remarques

  • getpid() est la caricature de l'appel système. Pour 2 instructions, on a un overhead incroyable. Il faut toutefois retenir que ca coûte de faire des tours dans le noyau.

  • A la fin d'un appel système, on regarde si on un signal attend. Si oui, on exécute le handler du signal.
    if (issig())
        psig();
    
  • A la fin de l'appel système on regarde ensuite si le drapeau runrun est positionné. Ceci signifie que le scheduler réclame qu'il y ait une commutation de processus.

  • Cours 5

    Commutation de processus

  • Le système Unix a son noyau non préemptif. Jamais un appel-système ne peut être empêché d'aller au bout. Il peut s'arrêter par sleep() car une ressource dont il a besoin est absente. Il peut abandonner le processeur, par swtch(), car il voit le drapeau runrun positionné en fin d'appel-système.

  • Avec cette seule philosophie, jamais un processus ne faisant d'appels-système n'abandonnerait le cpu. Donc, en fin d'interruption (horloge), dans hardclock() de sys/sys/kern_clock, le drapeau runrun est mis en appelant setpri() de sys/sys/kern_synch. Et la procédure trap() de sys/mips/trap.c --- qui correspond à syscall() --- fait la commutation si le processus interrompu est en mode utilisateur et si runrun est positionné, en fin de traitement de l'exception.

  • La non préemption des appels-système simplifie grandement l'écriture du noyau. Il faut donc se garder uniquement contre les interruptions possibles par les software priority levels splclock(), spltty(), ... définies dans sys/mips/locore.s.

  • L'argument précédent n'est plus vrai sur un multi processeur! Cf Unix sur Sequent, Encore, Alliant.

  • Une interruption fait en général très peu, i.e. mettre qques drapeaux ou qques changements légers dans les structures de données, et s'empresse de rendre la main.

  • Cours 5

    Signaux

  • La présence d'un signal est testée, en fin de traitement d'exception en mode utilisateur, ou en fin d'appel système par issig().

  • La présence d'un signal est aussi testée dans la procédure d'abandon de cpu sleep() en fonction de la priorité d'endormissement (2ème argument de sleep()).

  • Les signaux sont traités en créant au sommet de la pile utilisateur un nouveau frame par sendsig() de sys/mips/mips/machdep.c

  • Le retour du signal se fait en revenant par sigreturn().

  • L'envoi d'un signal se fait par kill() et psignal(). Remarquons que kill() parcourt la table des proc et positionne la présence du signal. La partie ``signal'' d'un processus doit se trouver dans la structure proc et non dans la structure u.

  • Cours 5

    Interruptions horloge

  • Des alarmes callout sont mises en attente des interruptions horloge.

  • Les callout sont mis en ordre croissant le délai de réveil et chacun stocke le temps incrémental. A chaque tick d'horloge, on diminue le temps du premier, et on teste s'il est négatif ou nul.

  • On peut manquer une IT horloge, ou en reprendre une pendant son traitement. C'est pourquoi il faut ausi tester si le premier réveil est négatif.
    /*
     * The callout structure is for
     * a routine arranging
     * to be called by the clock interrupt
     * (clock.c) with a specified argument,
     * in a specified amount of time.
     * Used, for example, to time tab
     * delays on typewriters.
     */
    
    struct  callout {
        int     c_time;             /* incremental time */
        caddr_t c_arg;              /* argument to routine */
        int     (*c_func)();        /* routine */
        struct  callout *c_next;
    };
    

  • Cours 5

    Scheduling

  • la priorité p_usrpri d'un processus descend de 1 tous les 4 ticks (4 * 1/60 s). La priorité ne peut être plus grande que 127.

    p_usrpri = PUSER + Partie_Entière{p_cpu / 4} + 2 * p_nice

  • un amortissement est fait par le filtre

    p_cpu = {2 load} / {2 load + 1} p_cpu + p_nice

  • le scheduler schedcpu() de sys/sys/kern_synch.c recalcule la priorité toutes les secondes, grâce à un réveil
        timeout(schedcpu, (caddr_t)0, hz);
    

  • Cours 5

    Ordonnancement des processus -- Scheduling

    On a un ensemble de processeurs P = {P1, P2, ... Pm} et un ensemble de ressources R = {R1, R2, ... Rs}. Un système de tâches est donné par un quadruplet (F, <, [tij], {Ri}, {wj}) où:
    1. F = {T1, T2, ..., Tn} ensemble des tâches,
    2. < est une ordre partiel entre les tâches (dépendance),
    3. [tij] est une matrice m * n des temps d'exécution. Si tous les processeurs sont identiques, on écrira ti,
    4. wi coûts ou priorités,
    Soient si et fi les temps de début et de fin de Ti, soit S un ordonnancement particulier. On a plusieurs coûts possibles:

    omega(S) = max {fi(S) | 1 <= i <= n}


    ou

    omega'(S) = 1 / n Somme(wi * fi(S)) de 1 à N

    TIT     .COMPUTER & JOB/SHOP SCHEDULING THEORY
    AUT     .Coffman, Edward G.
    COL     .John Wiley & Sons, 1976
    

    Cours 5

    Créations de processus -- Fin de processus

  • fork() dans sys/sys/kern_fork.c, qui appelle newproc(). Tout est copié (y compris la structure u). Quelques ajustement sont faits dans la structure proc.

  • exit() dans sys/sys/kern_exit.c relâce les ressources, sauve la structure u pour que le parent puisse la consulter, entre dans l'état ZOMBIE, réveille le parent et réarrange les processus fils.

  • Cours 5

    Etats d'un processus -- proc.h

    /* stat codes */
    #define SSLEEP  1               /* awaiting an event */
    #define SWAIT   2               /* (abandoned state) */
    #define SRUN    3               /* running */
    #define SIDL    4               /* intermediate state in process creation */
    #define SZOMB   5               /* intermediate state in process termination */
    #define SSTOP   6               /* process being traced */
    
    /* flag codes */
    #define SLOAD   0x0000001       /* in core */
    #define SSYS    0x0000002       /* swapper or pager process */
    #define SLOCK   0x0000004       /* process being swapped out */
    #define SSWAP   0x0000008       /* save area flag */
    #define STRC    0x0000010       /* process is being traced */
    #define SWTED   0x0000020       /* stpd proc not been waited for by parent */
    #define SULOCK  0x0000040       /* user settable lock in core */
    #define SPAGE   0x0000080       /* process in page wait state */
    #define SKEEP   0x0000100       /* another flag to prevent swap out */
    #define SOMASK  0x0000200       /* restore old mask after taking signal */
    #define SWEXIT  0x0000400       /* working on exiting */
    #define SPHYSIO 0x0000800       /* doing physical i/o (bio.c) */
    #define SVFORK  0x0001000       /* process resulted from vfork() */
    #define SVFDONE 0x0002000       /* another vfork flag */
    #define SNOVM   0x0004000       /* no vm, parent in a vfork() */
    #define SPAGI   0x0008000       /* init data space on demand, from inode */
    #define SSEQL   0x0010000       /* user warned of sequential vm behavior */
    #define SUANOM  0x0020000       /* user warned of random vm behavior */
    #define STIMO   0x0040000       /* timing out during sleep */
    /* was SDETACH */
    #define SNOCLDSTP       0x0100000       /* POSIX child stop flag */
    /* was SOUSIG */
    #define SOWEUPC 0x0200000       /* owe process an addupc() call at next ast */
    #define SSEL    0x0400000       /* selecting; wakeup/waiting danger */
    #define SLOGIN  0x0800000       /* a login process (legit child of init) */
    #define SPTECHG 0x1000000       /* ptés for process have changed */
    

    Cours 5

    Synchronisation dans le noyau

  • sleep(chan, pri) a 2 arguments. Le 1er argument est une adresse mémoire quelconque, par exemple
        sleep((caddr_t)&runin, PSWP);
    
    pour attendre avec une forte priorité qu'un swap in soit possible. Si la priorité est faible, on teste les signaux avant de s'endormir.

  • si on part sur un signal dans un sleep() faible, on retourne grâce à un goto non local longjmp(&u.u_qsave) vers la fin d'une entrée/sortie, ou vers la fin d'un wait().

  • l'abandon du cpu se fait en appelant swtch().

  • wakeup(chan) réveille les processus endormis sur ce canal. Le réveil est d'autant plus fort que la priorité est forte (i.e. plus proche de 0). La priorité est fixée par le 2ème argument de sleep(). Si la priorité de réveil est faible, on teste les signaux.

  • swtch() se fait dans sys/mips/mips/swtch.c qui appelle 2 procédures save() et resume() de locore.s qui agissent comme des goto non locaux.

  • Cours 5

    Exercices en TD et à la maison

    Finir les exercices de la dernière fois.