Memahami panggilan sistem di Linux dengan strace

Terjemahan artikel disiapkan khusus untuk siswa kursus dasar dan lanjutan Administrator Linux.






Syscall adalah mekanisme di mana program pengguna berinteraksi dengan kernel Linux, dan strace adalah alat yang ampuh untuk melacaknya. Untuk lebih memahami cara kerja sistem operasi, akan sangat membantu untuk memahami cara kerjanya.



Sistem operasi dapat dibagi menjadi dua mode operasi:



  • Mode kernel adalah mode hak istimewa yang digunakan oleh kernel sistem operasi.
  • Mode pengguna adalah mode di mana sebagian besar aplikasi pengguna dijalankan.




Pengguna biasanya menggunakan utilitas baris perintah dan antarmuka grafis (GUI) untuk pekerjaan sehari-hari mereka. Pada saat yang sama, panggilan sistem bekerja secara tidak terlihat di latar belakang, mengacu pada kernel untuk melakukan pekerjaan tersebut.



Panggilan sistem sangat mirip dengan panggilan fungsi dalam arti bahwa panggilan tersebut diberikan argumen dan mengembalikan nilai. Satu-satunya perbedaan adalah bahwa panggilan sistem berfungsi di tingkat kernel, tetapi fungsi tidak. Beralih dari mode pengguna ke mode kernel dilakukan menggunakan mekanisme interupsi khusus .



Sebagian besar detail ini disembunyikan dari pengguna di pustaka sistem (glibc di sistem Linux). Panggilan sistem bersifat generik, tetapi meskipun demikian, mekanisme pelaksanaannya sebagian besar bergantung pada perangkat keras.



Artikel ini membahas beberapa contoh praktis penggunaan panggilan sistem parsing strace. Contohnya menggunakan Red Hat Enterprise Linux, tetapi semua perintah seharusnya berfungsi pada distribusi Linux lain:



[root@sandbox ~]# cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.7 (Maipo)
[root@sandbox ~]#
[root@sandbox ~]# uname -r
3.10.0-1062.el7.x86_64
[root@sandbox ~]#




Pertama, pastikan Anda memiliki alat yang diperlukan terinstal di sistem Anda. Anda stracedapat memeriksa apakah sudah diinstal menggunakan perintah di bawah ini. Untuk melihat versi, stracejalankan dengan parameter -V:



[root@sandbox ~]# rpm -qa | grep -i strace
strace-4.12-9.el7.x86_64
[root@sandbox ~]#
[root@sandbox ~]# strace -V
strace -- version 4.12
[root@sandbox ~]#




Jika stracetidak diinstal, instal dengan menjalankan:



yum install strace




Misalnya, buat direktori pengujian dalam /tmpdan dua file menggunakan perintah touch:



[root@sandbox ~]# cd /tmp/
[root@sandbox tmp]#
[root@sandbox tmp]# mkdir testdir
[root@sandbox tmp]#
[root@sandbox tmp]# touch testdir/file1
[root@sandbox tmp]# touch testdir/file2
[root@sandbox tmp]#




(Saya /tmphanya menggunakan direktori karena setiap orang memiliki akses ke sana, tetapi Anda dapat menggunakan direktori lain.)



Gunakan perintah untuk lsmemeriksa bahwa testdirfile telah dibuat di direktori :



[root@sandbox tmp]# ls testdir/
file1  file2
[root@sandbox tmp]#




Anda mungkin menggunakan perintah lssetiap hari tanpa menyadari bahwa panggilan sistem sedang berjalan di bawah tenda. Di sinilah abstraksi berperan. Beginilah cara kerja perintah ini:



   ->    (glibc) ->  




Perintah tersebut lsmemanggil fungsi dari pustaka sistem Linux (glibc). Perpustakaan ini, pada gilirannya, memanggil panggilan sistem, yang melakukan sebagian besar pekerjaan.



Jika Anda ingin mengetahui fungsi apa yang dipanggil dari pustaka glibc, gunakan perintah yang ltracediikuti dengan perintah ls testdir/:



ltrace ls testdir/




Jika ltracetidak diinstal, maka instal:



yum install ltrace




Akan ada banyak informasi di layar, tapi jangan khawatir - kita akan melihatnya nanti. Berikut beberapa fungsi pustaka penting dari keluarannya ltrace:



opendir("testdir/")                                  = { 3 }
readdir({ 3 })                                       = { 101879119, "." }
readdir({ 3 })                                       = { 134, ".." }
readdir({ 3 })                                       = { 101879120, "file1" }
strlen("file1")                                      = 5
memcpy(0x1665be0, "file1\0", 6)                      = 0x1665be0
readdir({ 3 })                                       = { 101879122, "file2" }
strlen("file2")                                      = 5
memcpy(0x166dcb0, "file2\0", 6)                      = 0x166dcb0
readdir({ 3 })                                       = nil
closedir({ 3 })    




