Cara membaca variabel lingkungan dari suatu proses

43

Linux /proc/<pid>/environtidak memperbarui (seperti yang saya mengerti, file tersebut mengandung lingkungan awal dari proses).

Bagaimana saya bisa membaca lingkungan proses saat ini ?

Jonathan Ben-Avraham
sumber

Jawaban:

20

/proc/$pid/environtidak memperbarui jika proses mengubah lingkungannya sendiri. Tetapi banyak program tidak perlu repot mengubah lingkungannya sendiri, karena ini sedikit tidak berguna: lingkungan program tidak terlihat melalui saluran normal, hanya melalui /procdan ps, dan bahkan tidak setiap varian unix memiliki fitur semacam ini, sehingga aplikasi tidak bergantung di atasnya.

Sejauh menyangkut kernel, lingkungan hanya muncul sebagai argumen dari execvepanggilan sistem yang memulai program. Linux mengekspos area dalam memori melalui /proc, dan beberapa program memperbarui area ini sementara yang lain tidak. Secara khusus, saya tidak berpikir shell memperbarui area ini. Karena area memiliki ukuran tetap, tidak mungkin untuk menambahkan variabel baru atau mengubah panjang nilai.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
jadi, secara efektif tidak ada cara untuk mengakses proses '* envp (array pointer ke pengaturan lingkungan). @Gilles, bisakah Anda tunjukkan apakah mungkin untuk melampirkan debugger dan membaca array pointer ke pengaturan lingkungan.
Nikhil Mulley
2
@Nikhil Tentu, benar. Tetapi hanya karena Anda menulis PATH=foodi shell tidak berarti shell akan dimodifikasi *envp. Dalam beberapa shell, yang hanya memperbarui struktur data internal, dan kode eksekusi program eksternal yang diperbarui *envp. Lihat di assign_in_envdalam variables.csumber bash, misalnya.
Gilles 'SANGAT berhenti menjadi jahat'
9
@Gilles: Jawaban ini paling menyesatkan (-1). Lingkungan di / proc / $$ / environment dibaca dari tumpukan proses. Lihat fs / proc / base.c. Ini adalah lingkungan awal. Tidak pernah diperbarui dan pada kenyataannya tidak bisa. Lingkungan yang digunakan oleh libc setenv dialokasikan pada heap dan diinisialisasi dengan isi lingkungan dalam stack. Jika proses panggilan libc's forkmaka libc membuat sys_forkpanggilan menggunakan lingkungan heap yang dialokasikan untuk proses anak.
Jonathan Ben-Avraham
7
@ JonathanBen-Avraham Anda benar bahwa lingkungan awal tidak diperbarui di shell apa pun. Namun area itu tidak hanya dibaca di Linux, saya menemukan program yang menggunakannya untuk melaporkan status mereka (laporan status argvlebih umum tetapi keduanya ada).
Gilles 'SO- stop being evil'
39

Anda dapat membaca lingkungan awal suatu proses dari /proc/<pid>/environ.

Jika suatu proses mengubah lingkungannya, maka untuk membaca lingkungan Anda harus memiliki tabel simbol untuk proses dan menggunakan ptracepanggilan sistem (misalnya dengan menggunakan gdb) untuk membaca lingkungan dari char **__environvariabel global . Tidak ada cara lain untuk mendapatkan nilai variabel apa pun dari proses Linux yang sedang berjalan.

Itulah jawabannya. Sekarang untuk beberapa catatan.

Di atas mengasumsikan bahwa prosesnya adalah POSIX compliant, yang berarti bahwa proses mengelola lingkungannya menggunakan variabel global char **__environseperti yang ditentukan dalam Referensi Ref .

