Python: općenite usluge operacijskog sustava: osnovna sučelja
-
module os
(službena dokumentacija) pruža prenosiv način za pristup sučeljima operacijskog sustava- prenosiv u terminima da radi na više različitih operacijskih sustava (operacijski sustavi slični Unixu i Windows)
- u dokumentaciji svake pojedine funkcije piše dostupnost; velik broj funkcija su specifične za operacijske sustave slične Unixu ili dostupne na operacijskim sustavima sličnim Unixu i na Windowsima, a manji broj funkcija su isključivo dostupne na Windowsima
Dohvaćanje informacija i izvođenje osnovnih operacija
-
os.uname()
vraća informacije o operacijskom sustavu u objektu koji ima atributesysname
,nodename
,release
,version
imachine
, slično kao naredbauname
; primjerice, kodimport os uname = os.uname() print(uname.sysname)
ispisat će
Linux
,FreeBSD
, ... (radi samo na operacijskim sustavima sličnim Unixu) -
os.getlogin()
vraća korisničko ime korisnika kojem pripada proces (radi na operacijskim sustavima sličnim Unixu i na Windowsima) os.kill(pid, sig)
šalje signalsig
procesu s PID-ompid
, slično kao naredbakill
os.nice(increment)
povećava niceness procesa, slično kao naredbanice
os.getpid()
vraća PID procesa interpreteraos.getppid()
vraća PID roditelja (PPID) procesa interpretera
Zadatak
Provjerite u službenoj dokumentaciji za preostale funkcije istaknute iznad rade li na (nekim) operacijskim sustavima sličnim Unixu i/ili na Windowsima.
Zadatak
Napravite program koji radi jednostavan login. Korisnika se pita da upiše korisničko ime, a onda se provjerava je li to korisničko ime isto kao ime korisnika kojem pripada proces. Ako je, ispisuje se PID procesa i PID roditelja procesa, a korisniku se nudi mogućnost unosa dva broja: prvi broj je PID procesa, a drugi je signal. Program zatim šalje uneseni signal procesu s PID-em koji je korisnik unio.
-
razlikujemo dva tipa korisničkih (UID) i grupnih ID-eva (GID)
- stvarni UID i GID utječu na dozvole za slanje signala procesima
- efektivni UID i GID utječu na dozvole za stvaranje i pristup datotekama
-
uglavnom će biti isti dok ih ne promijenimo funkcijama za postavljanje ID-eva
-
os.getuid()
dohvaća stvarni UID os.setuid(uid)
postavljan stvarni UID na brojačnu vrijednostuid
os.getgid()
dohvaća stvarni GID-
os.setgid(gid)
postavljan stvarni GID na brojčanu vrijednostgid
-
os.geteuid()
dohvaća efektivni UID os.seteuid(euid)
postavljan efektivni UID na brojčanu vrijednosteuid
os.getegid()
dohvaća efektivni GID-
os.setegid(egid)
postavljan efektivni GID na brojčanu vrijednostegid
-
os.getgroups()
dohvaća popis grupa korisnika os.setgroups(groups)
postavlja popis grupa korisnika na listu brojčanih vrijednostigroups
Zadatak
Provjerite koji su stvarni UID i GID te efektivni UID i GID procesa, a zatim ih pokušajte promijeniti na vrijednost 0 (UID korisnika root
i GID grupe root
ili wheel
). Objasnite zašto to možete ili ne možete napraviti.
Zadatak
Na temelju:
- stvarnog UID-a i sadržaja datoteke
/etc/passwd
, te - stvarnog GID-a i sadržaja datoteke
/etc/group
,
saznajte ime korisnika i grupe.
os.umask(mask)
postavlja korisničku masku na vrijednostmask
os.getcwd()
vraća trenutni radni direktorij procesaos.chdir(path)
mijenja trenutni radni direktorij procesa upath
os.listdir(path)
izlistava sadržaj direktorija, kao rezultat vraća listu znakovnih nizova koji su imena datoteka i poddirektorija
Zadatak
Implementirajte naredbu ls
, odnosno napravite Python program koji:
- ako je pozvan bez argumenata, izlistava trenutni radni direktorij,
- ako je pozvan s jednim argumentom, izlistava sadržaj direktorija čija je putanja navedenu u argumentu (nije potrebno provjeriti je li ispravno unesena od strane korisnika).
-
os.ttyname(fd)
vraća ime terminala koji je povezan na opisnik datotekefd
- opisnici datoteka koje ima svaki proces su
0
(standardni ulaz),1
(standardni izlaz) i2
(standardni izlaz za greške); npr.os.ttyname(0)
vratit će ime terminala povezanog na standardni ulaz - otvaranjem datoteka funkcijom
open()
proces dobiva opisnike datoteka3
,4
, itd.
- opisnici datoteka koje ima svaki proces su
Zadatak
Provjerite imena terminala povezanih na opisnike datoteka 0
, 1
, 2
u situacijama:
- kada je Python program pokrenut bez preusmjeravanja ulaza i izlaza,
- kada je Python program pokrenut s preusmjeravanjem standardnog izlaza, te
- kada je Python program pokrenut s preusmjeravanjem standardnog izlaza i standardnog izlaza za greške.
-
os.environ
je rječnik varijabli okoline oblika{'VAR1': 'vrijednost1', 'VAR2': 'vrijednost2', ... }
- dohvaćanje vrijednosti pojedine varijable:
os.environ['IME_VARIJABLE']
ilios.getenv('IME_VARIJABLE')
- dodavanje nove varijable i postavljanje njene vrijednosti:
os.environ['IME_VARIJABLE'] = 'vrijednost varijable'
- dohvaćanje vrijednosti pojedine varijable:
Zadatak
- Ispište prvo u ljusci, a zatim u Python programu vrijednosti varijabli
USER
,SHELL
,LANG
,PPID
,PWD
,RANDOM
i_
. Postoje li sve u oba slučaja i imaju li iste vrijednosti? - Postavite u programu varijablu okoline
KOLEGIJ
na vrijednostOperacijski sustavi 2
i uvjerite se prvo da je zaista uspješno postavljena, a zatim provjerite je li postavljena i u ljusci nakon završetka izvođenja programa.
Pokretanje procesa i podprocesa
-
os.exec{l,v{,p,e,pe}}()
pokreće proces koji zamjenjuje trenutni proces- novi proces zadržava PID i okolinu
- stog, hrpa i podaci procesa zamjenjuju se novim
- l -- argumenti naredbe navedeni su kao argumenti funkcije
execl()
- v -- argumenti naredbe navedeni su kao lista koja je jedan arugment funkcije
execv()
- p -- iskoristi varijablu okoline
$PATH
za pronalaženje datoteke za pokretanje - e -- kod pokretanja naredbe prosljeđuje se i rječnik varijabli okoline koje se dodaju
os.environ
-
os.execl()
ima četiri varijante;arg0
je ime naredbe,arg1
i ostali su argumenti koje naredba prima:os.execl(path, arg0, arg1, ...)
os.execle(path, arg0, arg1, ..., env)
os.execlp(file, arg0, arg1, ...)
os.execlpe(file, arg0, arg1, ..., env)
-
os.execv()
također ima četiri varijante;args[0]
je ime naredbe,args[1]
i ostali su argumenti koje naredba prima:os.execv(path, args)
os.execve(path, args, env)
os.execvp(file, args)
os.execvpe(file, args, env)
-
primjerice, kako bismo pokrenuli
cal
koja se nalazi u/usr/bin/cal
s parametrom-3
i argumentima5
i2020
(prikaz kalendara za travanj, svibanj i lipanj 2020. godine, naredba u ljuscical -3 5 2020
), pozvat ćemo bilo koju od sljedećih funkcija:os.execl('/usr/bin/cal', 'cal', '-3', '5', '2020')
os.execv('/usr/bin/cal', ['cal', '-3', '5', '2020'])
os.execlp('cal', 'cal', '-3', '5', '2020')
os.execvp('cal', ['cal', '-3', '5', '2020'])
-
za ilustraciju prosljeđivanja varijabli okoline, razmotrimo DEC VT52, hardverski terminal iz 1975. godine koji se može emulirati postavljanjem varijable ljuske
$TERM
na vrijednostvt52
i na kojemu ne postoji mogućnost inverzije boje pozadine i boje teksta pa naredbacal
kod ispisa ne označava trenutni datum; naredba u ljusciTERM=vt52 cal -3 5 2020
imat će isti rezultat kao pozivi funkcija:os.execl('/usr/bin/cal', 'cal', '-3', '5', '2020', {'TERM': 'vt52'})
os.execv('/usr/bin/cal', ['cal', '-3', '5', '2020'], {'TERM': 'vt52'})
os.execlp('cal', 'cal', '-3', '5', '2020', {'TERM': 'vt52'})
os.execvp('cal', ['cal', '-3', '5', '2020'], {'TERM': 'vt52'})
Zadatak
- Napišite Python skriptu koja pokreće
ls
na direktorij/etc
tako da iskoristite varijantu funkcijeexec()
koja za traženje naredbe koristi varijablu okoline$PATH
. - Napišite Python skriput koja pokreće
cal
da ispiše kalendar srpanj 2020. godine i to tako da se kod pokretanja okolina modificira tako da$LANG
poprimi vrijednosten_US.UTF-8
.
-
zombie proces, ponekad nazvan mrtav proces (engl. defunct process), proces koji je završio s izvođenjem ali i dalje ima unos u tablici procesa zbog potrebe da njegov roditelj pročita izlazni status
-
os.fork()
stvara kopiju procesa; procesu roditelju vraća PID procesa djeteta, procesu djetetu vraća 0 (na temelju toga ih razlikujemo)import os child_pid = os.fork() print("Ovo ispisuju oba procesa") if child_pid == 0: print("Ovo ipisuje samo proces dijete") else: print("Ovo ispisuje samo proces roditelj")
-
os.wait()
čeka na završetak izvođenja procesa djeteta; vraća uređeni par(pid, status)
import os child_pid = os.fork() print("Ovo ispisuju oba procesa") if child_pid == 0: print("Ovo ipisuje samo proces dijete") else: print("Ovo ispisuje samo proces roditelj") pid, status = os.wait() print("Proces dijete s PID-om", pid, "završio je izvođenje s izlaznim statusom", status)
Zadatak
Napišite Python skriptu koja radi fork()
i u procesu djetetu pokreće proces koji izlistava trenutni direktorij, a zatim čeka na završetak procesa djeteta i ispisuje na ekran njegov izlazni status. Razmislite na koji ćete način osigurati da se naredba pokreće samo u procesu djetetu.
(Vremenom ćemo naučiti koristiti elegantnije sučelje za baratanje potprocesima, u sklopu modula subprocess
.)
-
grupa procesa je skup koji se sastoji od jednog ili više procesa
- identifikator grupe procesa (engl. Process Group Identifier, PGID) jednak je PID-u procesa koji je voditelj grupe
- specijalno, kontrolne grupe (engl. control groups, kraće cgroups) omogućuju ograničavanje i praćenje korištenja resursa od strane grupa procesa
-
sesija je skup koji se sastoji od jedne ili više procesnih grupa
- postoji definiran proces koji je voditelj sesije
- procesi jedne sesije mogu stvarati procesne grupe samo unutar te sesije
-
os.getsid(pid)
vraća id sesije procesa s PID-ompid
-
os.setsid()
stvara novu sesiju u kojoj trenutni proces postaje voditelj i sesije i vlastite procesne grupe -
upravljački terminal (engl. controlling terminal, CTTY) je terminal koji može biti pridružen sesiji
- odlučuje o stvarima kao što su koji proces prima ulaz s tipkovnice i kako procesi primaju informacije o promjeni veličine prozora terminala
- upravljački terminal se može mijenjati
- sesija koja nema upravljački terminal dobiva ga kad voditelj otvori prvu datoteku
-
daemon proces je proces pokrenut u pozadini, bez terminala koji njime upravlja, često bez roditelja zbog čega ga posvaja proces s PID-om 1 (
init
)- primjeri takvih procesa su
sshd
,httpd
,syslogd
,ospfd
,ntpd
,acpid
, ... -
nastaje dvostrukim forkanjem (tzv. double fork magic, poznat i pod nazivom daemon fork) uz neke dodatne operacije:
- prvi
fork()
služi za stvaranje procesa djeteta koji zatim mijenja radni direktorij u/
, postavlja korisničku masku na 0 i odvaja se od upravljačkog terminala i procesa roditelja tako što postaje voditelj vlastite sesije i procesne grupe pozivom funkcijesetsid()
; - drugi
fork()
osigurava da proces prestaje biti voditelj sesije, kako ne bi bio spojen na upravljački terminal. - nakon svakog
fork()
-a prekida se izvođenja procesa roditelja (npr. pozivom funkcijesys.exit()
), što se popularno naziva fork off and die
- prvi
- primjeri takvih procesa su
Zadatak
Napišite program koji izvodi dvostruki fork i postaje daemon, a zatim ga pokrenite i uvjerite se pregledom izlaza naredbe pstree
da je init
posvojio vaš pokrenuti proces.
Zadatak
Modificirajte kod koji radi dvostruki fork tako da proces koji je daemon čini sljedeće:
- u kućnom direktoriju korisnika koji ga je pokrenuo ili u direktoriju
/tmp
(prema vašem izboru) otvara datotekumojdaemon.log
za zapisivanje, - saznaje UID i korisničko ime korisnika koji ga je pokrenuo i ispisuje ga na ekran, a zatim u datoteku,
- saznaje ime operacijskog sustava i ispisuje ga na ekran, a zatim u datoteku,
- spava 5 sekundi,
- ispisuje na ekran trenutni datum i vrijeme, a zatim u datoteku,
- ispisuje na ekran vrijednost varijable okoline
$PATH
, - uspisuje u datoteku niz znakova
=== kraj izvođenja procesa s PID-em {} ===
(na mjestu{}
je PID procesa), a zatim zatvara datoteku.
Author: Vedran Miletić