Dengan memeriksa keluaran ini, Anda mungkin dapat memahami apa yang sedang terjadi. Direktori bernama testdirdibuka menggunakan fungsi perpustakaan opendir, diikuti dengan panggilan ke fungsi readdiryang membaca isi direktori. Akhirnya, sebuah fungsi dipanggil closediryang menutup direktori yang dibuka sebelumnya. Untuk saat ini, abaikan fungsi lain seperti strlendan memcpy.



Seperti yang Anda lihat, sangat mudah untuk melihat fungsi pustaka yang dipanggil, tetapi dalam artikel ini kita akan fokus pada panggilan sistem yang dipanggil oleh fungsi pustaka sistem.



Untuk melihat panggilan sistem gunakan stracedengan perintah ls testdirseperti yang ditunjukkan di bawah ini. Dan sekali lagi Anda mendapatkan banyak informasi yang tidak koheren:



[root@sandbox tmp]# strace ls testdir/
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
brk(NULL)                               = 0x1f12000
<<< truncated strace output >>>
write(1, "file1  file2\n", 13file1  file2
)          = 13
close(1)                                = 0
munmap(0x7fd002c8d000, 4096)            = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++
[root@sandbox tmp]#


Sebagai hasil eksekusi, straceAnda akan menerima daftar panggilan sistem yang dijalankan selama eksekusi perintah ls. Semua panggilan sistem dapat dibagi ke dalam kategori berikut:



  • Manajemen proses
  • Manajemen file
  • Direktori dan manajemen sistem file
  • Lainnya




Ada cara mudah untuk menganalisis informasi yang diterima - tulis output ke file menggunakan opsi -o.



[root@sandbox tmp]# strace -o trace.log ls testdir/
file1  file2
[root@sandbox tmp]#




Kali ini, tidak akan ada data di layar - perintah akan lsberfungsi seperti yang diharapkan, menampilkan daftar file dan menulis semua output straceke file trace.log. Untuk perintah sederhana, lsfile tersebut berisi hampir 100 baris:



[root@sandbox tmp]# ls -l trace.log
-rw-r--r--. 1 root root 7809 Oct 12 13:52 trace.log
[root@sandbox tmp]#
[root@sandbox tmp]# wc -l trace.log
114 trace.log
[root@sandbox tmp]#




Lihat baris pertama di file trace.log:



execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0




  • Di awal baris adalah nama panggilan sistem yang sedang dijalankan - execve.
  • Teks dalam tanda kurung adalah argumen yang diteruskan ke panggilan sistem.
  • Angka setelah tanda = (dalam hal ini, 0) adalah nilai yang dikembalikan oleh pemanggilan sistem.




Sekarang hasilnya tidak terlalu menakutkan, bukan? Dan Anda juga dapat menerapkan logika yang sama untuk baris lain.



Perhatikan satu-satunya perintah yang Anda panggil - ls testdir. Anda tahu nama direktori yang digunakan oleh perintah tersebut ls, jadi mengapa tidak menggunakan grepfor testdirdi dalam file trace.logdan melihat apa yang ditemukannya? Perhatikan baik-baik hasilnya:



