GNU/Linux >> Belajar Linux >  >> Linux

Pertanyaan tentang putenv() dan setenv()

  • [The] putenv(char *string); [...] tampaknya cacat fatal.

Ya, itu cacat fatal. Itu disimpan dalam POSIX (1988) karena itu adalah penemuan sebelumnya. setenv() mekanisme tiba kemudian. Koreksi: Standar POSIX 1990 mengatakan dalam §B.4.6.1 "Fungsi tambahan putenv() dan clearenv() dianggap tetapi ditolak". Single Unix Specification (SUS) versi 2 dari tahun 1997 mencantumkan putenv() tapi bukan setenv() atau unsetenv() . Revisi berikutnya (2004) mendefinisikan kedua setenv() dan unsetenv() juga.

Karena tidak menyalin string yang diteruskan, Anda tidak dapat memanggilnya dengan lokal dan tidak ada jaminan string yang dialokasikan heap tidak akan ditimpa atau dihapus secara tidak sengaja.

Anda benar bahwa variabel lokal hampir selalu merupakan pilihan yang buruk untuk diteruskan ke putenv() - pengecualiannya tidak jelas sampai hampir tidak ada. Jika string dialokasikan pada heap (dengan malloc() et al), Anda harus memastikan bahwa kode Anda tidak mengubahnya. Jika ya, itu mengubah lingkungan pada saat yang sama.

Selain itu (walaupun saya belum mengujinya), karena salah satu penggunaan variabel lingkungan adalah untuk meneruskan nilai ke lingkungan anak, hal ini tampaknya tidak berguna jika anak memanggil salah satu dari exec*() fungsi. Apakah saya salah dalam hal itu?

exec*() fungsi membuat salinan lingkungan dan meneruskannya ke proses yang dieksekusi. Tidak ada masalah di sana.

Halaman manual Linux menunjukkan bahwa glibc 2.0-2.1.1 mengabaikan perilaku di atas dan mulai menyalin string tetapi hal ini menyebabkan kebocoran memori yang telah diperbaiki di glibc 2.1.2. Tidak jelas bagi saya apa kebocoran memori ini atau bagaimana cara memperbaikinya.

Kebocoran memori muncul karena setelah Anda memanggil putenv() dengan string, Anda tidak dapat menggunakan string itu lagi untuk tujuan apa pun karena Anda tidak dapat mengetahui apakah itu masih digunakan, meskipun Anda dapat mengubah nilainya dengan menimpanya (dengan hasil yang tidak pasti jika Anda mengubah nama menjadi variabel lingkungan ditemukan di posisi lain di lingkungan). Jadi, jika Anda telah mengalokasikan ruang, putenv() klasik membocorkannya jika Anda mengubah variabel lagi. Ketika putenv() mulai menyalin data, variabel yang dialokasikan menjadi tidak direferensikan karena putenv() tidak lagi menyimpan referensi ke argumen, tetapi pengguna berharap lingkungan akan mereferensikannya, sehingga memori bocor. Saya tidak yakin apa perbaikannya — saya kira 3/4 akan kembali ke perilaku lama.

setenv() menyalin string tetapi saya tidak tahu persis cara kerjanya. Ruang untuk lingkungan dialokasikan saat proses dimuat tetapi sudah diperbaiki.

Ruang lingkungan asli sudah diperbaiki; ketika Anda mulai memodifikasinya, aturannya berubah. Bahkan dengan putenv() , lingkungan asli dimodifikasi dan dapat berkembang sebagai hasil dari penambahan variabel baru, atau sebagai hasil dari perubahan variabel yang ada untuk memiliki nilai yang lebih panjang.

Apakah ada konvensi (sewenang-wenang?) yang bekerja di sini? Misalnya, mengalokasikan lebih banyak slot dalam larik penunjuk string env daripada yang digunakan saat ini dan memindahkan penunjuk penghentian nol sesuai kebutuhan?

