Sizin gibi okuyucular MUO'yu desteklemeye yardımcı oluyor. Sitemizdeki bağlantıları kullanarak bir satın alma işlemi gerçekleştirdiğinizde, bir ortaklık komisyonu kazanabiliriz.
İki işlemin belirli bir sırada gerçekleşmesi gerektiğinde bir yarış durumu oluşur, ancak bunlar ters sırada çalışabilir.
Örneğin, çok iş parçacıklı bir uygulamada, iki ayrı iş parçacığı ortak bir değişkene erişebilir. Sonuç olarak, bir iş parçacığı değişkenin değerini değiştirirse, diğeri en yeni değeri göz ardı ederek eski sürümü kullanmaya devam edebilir. Bu istenmeyen sonuçlara neden olacaktır.
Bu modeli daha iyi anlamak için işlemcinin işlem değiştirme sürecini yakından incelemek iyi olacaktır.
Bir İşlemci İşlemleri Nasıl Değiştirir?
Çağdaş işletim sistemleri çoklu görev adı verilen birden fazla işlemi aynı anda çalıştırabilir. Bu sürece şu açıdan baktığınızda CPU yürütme döngüsü, çoklu görevin gerçekten var olmadığını görebilirsiniz.
Bunun yerine, işlemciler, süreçleri aynı anda çalıştırmak için sürekli olarak süreçler arasında geçiş yapıyor veya en azından öyle yapıyormuş gibi davranıyor. CPU, bir işlemi tamamlanmadan kesebilir ve farklı bir işleme devam edebilir. İşletim sistemi bu süreçlerin yönetimini kontrol eder.
Örneğin en basit anahtarlama algoritmalarından biri olan Round Robin algoritması şu şekilde çalışır:
Genel olarak, bu algoritma, işletim sisteminin belirlediği şekilde, her işlemin çok küçük zaman dilimlerinde çalışmasına izin verir. Örneğin, bu iki mikrosaniyelik bir süre olabilir.
CPU sırayla her işlemi alır ve iki mikrosaniye boyunca çalışacak komutları yürütür. Daha sonra mevcut işlemin bitip bitmediğine bakılmaksızın bir sonraki işleme devam eder. Böylece bir son kullanıcı açısından birden fazla işlem aynı anda çalışıyormuş gibi görünür. Ancak, perde arkasına baktığınızda, CPU hala işleri sırayla yapıyor.
Bu arada, yukarıdaki şemada gösterildiği gibi, Round Robin algoritması herhangi bir optimizasyon veya işleme önceliği kavramından yoksundur. Sonuç olarak, gerçek sistemlerde nadiren kullanılan oldukça ilkel bir yöntemdir.
Şimdi, tüm bunları daha iyi anlamak için iki iş parçacığının çalıştığını hayal edin. İş parçacıkları ortak bir değişkene erişirse, bir yarış durumu ortaya çıkabilir.
Örnek Bir Web Uygulaması ve Yarış Durumu
Şimdiye kadar okuduğunuz her şeyin somut bir örneğini yansıtmak için aşağıdaki basit Flask uygulamasına göz atın. Bu uygulamanın amacı web üzerinde gerçekleşecek para işlemlerini yönetmektir. Aşağıdakileri adlı bir dosyaya kaydedin. para.py:
itibaren matara içe aktarmak şişe
itibaren flask.ext.sqlalchemy içe aktarmak SQLAlchemyuygulama = Şişe (__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy (uygulama)sınıfHesap(db. modeli):
kimlik = veritabanı Sütun (db. Tamsayı, birincil_anahtar = Doğru)
miktar = db. Sütun (db. Sicim(80), benzersiz = Doğru)kesin__içinde__(öz, say):
self.miktar = miktarkesin__repr__(kendi):
geri dönmek '' % öz.miktar@app.route("/")
kesinMERHABA():
hesap = Account.query.get(1) # Sadece bir cüzdan var.
geri dönmek "Toplam Para = {}".format (account.amount)@app.route("/send/")
kesinGöndermek(miktar):
hesap = Account.query.get(1)eğer int (account.amount) < tutar:
geri dönmek "Yetersiz bakiye. Parayı sıfırla ile /reset!)"hesap.amount = int (account.amount) - tutar
db.session.commit()
geri dönmek "Gönderilen miktar = {}".format (miktar)@app.route("/reset")
kesinSıfırla():
hesap = Account.query.get(1)
hesap.tutar = 5000
db.session.commit()
geri dönmek "Para sıfırlandı."
eğer __name__ == "__main__":
app.secret_key = 'heLLoTHisIsSeCReTKey!'
uygulama.run()
Bu kodu çalıştırmak için hesap tablosunda bir kayıt oluşturmanız ve işlemlere bu kayıt üzerinden devam etmeniz gerekmektedir. Kodda da görebileceğiniz gibi bu bir test ortamı olduğundan tablodaki ilk kayda göre işlem yapar.
itibaren para içe aktarmak db
db.create_all()
itibaren para içe aktarmak Hesap
hesap = Hesap (5000)
db.oturum.eklemek(hesap)
db.oturum.işlemek()
Şimdi bakiyesi 5.000$ olan bir hesap oluşturdunuz. Son olarak, Flask ve Flask-SQLAlchemy paketlerinin kurulu olması koşuluyla, aşağıdaki komutu kullanarak yukarıdaki kaynak kodunu çalıştırın:
pitonpara.py
Yani basit bir ayıklama işlemi yapan Flask web uygulamasına sahipsiniz. Bu uygulama GET request linkleri ile aşağıdaki işlemleri gerçekleştirebilmektedir. Flask varsayılan olarak 5000 bağlantı noktasında çalıştığından, ona eriştiğiniz adres 127.0.0.1:5000/. Uygulama aşağıdaki uç noktaları sağlar:
- 127.0.0.1:5000/ mevcut bakiyeyi görüntüler.
- 127.0.0.1:5000/send/{miktar} tutarını hesaptan çıkarır.
- 127.0.0.1:5000/sıfırlama hesabı 5.000$ olarak sıfırlar.
Şimdi bu aşamada yarış koşulu zafiyetinin nasıl oluştuğunu inceleyebilirsiniz.
Yarış Koşulu Güvenlik Açığı Olasılığı
Yukarıdaki web uygulaması, olası bir yarış durumu güvenlik açığı içermektedir.
Başlangıç için 5.000$'ınız olduğunu hayal edin ve 1$ gönderecek iki farklı HTTP isteği oluşturun. Bunun için linke iki farklı HTTP request gönderebilirsiniz. 127.0.0.1:5000/gönder/1. Varsayalım ki, en kısa sürede web sunucusu ilk isteği işler, CPU bu işlemi durdurur ve ikinci isteği işler. Örneğin, aşağıdaki kod satırını çalıştırdıktan sonra ilk işlem durabilir:
hesap.tutar = int(hesap.amount) - tutar
Bu kod yeni bir toplam hesapladı ancak kaydı henüz veritabanına kaydetmedi. İkinci istek başladığında, aynı hesaplamayı yapacak, veritabanındaki değerden 1 $ - 5.000 $ - çıkaracak ve sonucu saklayacaktır. İlk işlem kaldığı yerden devam ettiğinde kendi değerini (4.999$) depolayacak ve bu en son hesap bakiyesini yansıtmayacaktır.
Yani, iki istek tamamlandı ve her birinin hesap bakiyesinden 1 ABD Doları düşerek 4.998 ABD Doları tutarında yeni bir bakiye vermesi gerekirdi. Ancak web sunucusunun bunları işleme sırasına bağlı olarak nihai hesap bakiyesi 4.999$ olabilir.
Hedef sisteme 5 saniyelik bir zaman diliminde 128 dolarlık bir transfer yapmak için istek gönderdiğinizi hayal edin. Bu işlemin sonucunda beklenen hesap özeti 5.000$ - 128$ = 4.875$ olacaktır. Ancak, yarış koşulu nedeniyle, nihai bakiye 4.875$ ile 4.999$ arasında değişebilir.
Programcılar Güvenliğin En Önemli Bileşenlerinden Biri
Bir yazılım projesinde, bir programcı olarak epeyce sorumluluğunuz vardır. Yukarıdaki örnek basit bir para transferi uygulaması içindi. Bir banka hesabını veya büyük bir e-ticaret sitesinin arka ucunu yöneten bir yazılım projesinde çalıştığınızı hayal edin.
Bunları korumak için yazdığınız programın güvenlik açıklarından arınmış olması için bu tür güvenlik açıklarına aşina olmalısınız. Bu güçlü bir sorumluluk gerektirir.
Bir yarış koşulu güvenlik açığı bunlardan yalnızca biridir. Hangi teknolojiyi kullanırsanız kullanın, yazdığınız koddaki güvenlik açıklarına dikkat etmeniz gerekir. Bir programcı olarak edinebileceğiniz en önemli becerilerden biri, yazılım güvenliğine aşinalıktır.