2. Ohjelman suoritusympäristö
Sisältö:
2 Ohjelman suoritusympäristö
2.1 Komentoriviargumentit
Prosessi voidaan käynnistää komentotulkin kautta:$ prog -kc puppu.datKoodin suoritus alkaa funktiosta int main(int agrc, char *argv[]);. Komentotulkki välittää argumentit ohjelmalle pinossa. Niihin voi viitata muuttujilla argc ja argv[].
/* argc argumenttien lukumäärä argv[] ohjelman nimi ja argumentit merkkijonoina Yo. esimerkissä argc = 4 argv[0] = "prog" argv[1] = "-kc" argv[2] = "puppu.dat" argv[3] = NULL */ int main(int argc, char *argv[]) { int i; for (i=0; i < argc; i++) printf("argv[%d]: %s\n", i, argv[i]); exit(0); }
Prosessin suoritus päättyy "normaalisti", kun suoritetaan:
- main() funktiossa return(status)
- exit(status)
- _exit(status)
exit() tekee standardikirjastoihin liittyvää siivousta ja kutsuu edelleen funktiota _exit(). Normaalisti päättyvän ohjelman tulisi palauttaa 0. Käytä myös main-funktiossa funktiota exit(). Prosessin suoritus päättyy myös
- kun kutsutaan funktiota abort()
- saadaan signaali, johon ei ole varauduttu
Prosessin päättyessä suoritetaan funktiolla atexit() rekisteröidyt funktiot, viimeiseksi rekisteröity ensin.
#include < stdlib.h > int atexit(void (*func)(void)); static void my_exit1(void), my_exit2(void); int main(void) { if (atexit(my_exit2) != 0) perror("can't register my_exit2"); if (atexit(my_exit1) != 0) perror("can't register my_exit1"); if (atexit(my_exit1) != 0) perror("can't register my_exit1"); printf("main is done\n"); return(0); } static void my_exit1(void) { printf("first exit handler\n"); } static void my_exit2(void) { printf("second exit handler\n"); }
2.2 Ympäristömuuttujat
Tietoa suoritusympäristöstä voi välittää prosessille myös ympäristömuuttujien avulla. Komentoriviargumentit ovat tarkoitettu yksittäisen sovelluksen omien parametrien välittämiseen. Ympäristömuuttujien arvot pysyvät kauan samoina ja ne ovat usealle sovellukselle yhteisiä. Ympäristömuuttujalla on nimi ja arvo. Käytännössä se on merkkijono, joka on muotoa nimi=arvo Tcsh-komentotulkissa ympäristömuuttuja asetetaan komennolla setenv, esim.% setenv KURSSI UnsoJos ympäristömuuttuja halutaan asetaa vain yhden ohjelman suoritusajaksi, voi käyttää komentoa env, esim
$ env KURSSI=Unso prog -kc puppu.datOptiolla -i voi kieltää vielä äitiprosessin ympäristön periytymisen. Ympäristömuuttujaan viitattaessa on käytettävä tunnuksen edessä $-merkkiä
$ echo $KURSSIKaikkien ympäristömuuttujien arvot saa tcsh:ssä listattua komennolla
$ printenvLapsiprosessi perii äitiprosessilta ympäristömuuttujien arvot. Komentotulkki välittää ympäristömuuttujiensa arvot käynnistyvälle prosessille pinossa. Niihin voi viitata C-ohjelmassa muuttujan envp[] kautta.
int main(int argc,char *argv[],char *envp[]) envp[] ympäristömuuttujat merkkijonoina envp[0] = "ARCH=sun4" envp[1] = "DISPLAY=:0.0" ... envp[n] = NULL
Ympäristömuuttujia voi käsitellä myös esittelemällä koodissa muuttujan extern char **environ; ja käyttämällä sitten funktioita:
- char *getenv(const char *name); Palauta ympäristömuuttujan name arvo
- char *putenv(const char *str); Aseta ympäristömuuttujan arvo
printf("Tunnus on %s\n",getenv("USER")); if (putenv("KURSSI=Unso") == 0) ... onnistui ... else ... muistiongelmia ...
putenv() ei muuta main-funktion "envp"-argumenttia, sensijaan extern-muuttuja environ muuttuu. getenv() näkee uudet arvot. Lapsiprosessi ei voi muuttaa sen käynnistäneen prosessin ympäristömuuttujien arvoja.
2.3 Käyttäjätiedot
Unix on monen käyttäjän moniajojärjestelmä ja siksi tarvitaan resurssien hallittua jakelua, kiintiöintiä, tiedostojen yhteiskäyttöä ja tiedon suojausta luvattomalta käytöltä. Toteutus perustuu käyttäjätunnuksiin, salasanoihin ja käyttäjäryhmiin.
Käyttäjätunnukset ja salasanat "loytyvät" tiedostosta /etc/passwd Käyttäjäryhmät löytyvät tiedostosta /etc/group Tiedostojen käyttöoikeuteen liittyvät attribuutit, ns. rwx-bitit eli esim drwxr-xr-x tiedosto
Käyttäjän yksikäsitteinen tunniste on :
- käyttäjätunnus (username)
- käyttäjänumero (user ID, uid)
Käyttäjätunnusta tarvitaan sisäänkirjoittautumisessa ja käyttäjien välisessä kommunikoinnissa. Käyttäjänumeroa käytetään prosessien suorituksessa ja tiedostojen käyttöoikeuksien määräämisessä. Käyttäjän perustiedot ovat yhdellä rivillä tavallisesti tiedostossa
/etc/passwd (tai /var/yp/passwd tms.)Rivin rakenne on tavallisesti:
kjätunnus:salasana:uid:gid:gecos:hakemisto:ohjelma
Uid-numeron saa selville funktiolla getuid() ja käyttäjätunnuksen funktiolla getlogin(). Muut tiedot voi hakea käyttäjätunnuksen tai -numeron perusteella funktioilla getpwuid() ja getpwnam().
#include < pwd.h > struct passwd { char *pw_name; käyttäjätunnus char *pw_passwd; koodattu salasana uid_t pw_uid; käyttäjä numero gid_t pw_gid; ensisijainen ryhmä char *pw_age; salasanan ikä char *pw_comment; lisätietoa char *pw_gecos; lisätietoa char *pw_dir; kotihakemisto char *pw_shell; komentotulkki };
POSIX vaatii vain, että rakenteessa on vähintään kentät pw_name, pw_uid, pw_gid, pw_dir ja pw_shell. Ryhmän yksikäsitteinen tunniste on:
- ryhmätunnus (groupname)
- ryhmänumero (group ID)
Ryhmätunnusta käytetään prosessien suorituksessa ja tiedostojen käyttöoikeuksien määräämisessä. Ensisijainen ryhmä on kirjattu salasanatiedostoon. Sen lisäksi käyttäjä voi kuulua toissijaisiin ryhmiin. Toissijaisten ryhmien tiedot ovat tiedostossa:
/etc/groupRivin rakenne on:
ryhmätunnus:salasana:gid:jäsenten kjätunnukset
Komentotulkissa omat ryhmätunnukset saa komennolla
$ groups
Sovellus saa ensisijaisen ryhmätunnuksen funktiolla getgid() ja toisijaiset ryhmänumerot funktiolla getgroups().
#include < sys/types.h > #include < unistd.h > gid_t getgid(void); int getgroups(int gidsetsize, gid_t grouplist[]); Palauttaa: toissijaisten ryhmien lkm
Funktio täyttää taulukon grouplist toissijaisilla ryhmänumeroilla. Funktion ens. parametri on taulukon koko. Jos kooksi annetaan 0, palauttaa ryhmien lkm:n eli saa selville taulukon tilantarpeen. Muut tiedot voi hakea ryhmätunnuksen tai -numeron perusteella funktioilla getgrgid() ja getgrname().
#include < sys/types.h > #include < grp.h > struct passwd *getgrgid(gid_t gid); struct passwd *getgrnam(const char *name); Palauttaa: osoitin passwd-rakenteeseen #include < grp.h > struct group { char *gr_name; ryhmätunnus char *gr_passwd; koodattu salasana gid_t gr_gid; ryhmänu mero char **gr_mem; käyttäjätunnukset };
POSIX vaatii vain, että rakenteessa on vähintään kentät gr_name, gr_gid, ja gr_mem. Koko ryhmätiedoston käsittelyä varten on funktiot setgrent(), getgrent() ja endgrent().
#include < sys/types.h > #include < grp.h > void setgrent(void); void endgrent(void); struct group *getgrent(void); Palauttaa: osoitin seuraavaan group-rakenteeseen, NULL kun tdsto lopussa
2.4 Kirjanpito istuntojen kestosta
Tiedostoissa /var/adm/utmp tunnukset, joilla istunto menossa /var/adm/wtmp istuntojen alkamis- ja päättymisajat
Login-prosessi kirjoittaa rivin sekä utmp-tiedostoon että wtmp-tiedostoon. Logout-komennon suoritus poistaa rivin utmp-tiedostosta ja täydentää wtmp- tiedoston riviä. Komento
$ whonäyttää utmp-tiedoston sisällön ja komennolla
$ last käyttäjätunnusvoi tutkailla wtmp-tiedoston sisältöä.
2.5 Muita ympäristökyselyjä
Komento
$ uname -akertoo koneen nimen ja käyttöjärjestelmäversion. Sovellus voi kysyä vastaavat tiedot funktiolla uname().
#include < sys/utsname.h > struct utsname { char sysname[SYS_NMLN]; KJ:n nimi char nodename[SYS_NMLN]; Koneen solmunimi char release[SYS_NMLN]; KJ:n releasenumero char version[SYS_NMLN]; KJ:n versionumero char machine[SYS_NMLN]; Laitteiston tyyppi }; int uname(struct utsname *name); Palauttaa: ei-negatiivinen arvo
Pistokkeet (engl. socket) toteuttavissa järjestelmissä voi solmunimen kysyä komennolla:
$ hostnameja sovelluksessa funktiolla gethostname()
#includeKomentoint gethostname(char *name, int namelen);
$ hostidantaa koneen yksikäsitteisen tunnusnumeron. Sillä on käyttöä esim. ohjelmistolisenssien tarkistuksessa. Koneen solmunimi, käyttäjätunnus, käyttöjärjestelmän tyyppi yms. on yleensä asetettu myös sopivien ympäristömuuttujien (HOST, LOGNAME, ARCH, OS jne.) arvoiksi.
2.6 Aika ja kalenteri
Unix tallettaa esim. tiedoston attribuutteihin kalenteriajan sekunteina omassa sisäisessä esitysmuodossaan. Nollakohta on 00:00:00, 1.1.1970 GMT. Päiväys ja kellonaika saadaan komennolla date.
$ date Wed Jan 22 10:21:58 1996
Aikaviipaleita talletetaan kellokeskeytysten lukumääränä (engl. ticks). Sekunti on järjestelmästä riippuen 50, 60 tai 100 "tiksausta". Tässä muodossa on talletettu mm. prosessin kuvaajaan.
- Seinäkelloaika: aika, joka on kulunut prosessin suorituksen alkamisesta
- Prosessoriaika: aika, jonka prosessi on ollut suoritettavana prosessorissa. Se jaetaan
vielä osiin:
- aika, jonka prosessori on käyttänyt suorittaessaan prosessia käyttäjätilassa
- aika, jonka prosessori on käyttänyt suorittaessaan prosessia systeemitilassa (ts. suorittaessaan prosessin systeemikutsuja)
Komennon tai komentojonon suoritukseen kuluvat em. ajat saa komennolla time.
$ time grep xyz *.txt > /dev/null real 0m19.81s user 0m0.43s sys 0m4.53s
Sovellus saa kalenteriajan Unixin sisäisessä esitysmuodossa funktiolla time().
#include < time.h > time_t time(time_t *calptr); Palauttaa: kalenteriaika Unixin sisäisessä esitysmuodossa, myös muuttujassa calptr, jos ei NULL kutsuttaessa
Kalenteriajan saa pilkottua helpommin käsiteltäviin osiin funktioilla gmtime() ja localtime().
#include < time.h > struct tm { int tm_sec; sekunnit [0..61] int tm_min; minuutit [0..59] int tm_hour; tunnit [0..23] int tm_mday; päivä [1..31] int tm_mon; kuukausi[0..11] int tm_year; vuosi kahdella numerolla int tm_wday; vkonpvä [0..6], 0=sun int tm_yday; päivä vuoden alusta [0..365] int tm_isdst; 1, jos kesäaika }; struct tm *gmtime(const time_t *calptr); struct tm *localtime(const time_t *calptr);
Funktioille on myös käänteisfunktiot timegm() ja timelocal(). Rakenteena esitetyn ajan voi pakata Unixin esitysmuotoon funktiolla time_t mktime(struct tm *tmptr); Edellä esitetyt muodot (time_t ja struct tm) voi muuttaa merkkijonomuotoon funktioilla char *asctime(const struct tm *tmptr); ja char *ctime(const time_t *calptr); Molemmat palauttavat ajan muodossa (26 merkkiä):
Wed Jan 22 10:21:58 1997\n\0
tm-rakenteessa olevan ajan voi muuttaa vapaastimuotoilluksi merkkijonoksi funktiolla size_t strftime(char *buf, size_t buflen, const char *format, const struct tm *tmptr);, joka palauttaa taulukkoon buf talletettujen merkkien lukumäärän.
Jan Lindström (Jan.Lindstrom@cs.Helsinki.FI)