Lingkungan awal untuk suatu proses dilewatkan ke proses dalam buffer dengan panjang tetap pada tumpukan proses. (Mekanisme biasa yang melakukan ini adalah linux//fs/exec.c:do_execve_common(...).) Karena ukuran buffer dihitung tidak lebih dari ukuran yang diperlukan untuk lingkungan awal, Anda tidak dapat menambahkan variabel baru tanpa menghapus variabel yang ada atau menghancurkan tumpukan. Jadi, setiap skema yang masuk akal untuk memungkinkan perubahan dalam lingkungan proses akan menggunakan heap, di mana memori dalam ukuran sewenang-wenang dapat dialokasikan dan dibebaskan, yang persis seperti yang dilakukan GNU libc( glibc) untuk Anda.

Jika proses menggunakan glibc, maka itu adalah POSIX compliant, dengan __environdideklarasikan di glibc//posix/environ.cGlibc menginisialisasi __environdengan pointer ke memori yang mallocberasal dari tumpukan proses, kemudian menyalin lingkungan awal dari tumpukan ke area tumpukan ini. Setiap kali proses menggunakan setenvfungsi, glibclakukan reallocuntuk menyesuaikan ukuran area yang __environmenunjuk untuk mengakomodasi nilai atau variabel baru. (Anda dapat mengunduh kode sumber glibc dengan git clone git://sourceware.org/git/glibc.git glibc). Untuk benar-benar memahami mekanisme ini, Anda juga harus membaca kode Hurd di hurd//init/init.c:frob_kernel_process()(git clone git: //git.sv.gnu.org/hurd/hurd.git hurd).

Sekarang jika proses baru hanya forkdiedit, tanpa execmenimpa tumpukan berikutnya , maka argumen dan sihir penyalinan lingkungan dilakukan linux//kernel/fork.c:do_fork(...), di mana copy_processpanggilan rutin dup_task_structyang mengalokasikan tumpukan proses baru dengan memanggil alloc_thread_info_node, yang memanggil setup_thread_stack( linux//include/linux/sched.h) untuk proses baru menggunakan alloc_thread_info_node.

Akhirnya, __environkonvensi POSIX adalah konvensi ruang pengguna . Tidak memiliki koneksi dengan apa pun di kernel Linux. Anda dapat menulis program userspace tanpa menggunakan glibcdan tanpa __environglobal dan kemudian mengelola variabel lingkungan sesuka Anda. Tidak ada yang akan menahan Anda untuk melakukan ini tetapi Anda harus menulis fungsi manajemen lingkungan Anda sendiri ( setenv/ getenv) dan pembungkus Anda sendiri untuk sys_execdan kemungkinan tidak ada yang akan bisa menebak di mana Anda menempatkan perubahan pada lingkungan Anda.

Jonathan Ben-Avraham
sumber
Banyak file di /proc/[pid]/tampaknya memiliki penyandian aneh (orang lain mungkin tahu apa dan mengapa). Bagi saya, cukup cat environmencetak variabel lingkungan dalam format yang sangat sulit dibaca. cat environ | stringsmemecahkan ini untukku.
retrohacker
@retrohacker Ini memberikan solusi yang lebih kuat: askubuntu.com/questions/978711/…
Frank Kusters
20

Ini diperbarui ketika dan ketika proses memperoleh / menghapus variabel lingkungannya. Apakah Anda memiliki referensi yang menyatakan environfile tidak diperbarui untuk proses dalam direktori prosesnya di bawah / proc filesystem?

xargs --null --max-args=1 echo < /proc/self/environ

atau

xargs --null --max-args=1 echo < /proc/<pid>/environ

atau

ps e -p <pid>

Di atas akan mencetak variabel lingkungan dari proses dalam psformat output, pemrosesan teks (parsing / filtering) diperlukan untuk melihat variabel lingkungan sebagai daftar.

Solaris (tidak ditanyakan, tetapi untuk referensi saya akan posting di sini):

/usr/ucb/ps -wwwe <pid>

atau

pargs -e <pid> 

EDIT: / proc / pid /viron tidak diperbarui! Saya berdiri dikoreksi. Proses verifikasi di bawah. Namun, anak-anak dari mana proses tersebut diwariskan mewarisi variabel lingkungan proses dan terlihat di file / proc / self / environment masing-masing. (Gunakan string)

Dengan di dalam shell: di sini xargs adalah proses anak dan karenanya mewarisi variabel lingkungan dan juga tercermin dalam /proc/self/environfile -nya .

[centos@centos t]$ printenv  | grep MASK
[centos@centos t]$ export MASK=NIKHIL
[centos@centos t]$ printenv  | grep MASK
MASK=NIKHIL
[centos@centos t]$ xargs --null --max-args=1 echo < /proc/self/environ  | grep MASK
MASK=NIKHIL
[centos@centos t]$ unset MASK
[centos@centos t]$ printenv  | grep MASK
[centos@centos t]$ xargs --null --max-args=1 echo < /proc/self/environ  | grep MASK
[centos@centos t]$

Memeriksa dari sesi lain, di mana terminal / sesi bukanlah proses anak dari shell di mana variabel lingkungan diatur.

Memverifikasi dari terminal / sesi lain pada host yang sama:

terminal1:: Perhatikan bahwa printenv adalah fork'd dan merupakan proses anak dari bash dan karenanya ia membaca file environmentnya sendiri.

[centos@centos t]$ echo $$
2610
[centos@centos t]$ export SPIDEY=NIKHIL
[centos@centos t]$ printenv | grep SPIDEY
SPIDEY=NIKHIL
[centos@centos t]$ 

terminal2: pada host yang sama - jangan memulai dengan shell yang sama di mana variabel di atas diatur, luncurkan terminal secara terpisah.

[centos@centos ~]$ echo $$
4436
[centos@centos ~]$ xargs --null --max-args=1 echo < /proc/self/environ | grep -i spidey
[centos@centos ~]$ strings -f /proc/2610/environ | grep -i spidey
[centos@centos ~]$ xargs --null --max-args=1 echo < /proc/2610/environ | grep -i spidey
[centos@centos ~]$ 
Nikhil Mulley
sumber
1
Saya lakukan export foo=bardi sesi satu bash (pid xxxx), kemudian lakukan cat /proc/xxxx/environ | tr \\0 \\ndi sesi bash lain dan saya tidak melihat foo.
Saya memperbarui jawaban di atas dengan contoh memeriksa proses yang sama di dalam shell.
Nikhil Mulley
Anda benar. Saya berdiri dikoreksi. Terima kasih. Saya sekarang harus pergi dan membaca manual saya untuk memeriksa variabel lingkungan dari proses yang berbeda dengan dalam kelompok proses pengguna.
Nikhil Mulley
1
Satu hal lagi: Saya mencoba memeriksa lingkungan yang menempel gdb pada pid, tetapi masih tidak ada referensi di sana. Blok variabel lingkungan dalam memori akan dialokasikan kembali setiap kali ada perubahan dan tidak mencerminkan dalam file lingkungan prosesnya sendiri dalam sistem file proc, tetapi bagaimanapun memungkinkan untuk diwarisi oleh proses anak. Itu berarti ini bisa menjadi lebih mudah untuk mengetahui detail intrinsik ketika garpu terjadi, bagaimana proses anak mendapat variabel lingkungan seperti apa adanya.
Nikhil Mulley
Saya harap @Gilles akan memberikan cahaya obornya untuk hal ini .. :-)
Nikhil Mulley
7

Nah berikut ini tidak terkait dengan niat sebenarnya penulis, tetapi jika Anda benar-benar ingin "BACA" /proc/<pid>/environ, Anda dapat mencoba

strings /proc/<pid>/environ

mana yang lebih baik dari catitu.

fibonacci
sumber
1
+1 untuk strings. Tetap sederhana.
Ed Randall
Setuju @EdRandall, terasa ini seperti mudah pendekatan vs xargs --null.
Per Lundberg
"File" adalah null dihentikan, ganti nol dengan baris baru dan alat normal bekerja lagi (dengan peringatan biasa), misalnya:tr '\0' '\n' < /proc/$$/environ | ...
Thor
Selama variabel lingkungan itu sendiri tidak mengandung baris baru. Kerang seperti BASH terkadang melakukan itu untuk 'fungsi yang diekspor'.
anthony