Apakah Linux secara otomatis membersihkan soket domain abstrak?

15

Ada jawaban yang bagus pada StackOverflow tentang memberikan kunci yang lebih baik untuk daemon (disintesis dari Eduardo Fleury ) yang tidak bergantung pada mekanisme kunci file PID umum untuk daemon. Ada banyak komentar bagus di sana tentang mengapa file kunci PID terkadang dapat menyebabkan masalah, jadi saya tidak akan mengulanginya di sini.

Singkatnya, solusinya bergantung pada soket domain namespace abstrak Linux, yang melacak soket berdasarkan nama untuk Anda, daripada mengandalkan file, yang dapat bertahan setelah daemon SIGKILL'd. Contoh tersebut menunjukkan bahwa Linux tampaknya membebaskan soket setelah proses mati.

Tapi saya tidak dapat menemukan dokumentasi definitif di Linux yang mengatakan apa yang sebenarnya Linux lakukan dengan soket abstrak ketika proses terikat SIGKILL. Apakah ada yang tahu?

Dengan kata lain, kapan tepatnya soket abstrak dibebaskan untuk digunakan lagi?

Saya tidak ingin mengganti mekanisme file PID dengan soket abstrak kecuali jika secara definitif menyelesaikan masalah.

CivFan
sumber
3
Saya tidak dapat menemukan apa pun yang menjawab ini secara langsung. Tetapi karena tidak ada API untuk menghapus soket abstrak, sepertinya mereka harus dikelola secara otomatis oleh kernel. Ketika tidak ada proses dengan soket terbuka, itu harus pergi.
Barmar
@Barmar Cukup adil. Ingin menambahkannya sebagai jawaban?
CivFan
Saya lebih suka memiliki informasi yang lebih pasti.
Barmar

Jawaban:

5

Ya, linux secara otomatis "membersihkan" soket abstrak sejauh pembersihan bahkan masuk akal. Berikut ini contoh kerja minimal yang dapat Anda verifikasi ini:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>

int
main(int argc, char **argv)
{
  int s;
  struct sockaddr_un sun;

  if (argc != 2 || strlen(argv[1]) + 1 > sizeof(sun.sun_path)) {
    fprintf(stderr, "usage: %s abstract-path\n", argv[0]);
    exit(1);
  }

  s = socket(AF_UNIX, SOCK_STREAM, 0);
  if (s < 0) {
    perror("socket");
    exit(1);
  }
  memset(&sun, 0, sizeof(sun));
  sun.sun_family = AF_UNIX;
  strcpy(sun.sun_path + 1, argv[1]);
  if (bind(s, (struct sockaddr *) &sun, sizeof(sun))) {
    perror("bind");
    exit(1);
  }
  pause();
}

Jalankan program ini sebagai ./a.out /test-socket &, lalu jalankan ss -ax | grep test-socket, dan Anda akan melihat soket yang digunakan. Kemudian kill %./a.out, dan ss -axakan ditampilkan soket yang hilang.

Namun, alasan Anda tidak dapat menemukan pembersihan ini dalam dokumentasi apa pun adalah karena itu tidak benar-benar membersihkan dalam arti yang sama bahwa soket unix-domain non-abstrak perlu dibersihkan. Soket non-abstrak sebenarnya mengalokasikan inode dan membuat entri dalam direktori, yang perlu dibersihkan di sistem file yang mendasarinya. Sebaliknya, pikirkan soket abstrak yang lebih mirip nomor port TCP atau UDP. Tentu, jika Anda mengikat port TCP dan kemudian keluar, port TCP itu akan bebas lagi. Tetapi berapa pun angka 16-bit yang Anda gunakan masih ada secara abstrak dan selalu dilakukan. Namespace nomor port adalah 1-65535 dan tidak pernah berubah atau perlu dibersihkan.

Jadi pikirkan saja nama soket abstrak seperti nomor port TCP atau UDP, baru saja memilih dari sekumpulan nomor port yang jauh lebih besar yang kebetulan terlihat seperti nama path tetapi tidak. Anda tidak dapat mengikat nomor port yang sama dua kali (kecuali SO_REUSEADDRatau SO_REUSEPORT). Tetapi menutup soket (secara eksplisit atau implisit dengan mengakhiri) membebaskan port, tanpa ada yang tersisa untuk dibersihkan.

