Δομή του λειτουργικού συστήματος Unix

Διομήδης Σπινέλλης
Τμήμα Διοικητικής Επιστήμης και Τεχνολογίας
Οικονομικό Πανεπιστήμιο Αθηνών
dds@aueb.gr

Ιστορία

Χρονολογικός πίνακας του Unix

Τέλη δεκαετίας 1960
Τα Bell Labs (AT&T), GE και το MIT υλοποιούν το MULTICS (Multiplexed Information and Computing Services), ένα πολυχρηστικό λειτουργικό σύστημα. Το 1969 τα Bell Labs αποσύρονται από το έργο όταν αυτό έγινε υπερβολικά ακριβό και περίπλοκο.
1969
Ο Ken Thompson (BL) αναπτύσσει λειτουργικό σύστημα για έναν χρήστη για τον υπολογιστή DEC PDP-7
1970
Ο Brian Kernighan σχηματίζει το όνομα UNIX ως λογοπαίγνιο του MULTICS. Το σύστημα μεταφέρεται στο PDP-11. Υποστηρίζει δύο χρήστες, το παγνίδι Spacewar, και επεξεργασία κειμένου.
1973
Ο Dennis Ritchie αναπτύσσει σε συνεργασία με τον Thompson τη γλώσσα προγραμματισμού C και ξαναγράφουν το Unix στη γλώσσα αυτή.
Μέσα δεκαετίας 1970
Το UNIX (Version 6) διατίθεται δωρεάν σε πανεπιστήμια. Μεταφέρεται και σε άλλους υπολογιστές. Όλος ο πυρήνας αποτελείται από λιγότερες από 10000 γραμμές κώδικα.
Τέλη δεκαετίας 1970
Εργασία του Thompson μαζί με τον Bill Joy στο Πανεπιστήμιο του Berkeley (UCB) καταλήγει στην Berkeley Software Distribution (BSD). Αυτή υποστηρίζει μεταξύ άλλων ιδεατή μνήμη και σελιδοποίηση.
1980
Η Microsoft παράγει το XENIX. Κυκλοφορεί η πρώτη έκδοση 32 bit, το BSD 4.1.
1983
Κυκλοφορεί νέα έκδοση από την AT&T με το όνομα System V. Η AT&T την προωθεί ως πρότυπο με τελική μορφή την έκδοση SVR4 μετά την υιοθέτηση χαρακτηριστικών του BSD 4.2. Παράλληλα η Sun κυκλοφορεί το SunOS με τελική μορφή το Solaris. Το Unix διαχωρίζεται σε αντίπαλα στρατόπεδα.
1985
Η AT&T δημοσιεύει το πρότυπο SVID (System V Interface Definition). Η ομάδα X/OPEN (αργότερα UNIX International) δημοσιεύει αντίστοιχο πρότυπο. Το πρότυπο POSIX 1003.1 υιοθετείται ως κοινό πεδίο.
1988
Η Open Software Foundation (OSF) ανταγωνίζεται την UI με βάση το AIX της IBM. Στο πανεπιστήμιο Carnegie Mellon αναπτύσσεται ο μικρο-πυρήνας MACH.
Δεκαετία 1990
Αναπτύσσονται και διανέμονται εκδόσεις του Unix που δε βασίζονται σε κώδικα της AT&T. Τα BSDI, 386BSD, NetBSD και FreeBSD βασίζονται στη διανομή Net/2 και αργότερη στην 4.4BSD Lite του Berkeley με κώδικα για τον επεξεργαστή Intel 386 από τον Bill Jolitz, ενώ το Linux βασίζεται σε κώδικα που έγραψε από την αρχή ο Linux Torvalds.

Γενεαλογικό δένδρο του Unix

Το παρακάτω σχήμα (βασισμένο στο βιβλίο Life with Unix) παριστάνει τη γενεαλογία του Unix:

Κλήσεις του λειτουργικού συστήματος

Όλες οι κλήσεις του λειτουργικού συστήματος χαρακτηρίζονται εσωτερικά από έναν αριθμό με βάση τον οποίο δρομολογούνται στον αντίστοιχο κώδικα του πυρήνα. Ο παρακάτω πίνακας περιέχει τις πρώτες 20 κλήσεις του FreeBSD Unix:
#define SYS_syscall     0
#define SYS_exit        1
#define SYS_fork        2
#define SYS_read        3
#define SYS_write       4
#define SYS_open        5
#define SYS_close       6
#define SYS_wait4       7
                                /* 8 is old creat */
#define SYS_link        9
#define SYS_unlink      10
                                /* 11 is obsolete execv */
