Merhaba, ctypes ı kullanırken nasıl çalıştığını merak ettim. Biraz araştırınca FFI’ı buldum. FFI’ı biraz araştırdım (wikipedia ve stackoverflow dan birde birkaç siteden daha okudum) ancak pek bir şey anlamadım. Python nasıl C kodu çalıştırabiliyor ? Python C de kodlandığı için mi ? Çünkü bir kod örneği gördüm ve C koduydu
PyObject bir C objesi sanırım.
Eğer Python C ile geliştirilmiş olmasaydı ctypes modülünü yapmak mümkün olmayacak mıydı ? DLL leri, C nin veri tiplerini nasıl Python’da kullanabiliyoruz ? Python zaten C de kodlandığı ve tüm veri tipleri zaten C de tanımlı olduğu için mi ? Birde GitHub da da bir kod buldum
Programlarin, birbirlerini ve kutuphaneleri (aslinda hepsi ayni sey) cagirdiklari arayuze ABI (Application Binary Interface) deniyor. ABI, parametrelerin nasil yollanacagini, hedef adrese hangi CPU instruction’i ile gidilecegini, hangi data turlerinin hangi sekilde kullanildigini vs. tanimliyor. Ayni ABI’ye sahip her kod bir digeriyle iletisim kurabilir.
Python C’de yazildigi icin degil, sistemin geri kalaninin kullandigi ABI’yi kullandigi icin C kodu calistirabiliyor. Aslinda calistirdigi kod C kodu degil, platformun calistirilabilir (executable) dosyasi (PE veya ELF).
Buradaki no_mangle direktifi fonksiyonun executable icindeki tablolarda isminin double_input olmasini sagliyor. Disaridan ismiyle arayan bulsun diye. Yoksa kim bilir belki super-double_input olacakti, Rust’in her fonksiyonun ismine “super-” ekledigini ve bu fonksiyonun Rust’ta yazildigini bilmeyen kimse bunu tahmin edemeyecekti.
Buradaki i32 kotu bir secim; 4 byte’lik bir sayi bekliyor/sunuyor. Bunun hangi ABI type’ina denk geldigi sisteme bagli. C int’ine denk gelmesini istiyorduysak c_int kullanmaliydik.
Bu konuyu CPython objelerini (PyXxx) araya karistirmadan ogrenmeni tavsiye ediyorum. (ctypes’i kullanabilirsin, Python’in FFI’si o.)
Bu arada bu Python kodu. C’de yazilmis olabilir ama CPython objelerini kullanarak, Python koduyla haberlesmek icin yazilmis. FFI ve aslinda ogrenmek istedigin mevzularla sadece uzaktan alakali. Veya simdilik oyle diyelim, detaylarini anlatmam mumkun degil. (Hem ben iyi bilmiyorum, hem de senin biraz daha FFI bilmen gerekiyor.)
Olaya çok yanlış açıdan bakmışım. Mesela Rust değil de herhangi bir derlenen dili de aynı ABI yi kullanıyorlarsa çağırabiliriz değil mi ?
Mesela windows un executable dosyası exe. O zaman C ile bir program yazıp exe ye derlersem ve şunu çalıştırırsam:
import os
os.system("cd C:\\Dosyanın\\Konumu\\&programadi.exe")
bu ctypes ın yaptığı ile aynı mı oluyor ?
Birde yukarıdaki rust örneğinde input * 2 return ediliyor sanırım, sonuçta python programında input * 2 nin sonucunu görebiliyoruz. Ancak os.system i kullanınca return edilen değer 1 veya 0 oluyor (asdasdasd gibi çalışmayacak bir komut girince 1, echo hello world gibi çalışan bir komutta 0 döndürüyor)
Edit: az önce basit bir program yazdım
int main()
{
return 5;
}
ve
import os
print(os.system("yeni.exe"))
Sonuç
5
>>>
oldu. Ama kodu biraz düzenleyince zorlaştı mesela yeni bir fonksiyon ekleyince onu çağıramadım. main fonksiyonu başlangıçta çalıştığından 5 i döndürdü ama
int main()
{
return 5;
}
int yenifonksiyon()
{
return 15;
}
Hayir. Bu ayri bir process yaratiyor. LoadLibrary executable’i mevcut process’in hafizasina yukluyor. Sonrasinda main fonksiyonunu bulup cagirirsan cok benzer bir sey yapmis olabilirsin.
Evet
Python programiyla rust programi yani program. Bir tane exe var (python.exe). Kendi icinde fonksiyon cagirip return value’sunu kullaniyor.
os.system’in return ettigi deger yani calistirilan process’in cikis kodu.
Yani aslında ctypes ın yaptığı python.exe içinde verdiğimiz dll veya exe den fonksiyonu çağırmak ve return value’sunu almak/fonksiyonun yaptıklarını yapmak.
Yazdigim kodun dinamik kisminin (hello.c, main_dyn.c) Windows versiyonunu yaz ve calistir. (hello.so → hello.dll, main_dyn → main_dyn.exe)
Sonra ctypes ile calistir.
Sonra sayi paslayip dondur.
Sonra liste paslayip toplamini dondur.
Butun bunlari python+ctypes disinda bir C programiyla da yap.
C programı olarak yaptım ama python+ctypes olarak çalışmadı (sayı ve liste döndürmeyi yaptım).
Edit: LoadLibraryA yı Türkçe çeviri ile 2.kez okudum ve daha iyi anladım. Sanırım LoadLibraryA yı python.exe den çağırırsak o zaman belirttiğimiz dll i python.exe nin içinde çalıştırıyor, ayrı bir process olarak değil. Ayrıca artık LoadLibrary + GetProcAddress (Win32) = dlopen + dlsym (POSIX) çok daha anlamlı geliyor.
python.exe ile aynı process e dll i yüklüyor ve fonksiyon_ismi ni çağırıyoruz.
Evet, her sey aynen dedigin gibi!
C program(lar)inin da daha fazla vakit almasini beklerdim, takdirler.
Soylediklerimin biraz ileri veya karisik gelebildiginin farkindayim, ama bir takim seyleri okuyup/ogrenip geri gelindiginde de ise yaramalarini amacliyorum. Giris yazisi yazmaya veya tercume etmeye gerek yok; giristen ilerideki insanlarin kafalarinin takilabilecegi noktalara deginmeyi seviyorum. (Giristekilerin akillarini karistirma pahasina.) ← Bu paragraf ne kadar anlamdi oldu bilmiyorum, erken kalktim :D
Bunu calistiran kod nerede?
DLL/SO olarak derleyip baska bir executable’da LoadLibrary/dlopen + GetProcAddress/dlsym kullanarak calistirman lazim fonksiyonlari.
Hata: OSError: [WinError 193] %1 geçerli bir Win32 uygulaması değil
Birde bir messagebox çıkıyor onda da yazanlar şunlar:
C:\Users\Furkan\Desktop\yeni2.dll programı Windows üzerinde çalışmak üzere tasarlanmadı veya hata içeriyor. Programı özgün yükleme medyasını kullanarak yüklemeyi deneyin veya destek için sistem yöneticinize veya yazılım satıcısına başvurun. Hata durumu: 0xc000012f.