pengguna3188445
sumber
Sudah lulus uji bebek. Itu bagus untuk beberapa hal, seperti python, tapi saya berharap lebih untuk fitur-fitur kernel Linux. Ambil contoh port TCP / UDP Anda - ada banyak dokumentasi yang menjelaskan kapan port tersebut dapat digunakan kembali.
CivFan
2
Dan mengapa dokumentasi itu tidak berlaku untuk soket abstrak? Apakah Anda punya referensi? Pertanyaan yang lebih baik mungkin adalah di mana komplikasi pembersihan ekstra untuk soket Unix-domain yang non-abstrak didokumentasikan. Di sistem saya, itu ada di unix (7) , yang mengatakan "Linux juga mendukung namespace abstrak yang tidak tergantung pada sistem file." Jadi bagi saya "independen dari filesystem" tidak menyiratkan pembersihan khusus sistem file.
user3188445
5

Saya memposting pertanyaan ini lebih dari setahun yang lalu dan tidak pernah cukup puas dengan kurangnya dokumentasi yang pasti. Saya pikir saya akan memeriksa dokumentasi Linux lagi untuk setiap pembaruan, dan senang melihat ini :

Soket abstrak

Izin soket tidak memiliki arti untuk soket abstrak: proses umask (2) tidak berpengaruh ketika mengikat soket abstrak, dan mengubah kepemilikan dan izin objek (melalui fchown (2) dan fchmod (2)) tidak berpengaruh pada aksesibilitas soket.

Soket abstrak otomatis hilang ketika semua referensi terbuka ke soket ditutup.

Juga, Antarmuka Pemrograman Linux oleh Michael Kerrisk mencakup pertanyaan (dikirim silang dari jawaban lain ini ):

57.6 Linux Abstract Socket Namespace

Yang disebut namespace abstrak adalah fitur khusus Linux yang memungkinkan kita untuk mengikat soket domain UNIX ke nama tanpa nama itu dibuat dalam sistem file. Ini memberikan beberapa potensi keuntungan:

  • Kami tidak perlu khawatir tentang kemungkinan tabrakan dengan nama yang ada di sistem file.
  • Tidak perlu memutuskan tautan socket pathname ketika kita selesai menggunakan socket. Nama abstrak dihapus secara otomatis ketika soket ditutup.
  • Kita tidak perlu membuat pathname sistem file untuk socket. Ini mungkin berguna dalam lingkungan chroot, atau jika kita tidak memiliki akses tulis ke sistem file.

Untuk membuat pengikatan abstrak, kami menentukan byte pertama bidang sun_path sebagai byte nol (\ 0). [...]

Saya rasa itu, bersama dengan jawaban @ user3188445, ini membersihkan pertanyaan dengan sangat tepat.

Yang mengatakan, masih ada asumsi yang dibuat di sini, bahwa proses yang SIGKILL akan menutup semua soket terbuka. Itu tampaknya asumsi yang masuk akal, tetapi saya tidak memiliki dokumentasi yang mendefinisikan perilaku itu.

CivFan
sumber
1
Paragraf terakhir: soket adalah deskriptor file, dan semua deskriptor file terbuka ditutup saat proses keluar. Ish. Lebih khusus lagi, soket adalah file yang terbuka, file yang terbuka dapat dilewatkan misalnya diwarisi oleh proses anak. Jadi Anda harus memastikan untuk memanggil socket SOCK_CLOEXECjika seandainya ada kode (termasuk perpustakaan) yang pernah melakukan fork () + exec (). Membuat proses anak tambahan menggunakan fork () tanpa exec () kurang umum; Anda mungkin sudah tahu jika Anda melakukan itu.
sourcejedi
Tidak perlu membatalkan tautan ... - um, karena tidak ada nama jalur, tidak mungkin untuk memutuskan tautan, bukan hanya tidak perlu.
domen