[root@sandbox tmp]# grep testdir trace.log
execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
stat("testdir/", {st_mode=S_IFDIR|0755, st_size=32, ...}) = 0
openat(AT_FDCWD, "testdir/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
[root@sandbox tmp]#




Kembali ke analisis di atas execve, dapatkah Anda mengetahui apa yang dilakukan oleh system call selanjutnya?



execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0




Anda tidak perlu mengingat semua panggilan sistem dan apa yang dilakukannya: semuanya ada di dokumentasi. Halaman manual sedang terburu-buru untuk membantu! Pastikan paket diinstal sebelum menjalankan perintah man man-pages:



[root@sandbox tmp]# rpm -qa | grep -i man-pages
man-pages-3.53-5.el7.noarch
[root@sandbox tmp]#




Ingatlah bahwa Anda perlu menambahkan "2" antara perintah mandan nama syscall. Jika Anda membaca manabout man( man man), Anda akan melihat bahwa bagian 2 dicadangkan untuk panggilan sistem. Demikian juga, jika Anda memerlukan informasi tentang fungsi pustaka, Anda perlu menambahkan 3 antara mandan nama fungsi pustaka.



Di bawah ini adalah nomor bagian man:



1.       .
2.   (,  ).
3.   (  ).
4.   (    /dev).




Untuk melihat dokumentasi untuk panggilan sistem, jalankan man dengan nama panggilan sistem itu.



man 2 execve




Menurut dokumentasi, panggilan sistem execvemenjalankan program yang diteruskan ke dalam parameter (dalam hal ini adalah ls). Parameter tambahan untuk ls juga diteruskan padanya. Dalam contoh ini, itu testdir. Oleh karena itu, panggilan sistem ini hanya berjalan lsdengan testdirsebagai parameter:



'execve - execute program'

'DESCRIPTION
       execve()  executes  the  program  pointed to by filename'




Panggilan sistem berikutnya statdiberikan parameter testdir:



stat("testdir/", {st_mode=S_IFDIR|0755, st_size=32, ...}) = 0




Untuk melihat penggunaan dokumentasi man 2 stat. Panggilan sistem stat mengembalikan informasi tentang file yang ditentukan. Ingatlah bahwa semua yang ada di Linux adalah file, termasuk direktori.



Selanjutnya, panggilan sistem openatterbuka testdir. Perhatikan bahwa nilai yang dikembalikan adalah 3. Ini adalah deskriptor file yang akan digunakan dalam panggilan sistem selanjutnya:



openat(AT_FDCWD, "testdir/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3




Sekarang buka file tersebut
trace.log
dan perhatikan baris yang mengikuti panggilan sistem openat. Anda akan melihat panggilan sistem getdentsyang melakukan sebagian besar pekerjaan yang diperlukan untuk menjalankan perintah ls testdir. Sekarang mari kita jalankan grep getdentsuntuk file tersebut trace.log:



[root@sandbox tmp]# grep getdents trace.log
getdents(3, /* 4 entries */, 32768)     = 112
getdents(3, /* 0 entries */, 32768)     = 0
[root@sandbox tmp]#




Documentation ( man getdents) mengatakan bahwa ia getdentsmembaca entri direktori , yang persis seperti yang kita butuhkan. Perhatikan bahwa argumen untuk getdent3 adalah deskriptor file yang diperoleh sebelumnya dari panggilan sistem openat.



Sekarang isi direktori telah diterima, kita memerlukan cara untuk menampilkan informasi di terminal. Jadi, kami lakukan grepuntuk panggilan sistem lain write, yang digunakan untuk mengeluarkan ke terminal:



[root@sandbox tmp]# grep write trace.log
write(1, "file1  file2\n", 13)          = 13
[root@sandbox tmp]#




Dalam argumen, Anda dapat melihat nama file yang akan di-output: file1dan file2. Untuk argumen pertama (1), ingatlah bahwa di Linux, tiga deskriptor file dibuka secara default untuk proses apa pun:



  • 0 - aliran input standar
  • 1 - aliran keluaran standar
  • 2 - aliran kesalahan standar




Jadi, panggilan sistem writemengambil file1dan file2keluaran standar, yang merupakan terminal, menunjukkan angka 1.



Sekarang Anda tahu apa yang panggilan sistem lakukan sebagian besar pekerjaan untuk tim ls testdir/. Tetapi bagaimana dengan 100+ panggilan sistem lainnya dalam file trace.log?



Sistem operasi melakukan banyak hal pendukung untuk memulai proses, sehingga banyak hal yang Anda lihat di file trace.logadalah menginisialisasi dan membersihkan proses. Lihat file trace.log sepenuhnya dan coba pahami apa yang terjadi saat perintah dijalankan ls.



Sekarang Anda dapat menganalisis panggilan sistem untuk program apa pun. Utilitas strace juga menyediakan banyak opsi baris perintah yang berguna, beberapa di antaranya dijelaskan di bawah ini.



Secara default strace, ini tidak menampilkan semua informasi tentang panggilan sistem. Namun, ini memiliki opsi -v verboseyang akan menampilkan informasi tambahan tentang setiap panggilan sistem:



strace -v ls testdir




Ini adalah praktik yang baik untuk menggunakan parameter -funtuk melacak proses anak yang dibuat oleh proses yang sedang berjalan:



strace -f ls testdir




Tetapi bagaimana jika Anda hanya menginginkan nama panggilan sistem, berapa kali mereka dijalankan, dan persentase waktu yang dihabiskan untuk mengeksekusinya? Anda dapat menggunakan opsi -cuntuk mendapatkan statistik ini:



strace -c ls testdir/




Jika Anda ingin melacak panggilan sistem tertentu, misalnya, opendan mengabaikan yang lain, Anda dapat menggunakan opsi -edengan nama panggilan sistem:



[root@sandbox tmp]# strace -e open ls testdir
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
file1  file2
+++ exited with 0 +++
[root@sandbox tmp]#




Bagaimana jika Anda perlu memfilter dengan beberapa panggilan sistem? Jangan khawatir, Anda dapat menggunakan opsi yang sama -edan memisahkan panggilan sistem yang diperlukan dengan koma. Misalnya untuk writedan getdent:



[root@sandbox tmp]# strace -e write,getdents ls testdir
getdents(3, /* 4 entries */, 32768)     = 112
getdents(3, /* 0 entries */, 32768)     = 0
write(1, "file1  file2\n", 13file1  file2
)          = 13
+++ exited with 0 +++
[root@sandbox tmp]#




Sejauh ini, kami hanya melacak perintah eksplisit yang dijalankan. Tapi bagaimana dengan perintah yang dijalankan sebelumnya? Bagaimana jika Anda ingin melacak setan? Untuk melakukan ini, Anda stracememiliki opsi khusus di -pmana Anda dapat meneruskan ID proses.



Kami tidak akan memulai daemon, tetapi menggunakan perintah catyang menampilkan konten file yang diteruskan sebagai argumen. Tetapi jika Anda tidak menentukan argumen, maka perintah cathanya akan menunggu masukan dari pengguna. Setelah memasukkan teks, itu akan menampilkan teks yang dimasukkan di layar. Begitu seterusnya hingga pengguna mengklik Ctrl+Cuntuk keluar.



Jalankan perintah catdi satu terminal.



[root@sandbox tmp]# cat




Di terminal lain, temukan ID proses (PID) dengan perintah ps:



[root@sandbox ~]# ps -ef | grep cat
root      22443  20164  0 14:19 pts/0    00:00:00 cat
root      22482  20300  0 14:20 pts/1    00:00:00 grep --color=auto cat
[root@sandbox ~]#




Sekarang mulailah stracedengan opsi -pdan PID yang Anda temukan ps. Setelah memulai, itu straceakan menampilkan informasi tentang proses yang terhubung, serta PID-nya. Sekarang stracememantau panggilan sistem yang dilakukan oleh perintah cat. Panggilan sistem pertama yang akan Anda lihat dibaca, menunggu masukan dari utas 0, yaitu, dari input standar, yang sekarang menjadi terminal tempat perintah dijalankan cat:



[root@sandbox ~]# strace -p 22443
strace: Process 22443 attached
read(0,




Sekarang kembali ke terminal tempat Anda membiarkan perintah berjalan catdan masukkan beberapa teks. Untuk demonstrasi, saya masuk x0x0. Harap dicatat bahwa saya cathanya mengulangi apa yang saya masukkan dan x0x0layar akan muncul dua kali.



[root@sandbox tmp]# cat
x0x0
x0x0




Kembali ke terminal tempat Anda straceterhubung ke proses cat. Sekarang Anda melihat dua panggilan sistem baru: yang sebelumnya read, yang sekarang telah dibaca x0x0, dan satu lagi untuk menulis write, yang menulis x0x0kembali ke terminal, dan lagi yang baru read, yang menunggu pembacaan dari terminal. Perhatikan bahwa input standar (0) dan output standar (1) ada di terminal yang sama:



[root@sandbox ~]# strace -p 22443
strace: Process 22443 attached
read(0, "x0x0\n", 65536)                = 5
write(1, "x0x0\n", 5)                   = 5
read(0,




Bayangkan manfaat peluncuran stracedaemon: Anda dapat melihat semua yang terjadi di latar belakang. Selesaikan perintahnya
cat
dengan mengklik
Ctrl+C
... Ini juga akan menghentikan sesi
strace
sejak proses yang dipantau telah dihentikan.



Untuk melihat stempel waktu panggilan sistem, gunakan opsi -t:



[root@sandbox ~]#strace -t ls testdir/

14:24:47 execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
14:24:47 brk(NULL)                      = 0x1f07000
14:24:47 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2530bc8000
14:24:47 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
14:24:47 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3




Bagaimana jika Anda ingin mengetahui waktu yang dihabiskan di antara panggilan sistem? Ada opsi praktis -ryang menunjukkan waktu yang dibutuhkan untuk menjalankan setiap panggilan sistem. Cukup berguna, bukan?



[root@sandbox ~]#strace -r ls testdir/

0.000000 execve("/usr/bin/ls", ["ls", "testdir/"], [/* 40 vars */]) = 0
0.000368 brk(NULL)                 = 0x1966000
0.000073 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb6b1155000
0.000047 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
0.000119 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3




Kesimpulan



Utilitas ini stracesangat berguna untuk mempelajari panggilan sistem di Linux. Untuk opsi baris perintah lainnya, lihat manual dan dokumentasi online.






All Articles