#define SYS_chdir       12
#define SYS_fchdir      13
#define SYS_mknod       14
#define SYS_chmod       15
#define SYS_chown       16
#define SYS_break       17
#define SYS_getfsstat   18
                                /* 19 is old lseek */
#define SYS_getpid      20

Για παράδειγμα ο παρακάτω κώδικας του πυρήνα υλοποιεί την κλήση getpid:

int
getpid(p, uap)
        struct proc *p;
        struct getpid_args *uap;
{
        p->p_retval[0] = p->p_pid;
        return (0);
}

Διεργασίες

Κάθε διεργασία φυλάσσεται στον πίνακα διεργασιών με βάση τα παρακάτω πεδία:
struct  proc {
        TAILQ_ENTRY(proc) p_procq;      /* run/sleep queue. */
        LIST_ENTRY(proc) p_list;        /* List of all processes. */

        /* substructures: */
        struct  pcred *p_cred;          /* Process owner's identity. */
        struct  filedesc *p_fd;         /* Ptr to open files structure. */
        struct  pstats *p_stats;        /* Accounting/statistics (PROC ONLY). */
        struct  plimit *p_limit;        /* Process limits. */
        struct  vm_object *p_upages_obj;/* Upages object */
        struct  sigacts *p_sigacts;     /* Signal actions, state (PROC ONLY). */


        int     p_flag;                 /* P_* flags. */
        char    p_stat;                 /* S* process status. */
        char    p_pad1[3];

        pid_t   p_pid;                  /* Process identifier. */
        LIST_ENTRY(proc) p_hash;        /* Hash chain. */
        LIST_ENTRY(proc) p_pglist;      /* List of processes in pgrp. */
        struct  proc *p_pptr;           /* Pointer to parent process. */
        LIST_ENTRY(proc) p_sibling;     /* List of sibling processes. */
        LIST_HEAD(, proc) p_children;   /* Pointer to list of children. */

        struct callout_handle p_ithandle; /*
                                              * Callout handle for scheduling
                                              * p_realtimer.
                                              */

        pid_t   p_oppid;         /* Save parent pid during ptrace. XXX */
        int     p_dupfd;         /* Sideways return value from fdopen. XXX */

        struct  vmspace *p_vmspace;     /* Address space. */

        /* scheduling */
        u_int   p_estcpu;        /* Time averaged value of p_cpticks. */
        int     p_cpticks;       /* Ticks of cpu time. */
        fixpt_t p_pctcpu;        /* %cpu for this process during p_swtime */
        void    *p_wchan;        /* Sleep address. */
        const char *p_wmesg;     /* Reason for sleep. */
        u_int   p_swtime;        /* Time swapped in or out. */
        u_int   p_slptime;       /* Time since last blocked. */

        struct  itimerval p_realtimer;  /* Alarm timer. */
        struct  timeval p_rtime;        /* Real time. */
        u_quad_t p_uticks;              /* Statclock hits in user mode. */
        u_quad_t p_sticks;              /* Statclock hits in system mode. */
        u_quad_t p_iticks;              /* Statclock hits processing intr. */
        struct  timeval *p_sleepend;    /* Wake time for nanosleep & friends */

        int     p_traceflag;            /* Kernel trace points. */
        struct  vnode *p_tracep;        /* Trace to vnode. */

        int     p_siglist;              /* Signals arrived but not delivered. */

        struct  vnode *p_textvp;        /* Vnode of executable. */

        char    p_lock;                 /* Process lock (prevent swap) count. */
        char    p_oncpu;                /* Which cpu we are on */
        char    p_lastcpu;              /* Last cpu we were on */
        char    p_pad2;                 /* alignment */

        short   p_locks;                /* DEBUG: lockmgr count of held locks */
        short   p_simple_locks;         /* DEBUG: count of held simple locks */
        register_t p_retval[2];         /* syscall aux returns */

        sigset_t p_sigmask;     /* Current signal mask. */
        sigset_t p_sigignore;   /* Signals being ignored. */
        sigset_t p_sigcatch;    /* Signals being caught by user. */

        u_char  p_priority;     /* Process priority. */
        u_char  p_usrpri;       /* User-priority based on p_cpu and p_nice. */
        char    p_nice;         /* Process "nice" value. */
        char    p_comm[MAXCOMLEN+1];

        struct  pgrp *p_pgrp;   /* Pointer to process group. */

        struct  sysentvec *p_sysent; /* System call dispatch information. */

        struct  rtprio p_rtprio;        /* Realtime priority. */

        struct  user *p_addr;   /* Kernel virtual addr of u-area (PROC ONLY). */
        struct  mdproc p_md;    /* Any machine-dependent fields. */

        u_short p_xstat;        /* Exit status for wait; also stop signal. */
        u_short p_acflag;       /* Accounting flags. */
        struct  rusage *p_ru;   /* Exit information. XXX */