Itulah setenv() mekanisme yang mungkin dilakukan. Variabel (global) environ menunjuk ke awal array pointer ke variabel lingkungan. Jika menunjuk ke satu blok memori pada satu waktu dan blok yang berbeda pada waktu yang berbeda, maka lingkungan dialihkan, begitu saja.

Apakah memori untuk string baru (disalin) dialokasikan di ruang alamat lingkungan itu sendiri dan jika terlalu besar untuk muat, Anda hanya mendapatkan ENOMEM?

Ya, Anda bisa mendapatkan ENOMEM, tetapi Anda harus berusaha keras. Dan jika Anda mengembangkan lingkungan terlalu besar, Anda mungkin tidak dapat menjalankan program lain dengan benar - lingkungan akan terpotong atau operasi eksekusi akan gagal.

Mempertimbangkan masalah di atas, apakah ada alasan untuk memilih putenv() daripada setenv()?

  • Gunakan setenv() dalam kode baru.
  • Perbarui kode lama untuk menggunakan setenv() , tetapi jangan menjadikannya sebagai prioritas utama.
  • Jangan gunakan putenv() dalam kode baru.

Tidak ada ruang "lingkungan" khusus - setenv hanya secara dinamis mengalokasikan ruang untuk string (dengan malloc misalnya) seperti yang biasa Anda lakukan. Karena lingkungan tidak berisi indikasi dari mana setiap string berasal, tidak mungkin untuk setenv atau unsetenv untuk mengosongkan ruang apa pun yang mungkin telah dialokasikan secara dinamis oleh panggilan sebelumnya ke setenv.

"Karena itu tidak menyalin string yang diteruskan, Anda tidak dapat memanggilnya dengan lokal dan tidak ada jaminan string yang dialokasikan heap tidak akan ditimpa atau dihapus secara tidak sengaja." Tujuan dari putenv adalah untuk memastikan bahwa jika Anda memiliki string yang dialokasikan heap, Anda dapat menghapusnya sengaja . Itulah yang dimaksud teks pemikiran dengan "satu-satunya fungsi yang tersedia untuk ditambahkan ke lingkungan tanpa mengizinkan kebocoran memori." Dan ya, Anda bisa menyebutnya dengan lokal, cukup hapus string dari lingkungan (putenv("FOO=") atau unsetenv) sebelum Anda kembali dari fungsi.

Intinya adalah menggunakan putenv membuat proses menghapus string dari lingkungan sepenuhnya deterministik. Sedangkan setenv pada beberapa implementasi yang ada akan memodifikasi string yang ada di lingkungan jika nilai baru lebih pendek (untuk menghindari selalu membocorkan memori), dan karena membuat salinan saat Anda memanggil setenv, Anda tidak mengontrol string yang awalnya dialokasikan secara dinamis sehingga Anda tidak dapat membebaskannya saat dihapus.

Sementara itu, setenv sendiri (atau unsetenv) tidak dapat membebaskan string sebelumnya, karena - bahkan mengabaikan putenv - string tersebut mungkin berasal dari lingkungan asli alih-alih dialokasikan oleh pemanggilan setenv sebelumnya.

(Seluruh jawaban ini mengasumsikan putenv yang diterapkan dengan benar, yaitu tidak yang ada di glibc 2.0-2.1.1 yang Anda sebutkan.)


Linux
  1. 25 Pertanyaan dan Jawaban Wawancara Linux Teratas

  2. 10 Fakta Menarik dan Menyenangkan Tentang Linux

  3. Siapa yang Menetapkan Variabel Lingkungan $user Dan $username?

  1. 20 Pertanyaan dan Jawaban Wawancara Postfix

  2. BIND – Pertanyaan dan Jawaban Wawancara Server DNS

  3. Kiat dan trik variabel lingkungan Linux

  1. Mewarnai Lingkungan Terminal dan Shell Anda?

  2. Tentang Mem Dan Vmem?

  3. Pertanyaan IPTables dan DHCP?