Tokie skaitytojai kaip jūs padeda palaikyti MUO. Kai perkate naudodami nuorodas mūsų svetainėje, galime uždirbti filialų komisinius.
Lenktynių sąlyga įvyksta, kai dvi operacijos turi būti atliekamos tam tikra tvarka, tačiau jos gali būti vykdomos priešinga tvarka.
Pavyzdžiui, kelių gijų programoje dvi atskiros gijos gali pasiekti bendrą kintamąjį. Dėl to, jei viena gija pakeičia kintamojo reikšmę, kita vis tiek gali naudoti senesnę versiją, nepaisydama naujausios reikšmės. Tai sukels nepageidaujamų rezultatų.
Norint geriau suprasti šį modelį, būtų naudinga atidžiai išnagrinėti procesoriaus perjungimo procesą.
Kaip procesorius perjungia procesus
Šiuolaikinės operacinės sistemos gali vienu metu vykdyti daugiau nei vieną procesą, vadinamą daugiafunkciniu. Kai pažvelgsite į šį procesą pagal CPU vykdymo ciklas, galite pastebėti, kad kelių užduočių atlikimas iš tikrųjų neegzistuoja.
Vietoj to, procesoriai nuolat perjungia procesus, kad juos paleistų vienu metu arba bent jau veiktų taip, lyg tai darytų. CPU gali nutraukti procesą jam nepasibaigus ir tęsti kitą procesą. Operacinė sistema kontroliuoja šių procesų valdymą.
Pavyzdžiui, Round Robin algoritmas, vienas iš paprasčiausių perjungimo algoritmų, veikia taip:
Paprastai šis algoritmas leidžia kiekvienam procesui vykdyti labai mažas laiko dalis, kaip nustato operacinė sistema. Pavyzdžiui, tai gali būti dviejų mikrosekundžių laikotarpis.
CPU paeiliui atlieka kiekvieną procesą ir vykdo komandas, kurios veiks dvi mikrosekundes. Tada jis tęsia kitą procesą, neatsižvelgiant į tai, ar dabartinis baigtas, ar ne. Taigi, galutinio vartotojo požiūriu, atrodo, kad vienu metu veikia daugiau nei vienas procesas. Tačiau pažvelgus į užkulisius, CPU vis tiek viską daro tvarkingai.
Beje, kaip parodyta aukščiau pateiktoje diagramoje, Round Robin algoritmui trūksta optimizavimo ar apdorojimo prioritetų sąvokų. Dėl to tai gana elementarus metodas, kuris retai naudojamas realiose sistemose.
Dabar, norėdami visa tai geriau suprasti, įsivaizduokite, kad veikia dvi gijos. Jei gijos pasiekia bendrą kintamąjį, gali atsirasti lenktynių sąlyga.
Žiniatinklio programos ir lenktynių sąlygos pavyzdys
Peržiūrėkite toliau pateiktą paprastą „Flask“ programą, kad apmąstytumėte konkretų visko, ką iki šiol skaitėte, pavyzdį. Šios programos tikslas – valdyti pinigų operacijas, kurios vyks internete. Išsaugokite toliau pateiktą failą pavadinimu pinigai.py:
iš kolba importuoti Kolba
iš kolba.ext.sqlalchemy importuoti SQLAlchemijaprograma = Kolba (__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy (programa)klasėsąskaita(db. Modelis):
id = db. Stulpelis (db. Sveikasis skaičius, pagrindinis_raktas = Tiesa)
suma = db. Stulpelis (db. String(80), unikalus = Tiesa)def__init__(savaime, skaičiuok):
savarankiškai.suma = sumadef__repr__(savarankiškai):
grąžinti '' % savęs.suma@app.route("/")
deflabas():
account = Account.query.get(1) # Yra tik viena piniginė.
grąžinti "Visi pinigai = {}".formatas (account.amount)@app.route("/send/")
defsiųsti(suma):
account = Account.query.get(1)jeigu int (account.amount) < suma:
grąžinti „Nepakankamas balansas. Iš naujo nustatyti pinigus su /reset!)"account.amount = int (account.amount) - suma
db.session.commit()
grąžinti "Išsiųsta suma = {}".formatas (suma)@app.route("/reset")
defnustatyti iš naujo():
account = Account.query.get(1)
sąskaita.suma = 5000
db.session.commit()
grąžinti „Pinigai iš naujo nustatyti“.
jeigu __name__ == "__main__":
app.secret_key = 'heLLoTHisIsSeCReTKey!'
app.run()
Norėdami paleisti šį kodą, sąskaitos lentelėje turėsite sukurti įrašą ir tęsti operacijas per šį įrašą. Kaip matote kode, tai yra bandomoji aplinka, todėl operacijos atliekamos pagal pirmąjį lentelės įrašą.
iš pinigų importuoti db
db.create_all()
iš pinigų importuoti sąskaita
sąskaita = sąskaita (5000)
db.sesija.papildyti(sąskaitą)
db.sesija.įsipareigoju()
Dabar sukūrėte paskyrą, kurios likutis yra 5 000 USD. Galiausiai paleiskite aukščiau pateiktą šaltinio kodą naudodami šią komandą, jei turite įdiegtus paketus Flask ir Flask-SQLAlchemy:
pitonaspinigų.py
Taigi jūs turite „Flask“ žiniatinklio programą, kuri atlieka paprastą ištraukimo procesą. Ši programa gali atlikti šias operacijas su GET užklausų nuorodomis. Kadangi pagal numatytuosius nustatymus „Flask“ veikia naudojant 5000 prievadą, adresas, kuriuo jį pasiekiate, yra 127.0.0.1:5000/. Programoje pateikiami šie galiniai taškai:
- 127.0.0.1:5000/ rodo esamą likutį.
- 127.0.0.1:5000/siųsti/{suma} atima sumą iš sąskaitos.
- 127.0.0.1: 5000 / iš naujo iš naujo nustato sąskaitą į 5000 USD.
Dabar, šiame etape, galite ištirti, kaip atsiranda lenktynių sąlygų pažeidžiamumas.
Rasės sąlygų pažeidžiamumo tikimybė
Aukščiau pateiktoje žiniatinklio programoje yra galimas lenktynių sąlygų pažeidžiamumas.
Įsivaizduokite, kad turite 5000 USD ir sukurkite dvi skirtingas HTTP užklausas, kurios atsiųs 1 USD. Norėdami tai padaryti, į nuorodą galite siųsti dvi skirtingas HTTP užklausas 127.0.0.1:5000/siųsti/1. Tarkime, kad kuo greičiau žiniatinklio serveris apdoroja pirmą užklausą, CPU sustabdo šį procesą ir apdoroja antrąją užklausą. Pavyzdžiui, pirmasis procesas gali sustoti paleidus šią kodo eilutę:
sąskaita.suma = tarpt(sąskaita.suma) – suma
Šis kodas apskaičiavo naują bendrą sumą, bet dar neišsaugojo įrašo duomenų bazėje. Kai prasidės antra užklausa, ji atliks tą patį skaičiavimą, atimdama 1 USD iš duomenų bazėje esančios vertės – 5 000 USD – ir išsaugos rezultatą. Kai pirmasis procesas bus atnaujintas, jis išsaugos savo vertę – 4 999 USD, o tai neatspindės naujausio sąskaitos likučio.
Taigi, dvi užklausos buvo įvykdytos ir kiekviena turėjo atimti 1 USD iš sąskaitos likučio, todėl naujas likutis yra 4 998 USD. Tačiau, atsižvelgiant į tai, kokia tvarka žiniatinklio serveris juos apdoroja, galutinis sąskaitos likutis gali būti 4 999 USD.
Įsivaizduokite, kad per penkias sekundes išsiunčiate 128 prašymus atlikti 1 USD pervedimą į tikslinę sistemą. Dėl šios operacijos numatomas sąskaitos išrašas bus 5 000–128 USD = 4 875 USD. Tačiau dėl lenktynių sąlygų galutinis likutis gali skirtis nuo 4 875 iki 4 999 USD.
Programuotojai yra vienas iš svarbiausių saugumo komponentų
Programinės įrangos projekte, kaip programuotojas, turite nemažai pareigų. Aukščiau pateiktas pavyzdys buvo skirtas paprastam pinigų pervedimo prašymui. Įsivaizduokite, kad dirbate su programinės įrangos projektu, kuris valdo banko sąskaitą arba didelės el. prekybos svetainės pagrindinę programą.
Turite būti susipažinę su tokiomis pažeidžiamomis vietomis, kad programa, kurią parašėte jas apsaugoti, nebūtų pažeidžiama. Tam reikia didelės atsakomybės.
Pažeidžiamumas dėl lenktynių yra tik vienas iš jų. Nesvarbu, kokią technologiją naudojate, turite stebėti, ar rašomame kode nėra spragų. Vienas iš svarbiausių įgūdžių, kuriuos galite įgyti kaip programuotojas, yra programinės įrangos saugumo išmanymas.