        int     p_nthreads;     /* number of threads (only in leader) */
        void    *p_aioinfo;     /* ASYNC I/O info */
        int     p_wakeup;       /* thread id */
        struct proc *p_peers;   
        struct proc *p_leader;
};
Κάθε διεργασία μπορεί να βρίσκεται σε μια από τις παρακάτω καταστάσεις:
#define SIDL    1               /* Process being created by fork. */
#define SRUN    2               /* Currently runnable. */
#define SSLEEP  3               /* Sleeping on an address. */
#define SSTOP   4               /* Process debugging or suspension. */
#define SZOMB   5               /* Awaiting collection by parent. */

Διαχείριση αρχείων

Η διαχείριση αρχείων βασίζεται στη δομή δεδομένων του πίνακα δεικτών, inode, η οποία περιέχει πληροφορίες για κάθε αρχείο. Τα βασικά στοιχεία της δομής που βρίσκονται στη μνήμη περιέχουν στοιχεία για τη διαχείριση του πίνακα των δεικτών:
struct inode {
        struct   lock i_lock;   /* Inode lock. >Keep this first< */
        LIST_ENTRY(inode) i_hash;/* Hash chain. */
        struct  vnode  *i_vnode;/* Vnode associated with this inode. */
        struct  vnode  *i_devvp;/* Vnode for block I/O. */
        u_int32_t i_flag;       /* flags, see below */
        dev_t     i_dev;        /* Device associated with the inode. */
        ino_t     i_number;     /* The identity of the inode. */

        union {                 /* Associated filesystem. */
                struct  fs *fs;         /* FFS */
                struct  lfs *lfs;       /* LFS */
                struct  ext2_sb_info *e2fs;     /* EXT2FS */
        } inode_u;
        struct   dquot *i_dquot[MAXQUOTAS]; /* Dquot structures. */
        u_quad_t i_modrev;      /* Revision level for NFS lease. */
        struct   lockf *i_lockf;/* Head of byte-level lock list. */
        /*
         * The on-disk dinode itself.
         */
        struct  dinode i_din;   /* 128 bytes of the on-disk dinode. */
};
Επιπλεόν το τμήμα κάθε πίνακα δεικτών που βρίσκεται στο δίσκο περιέχει τα ιδιοχαρακτηριστικά του αρχείου, δείκτες για τα τμήματα που περιέχουν δεδομένα του αρχείου, καθώς και δείκτες σε τμήματα που περιέχουν δείκτες σε τμήματα που περιέχουν δεδομένα του αρχείου.
struct dinode {
        u_int16_t       di_mode;        /*   0: IFMT, permissions; see below. */
        int16_t         di_nlink;       /*   2: File link count. */
        u_int64_t       di_size;        /*   8: File byte count. */
        int32_t         di_atime;       /*  16: Last access time. */
        int32_t         di_atimensec;   /*  20: Last access time. */
        int32_t         di_mtime;       /*  24: Last modified time. */
        int32_t         di_mtimensec;   /*  28: Last modified time. */
        int32_t         di_ctime;       /*  32: Last inode change time. */
        int32_t         di_ctimensec;   /*  36: Last inode change time. */
        ufs_daddr_t     di_db[NDADDR];  /*  40: Direct disk blocks. */
        ufs_daddr_t     di_ib[NIADDR];  /*  88: Indirect disk blocks. */
        u_int32_t       di_flags;       /* 100: Status flags (chflags). */
        int32_t         di_blocks;      /* 104: Blocks actually held. */
        int32_t         di_gen;         /* 108: Generation number. */
        u_int32_t       di_uid;         /* 112: File owner. */
        u_int32_t       di_gid;         /* 116: File group. */
        int32_t         di_spare[2];    /* 120: Reserved; currently unused */
};

Διαχείριση καταλόγων

Οι κατάλογοι αρχείων του συστήματος έχουν την παρακάτω δομή:
struct  direct {
        u_int32_t d_ino;                /* inode number of entry */
        u_int16_t d_reclen;             /* length of this record */
        u_int8_t  d_type;               /* file type, see below */
        u_int8_t  d_namlen;             /* length of string in d_name */
        char      d_name[MAXNAMLEN + 1];/* name with length <= MAXNAMLEN */
};

Σε κάθε κατάλογο μπορούν να φυλαχθούν οι παρακάτω τύποι αρχείων:
#define DT_UNKNOWN       0	/* Άγνωστο */
#define DT_FIFO          1	/* Ουρά FIFO (σωλήνωση) */
#define DT_CHR           2	/* Συσκευή χαρακτήρων */
#define DT_DIR           4	/* Κατάλογος */
#define DT_BLK           6	/* Συσκευή τμημάτων */
#define DT_REG           8	/* Κανονικό αρχείο */
#define DT_LNK          10	/* Δεσμός */
#define DT_SOCK         12	/* Διαδιεργασιακή σύνδεση */

