Saya ingin tahu apa yang ingin Anda capai. Jika proses Anda bersifat deterministik, maka pola alokasi/deallokasi harus sama.
Satu-satunya perbedaan yang mungkin adalah alamat yang dikembalikan oleh malloc
. Tetapi Anda mungkin tidak harus bergantung pada mereka (cara termudah adalah tidak menggunakan penunjuk sebagai peta kunci atau struktur data lainnya). Dan itupun, seharusnya hanya ada perbedaan jika alokasi tidak dilakukan melalui sbrk
(glibc menggunakan mmap
anonim untuk alokasi besar), atau jika Anda menggunakan mmap
(karena secara default alamat dipilih oleh kernel).
Jika Anda benar-benar ingin memiliki alamat yang persis sama, salah satu opsinya adalah memiliki buffer statis yang besar dan menulis pengalokasi khusus yang menggunakan memori dari buffer ini. Ini memiliki kerugian karena memaksa Anda untuk mengetahui sebelumnya jumlah maksimum memori yang Anda perlukan. Dalam executable non-PIE (gcc -fno-pie -no-pie
), buffer statis akan memiliki alamat yang sama setiap saat. Untuk eksekusi PIE, Anda dapat menonaktifkan pengacakan tata letak ruang alamat kernel untuk memuat program. Di pustaka bersama, menonaktifkan ASLR dan menjalankan program yang sama dua kali akan mengarah ke pilihan yang sama oleh penaut dinamis untuk memetakan pustaka.
Jika Anda tidak mengetahui sebelumnya ukuran maksimum memori yang ingin Anda gunakan, atau jika Anda tidak ingin mengkompilasi ulang setiap kali ukuran ini bertambah, Anda juga dapat menggunakan mmap
untuk memetakan buffer anonim besar di alamat tetap. Cukup berikan ukuran buffer dan alamat untuk digunakan sebagai parameter ke proses Anda dan gunakan memori yang dikembalikan untuk mengimplementasikan malloc
Anda sendiri di atasnya.
static void* malloc_buffer = NULL;
static size_t malloc_buffer_len = 0;
void* malloc(size_t size) {
// Use malloc_buffer & malloc_buffer_len to implement your
// own allocator. If you don't read uninitialized memory,
// it can be deterministic.
return memory;
}
int main(int argc, char** argv) {
size_t buf_size = 0;
uintptr_t buf_addr = 0;
for (int i = 0; i < argv; ++i) {
if (strcmp(argv[i], "--malloc-size") == 0) {
buf_size = atoi(argv[++i]);
}
if (strcmp(argv[i], "--malloc-addr") == 0) {
buf_addr = atoi(argv[++i]);
}
}
malloc_buffer = mmap((void*)buf_addr, buf_size, PROT_WRITE|PROT_READ,
MAP_FIXED|MAP_PRIVATE, 0, 0);
// editor's note: omit MAP_FIXED since you're checking the result anyway
if (malloc_buffer == MAP_FAILED || malloc_buffer != (void*)but_addr) {
// Could not get requested memory block, fail.
exit(1);
}
malloc_size = buf_size;
}
Dengan menggunakan MAP_FIXED
, kami memberi tahu kernel untuk mengganti pemetaan yang ada yang tumpang tindih dengan yang baru ini di buf_addr
.
(Catatan editor:MAP_FIXED
mungkin bukan yang Anda inginkan . Menentukan buf_addr
sebagai petunjuk, bukan NULL
sudah meminta alamat itu jika memungkinkan. Dengan MAP_FIXED
, mmap
akan mengembalikan kesalahan atau alamat yang Anda berikan. malloc_buffer != (void*)but_addr
check masuk akal untuk non-FIXED
kasus, yang tidak akan menggantikan pemetaan kode Anda yang ada atau perpustakaan bersama atau apa pun. Linux 4.17 memperkenalkan MAP_FIXED_NOREPLACE
yang dapat Anda gunakan untuk membuat mmap mengembalikan kesalahan alih-alih memori di alamat yang salah yang tidak ingin Anda gunakan. Tapi tetap tinggalkan centang agar kode Anda berfungsi pada kernel yang lebih lama.)
Jika Anda menggunakan blok ini untuk mengimplementasikan malloc Anda sendiri dan tidak menggunakan operasi non-deterministik lainnya dalam kode Anda, Anda dapat memiliki kontrol penuh atas nilai penunjuk.
Ini misalkan penggunaan pola malloc / free Anda bersifat deterministik. Dan bahwa Anda tidak menggunakan pustaka yang non-deterministik.
Namun, menurut saya solusi yang lebih sederhana adalah menjaga agar algoritme Anda tetap deterministik dan tidak bergantung pada alamat. Ini mungkin. Saya telah mengerjakan proyek berskala besar di mana banyak komputer harus memperbarui status secara deterministik (sehingga setiap program memiliki status yang sama, sementara hanya mengirimkan input). Jika Anda tidak menggunakan penunjuk untuk hal lain selain mereferensikan objek (hal terpenting adalah jangan pernah menggunakan nilai penunjuk untuk apa pun, bukan sebagai hash, bukan sebagai kunci dalam peta, ...), maka keadaan Anda akan tetap deterministik .
Kecuali jika yang ingin Anda lakukan adalah dapat memotret seluruh memori proses dan melakukan perbedaan biner untuk menemukan perbedaan. Menurut saya itu ide yang buruk, karena bagaimana Anda tahu bahwa keduanya telah mencapai titik yang sama dalam perhitungannya? Jauh lebih mudah untuk membandingkan output, atau agar proses dapat menghitung hash dari status dan menggunakannya untuk memeriksa apakah mereka sinkron karena Anda dapat mengontrol kapan ini dilakukan (dan karenanya menjadi deterministik juga, jika tidak, pengukuran Anda bersifat non-deterministik).
Yang tidak deterministik bukan hanya malloc
tetapi mmap (syscall dasar untuk mendapatkan lebih banyak ruang memori; ini bukan fungsi, ini adalah panggilan sistem jadi dasar atau atomik dari sudut pandang aplikasi; jadi Anda tidak dapat menulis ulang dalam aplikasi) karena pengacakan tata letak ruang alamat di Linux.
Anda dapat menonaktifkannya dengan
echo 0 > /proc/sys/kernel/randomize_va_space
sebagai root, atau melalui sysctl.
Jika Anda tidak menonaktifkan pengacakan tata letak ruang alamat, Anda macet.
Dan Anda memang menanyakan pertanyaan serupa sebelumnya, di mana saya menjelaskan bahwa malloc
Anda -s tidak selalu bersifat deterministik.
Saya masih berpikir bahwa untuk beberapa aplikasi praktis, malloc
tidak dapat bersifat deterministik. Bayangkan misalnya sebuah program memiliki tabel hash yang dikunci oleh pid
-s dari proses anak yang diluncurkan. Tabrakan di tabel itu tidak akan sama di semua proses Anda, dll.
Jadi saya yakin Anda tidak akan berhasil membuat malloc
deterministik dalam pengertian Anda, apa pun yang akan Anda coba (kecuali jika Anda membatasi diri pada kelas aplikasi yang sangat sempit untuk diperiksa, sangat sempit sehingga perangkat lunak Anda tidak akan berguna).
Sederhananya, seperti yang dinyatakan orang lain:jika eksekusi instruksi program Anda bersifat deterministik, maka memori dikembalikan oleh malloc()
akan bersifat deterministik. Itu mengasumsikan implementasi sistem Anda tidak memiliki panggilan ke random()
atau sesuatu untuk efek itu. Jika Anda tidak yakin, baca kode atau dokumentasi untuk malloc
sistem Anda .
Ini dengan kemungkinan pengecualian ASLR, seperti yang juga dinyatakan oleh orang lain. Jika Anda tidak memiliki hak akses root, Anda dapat menonaktifkannya per-proses melalui personality(2)
syscall dan parameter ADDR_NO_RANDOMIZE. Lihat di sini untuk informasi lebih lanjut tentang kepribadian tersebut.
Sunting:Saya juga harus mengatakan, jika Anda tidak sadar:apa yang Anda lakukan disebut bisimulasi dan merupakan teknik yang dipelajari dengan baik. Jika Anda tidak mengetahui terminologinya, mungkin ada gunanya memiliki kata kunci tersebut untuk penelusuran.