Διαχείριση συστημάτων αρχείων

Το σύστημα Unix υποστηρίζει πολλά διαφορετικά συστήματα αρχείων (Unix file system, Fast File System, ISO-9660 (CD-ROM file system), FAT, κ.λπ.). Ένα ιδεατό σύστημα αρχείων δρομολογεί τις κλήσεις με βάση ένα αντικειμενοστρεφές μοντέλο προς την υλοποίηση του αντίστοιχου συστήματος. Κάθε σύστημα αρχείων υποστηρίζει ορισμένες από τις παρακάτω κλήσεις:
access(struct vop_access_args *);
advlock(struct vop_advlock_args *);
chmod(struct vnode *, int, struct ucred *, struct proc *);
chown(struct vnode *, uid_t, gid_t, struct ucred *, struct proc *);
close(struct vop_close_args *);
create(struct vop_create_args *);
getattr(struct vop_getattr_args *);
link(struct vop_link_args *);
makeinode(int mode, struct vnode *, struct vnode **, struct componentname *);
missingop(struct vop_generic_args *ap);
mkdir(struct vop_mkdir_args *);
mknod(struct vop_mknod_args *);
mmap(struct vop_mmap_args *);
open(struct vop_open_args *);
print(struct vop_print_args *);
readdir(struct vop_readdir_args *);
readlink(struct vop_readlink_args *);
remove(struct vop_remove_args *);
rename(struct vop_rename_args *);
rmdir(struct vop_rmdir_args *);
setattr(struct vop_setattr_args *);
strategy(struct vop_strategy_args *);
symlink(struct vop_symlink_args *);

Σελιδοποίηση

Κάθε σελίδα συνδέεται με την παρακάτω δομή δεδομένων που περιγράφει τα χαρακτηριστικά της:
struct vm_page {
        TAILQ_ENTRY(vm_page) pageq;     /* queue info for FIFO queue or free list (P) */
        TAILQ_ENTRY(vm_page) hashq;     /* hash table links (O) */
        TAILQ_ENTRY(vm_page) listq;     /* pages in same object (O) */

        vm_object_t object;             /* which object am I in (O,P) */
        vm_pindex_t pindex;             /* offset into object (O,P) */
        vm_offset_t phys_addr;          /* physical address of page */
        u_short queue;                  /* page queue index */
        u_short flags,                  /* see below */
                pc;                     /* page color */
        u_short wire_count;             /* wired down maps refs (P) */
        short hold_count;               /* page hold count */
        u_char  act_count;              /* page usage count */
        u_char  busy;                   /* page busy count */
        /* NOTE that these must support one bit per DEV_BSIZE in a page!!! */
        /* so, on normal X86 kernels, they must be at least 8 bits wide */
        u_char  valid;                  /* map of valid DEV_BSIZE chunks */
        u_char  dirty;                  /* map of dirty DEV_BSIZE chunks */
};
Αντίστοιχα η σελίδα μπορεί να βρίσκεται σε μια από τις παρακάτω καταστάσεις:
#define PG_BUSY         0x01            /* page is in transit (O) */
#define PG_WANTED       0x02            /* someone is waiting for page (O) */
#define PG_TABLED       0x04            /* page is in VP table (O) */
#define PG_FICTITIOUS   0x08            /* physical page doesn't exist (O) */
#define PG_WRITEABLE    0x10            /* page is mapped writeable */
#define PG_MAPPED       0x20            /* page is mapped */
#define PG_ZERO         0x40            /* page is zeroed */
#define PG_REFERENCED   0x80            /* page has been referenced */
#define PG_CLEANCHK     0x100           /* page has been checked for cleaning */
Όλες οι σελίδες ανοίκουν σε μια από 5 διαφορετικές λίστες ταξινομημένες σύμφωνα με το κριτήριο LRU (Least Recently Used - Λιγότερο πρόσφατη χρησιμοποιημένη).
free
Σελίδες έτοιμες για χρήση.
cache
Σελίδες σχεδόν έτοιμες για χρήση αφού ελευθερωθούν από τη χρήση βοηθητικής μνήμης.
inactive
Σελίδες που δεν έχουν χρησιμοποιηθεί πρόσφατα και είναι υποψήφιες για νέα χρήση.
active
Σελίδες που έχουν πρόσφατα χρησιμοποιηθεί.
zero
Ελεύθερες σελίδες που έχουν μηδενιστεί.

Βιβλιογραφία