Thread vs Proses di Linux

253

Saya baru-baru ini mendengar beberapa orang mengatakan bahwa di Linux, hampir selalu lebih baik menggunakan proses daripada thread, karena Linux sangat efisien dalam menangani proses, dan karena ada begitu banyak masalah (seperti penguncian) yang terkait dengan utas. Namun, saya curiga, karena sepertinya utas dapat memberikan peningkatan kinerja yang cukup besar dalam beberapa situasi.

Jadi pertanyaan saya adalah, ketika dihadapkan pada situasi yang dapat ditangani dengan baik oleh thread dan proses, haruskah saya menggunakan proses atau thread? Misalnya, jika saya menulis server web, haruskah saya menggunakan proses atau utas (atau kombinasi)?

pengguna17918
sumber
Apakah ada perbedaan dengan Linux 2.4?
mouviciel
3
Perbedaan antara proses dan utas dalam Linux 2.4 adalah bahwa utas berbagi lebih banyak bagian dari keadaan mereka (ruang alamat, pegangan file, dll.) Daripada proses, yang biasanya tidak. NPTL di Linux 2.6 membuat ini sedikit lebih jelas dengan memberi mereka "grup thread" yang sedikit mirip "proses" di win32 dan Solaris.
MarkR
6
Pemrograman bersamaan sulit. Kecuali jika Anda membutuhkan kinerja yang sangat tinggi, aspek terpenting dalam tradeoff Anda sering kali adalah kesulitan debugging . Proses membuat solusi yang jauh lebih mudah dalam hal ini, karena semua komunikasi eksplisit (mudah diperiksa, dicatat, dll.). Sebaliknya, memori bersama dari thread membuat gazillions tempat di mana satu thread dapat secara salah mempengaruhi yang lain.
Lutz Prechelt
1
@LutzPrechelt - Pemrograman bersamaan dapat multi-berulir serta multi-proses. Saya tidak melihat mengapa Anda menganggap pemrograman bersamaan adalah multi-threaded saja. Mungkin karena beberapa keterbatasan bahasa tertentu tetapi secara umum dapat menjadi keduanya.
iankit
2
Saya menautkan Lutz hanya menyatakan bahwa pemrograman konkuren adalah sesuatu yang sulit dipilih - proses atau utas - tetapi bahwa pemrograman konkuren menggunakan proses membuat proses debug lebih mudah dalam banyak kasus.
user2692263

Jawaban:

322

Linux menggunakan model threading 1-1, dengan (ke kernel) tidak ada perbedaan antara proses dan utas - semuanya hanyalah tugas yang dapat dijalankan. *

Di Linux, panggilan sistem clonemengkloning tugas, dengan tingkat berbagi yang dapat dikonfigurasi, di antaranya adalah:

  • CLONE_FILES: bagikan tabel deskriptor file yang sama (alih-alih membuat salinan)
  • CLONE_PARENT: jangan mengatur hubungan orangtua-anak antara tugas baru dan yang lama (jika tidak, child getppid()= parent's getpid())
  • CLONE_VM: berbagi ruang memori yang sama (alih-alih membuat salinan COW )

fork()panggilan clone(berbagi paling sedikit )dan pthread_create()panggilan clone(berbagi paling banyak ). **

forkBiaya sedikit lebih banyak daripada pthread_createkarena menyalin tabel dan membuat pemetaan SAP untuk memori, tetapi pengembang kernel Linux telah mencoba (dan berhasil) meminimalkan biaya-biaya tersebut.

Beralih di antara tugas, jika mereka berbagi ruang memori yang sama dan berbagai tabel, akan sedikit lebih murah daripada jika mereka tidak dibagikan, karena data mungkin sudah dimuat dalam cache. Namun, berpindah tugas masih sangat cepat walaupun tidak ada yang dibagikan - ini adalah hal lain yang coba dipastikan oleh pengembang kernel Linux (dan berhasil memastikan).

Bahkan, jika Anda menggunakan sistem multi-prosesor, tidak berbagi sebenarnya dapat bermanfaat bagi kinerja: jika setiap tugas berjalan pada prosesor yang berbeda, sinkronisasi memori bersama adalah mahal.


* Sederhana. CLONE_THREADmenyebabkan pengiriman sinyal untuk dibagikan (yang perlu CLONE_SIGHAND, yang berbagi tabel penangan sinyal).

** Sederhana. Ada keduanya SYS_forkdan SYS_clonesyscalls, tetapi di kernel, sys_forkdan sys_clonekeduanya adalah pembungkus yang sangat tipis di sekitar do_forkfungsi yang sama , yang itu sendiri adalah pembungkus tipis di sekitar copy_process. Ya, persyaratannya process,, threaddan taskdigunakan secara bergantian di kernel Linux ...

singkat
sumber
6
Saya pikir kita kehilangan 1 poin. Jika Anda membuat beberapa proses untuk server web Anda, maka Anda harus menulis proses lain untuk membuka soket dan meneruskan 'kerja' ke utas yang berbeda. Threading menawarkan satu proses beberapa utas, desain bersih. Dalam banyak situasi, thread itu wajar dan dalam situasi lain proses baru itu wajar. Ketika masalah jatuh di area abu-abu, trade off lainnya seperti yang dijelaskan oleh ephemient menjadi penting.
Saurabh
26
@ Suratabh Tidak juga. Anda dapat dengan mudah socket, bind, listen, fork, dan kemudian memiliki beberapa proses acceptkoneksi pada soket mendengarkan sama. Suatu proses dapat berhenti menerima jika sibuk, dan kernel akan merutekan koneksi yang masuk ke proses lain (jika tidak ada yang mendengarkan, kernel akan mengantri atau jatuh, tergantung pada listenbacklog). Anda tidak memiliki kendali lebih besar atas distribusi pekerjaan dari itu, tetapi biasanya itu cukup baik!
ephemient
2
@Bloodcount Semua proses / utas di Linux dibuat oleh mekanisme yang sama, yang mengkloning proses / utas yang ada. Bendera diberikan untuk clone()menentukan sumber daya mana yang dibagikan. Suatu tugas juga dapat unshare()sumber daya pada suatu titik waktu kemudian.
ephemient
4
@KarthikBalaguru Di dalam kernel itu sendiri, ada task_structuntuk setiap tugas. Ini sering disebut "proses" di seluruh kode kernel, tetapi sesuai dengan setiap utas yang dapat dijalankan. Tidak ada process_struct; jika sekelompok task_structs dihubungkan oleh thread_groupdaftar mereka , maka mereka "proses" yang sama untuk userspace. Ada sedikit penanganan khusus "utas", misalnya semua utas saudara dihentikan pada fork dan exec, dan hanya utas "utama" yang muncul ls /proc. Setiap utas dapat diakses melalui /proc/pid, apakah itu terdaftar /procatau tidak.
ephemient
5
@KarthikBalaguru Kernel mendukung kontinum perilaku antara utas dan proses; misalnya, clone(CLONE_THREAD | CLONE_VM | CLONE_SIGHAND))akan memberi Anda "utas" baru yang tidak membagikan direktori kerja, file, atau kunci, sementara clone(CLONE_FILES | CLONE_FS | CLONE_IO)akan memberi Anda "proses" yang berfungsi . Sistem yang mendasarinya menciptakan tugas dengan kloning; fork()dan pthread_create()hanya fungsi perpustakaan yang memanggil clone()berbeda (seperti yang saya tulis dalam jawaban ini).
ephemient
60

Linux (dan memang Unix) memberi Anda pilihan ketiga.

Opsi 1 - proses

Buat executable mandiri yang menangani beberapa bagian (atau semua bagian) aplikasi Anda, dan jalankan secara terpisah untuk setiap proses, misalnya program menjalankan salinannya sendiri untuk mendelegasikan tugas.

Opsi 2 - utas

Buat executable mandiri yang dimulai dengan utas tunggal dan buat utas tambahan untuk melakukan beberapa tugas

Opsi 3 - garpu

Hanya tersedia di Linux / Unix, ini sedikit berbeda. Proses bercabang benar-benar adalah prosesnya sendiri dengan ruang alamatnya sendiri - tidak ada yang dapat dilakukan anak (biasanya) untuk memengaruhi ruang alamat orang tua atau saudara kandungnya (tidak seperti utas) - sehingga Anda mendapatkan kekokohan tambahan.

Namun, halaman memori tidak disalin, itu adalah copy-on-write, jadi lebih sedikit memori yang digunakan daripada yang Anda bayangkan.

Pertimbangkan program server web yang terdiri dari dua langkah:

  1. Baca konfigurasi dan data runtime
  2. Sajikan permintaan halaman

Jika Anda menggunakan utas, langkah 1 akan dilakukan sekali, dan langkah 2 dilakukan di banyak utas. Jika Anda menggunakan proses "tradisional", langkah 1 dan 2 perlu diulang untuk setiap proses, dan memori untuk menyimpan konfigurasi dan data runtime digandakan. Jika Anda menggunakan fork (), maka Anda dapat melakukan langkah 1 sekali, dan kemudian fork (), meninggalkan data runtime dan konfigurasi dalam memori, tidak tersentuh, tidak disalin.

Jadi sebenarnya ada tiga pilihan.

MarkR
sumber
7
@Qwertie forking tidak begitu keren, itu merusak banyak perpustakaan dengan cara yang halus (jika Anda menggunakannya dalam proses induk). Ini menciptakan perilaku tak terduga yang membingungkan programmer bahkan berpengalaman.
MarkR
2
@MarkR dapatkah Anda memberikan beberapa contoh atau tautan tentang bagaimana forking memecah perpustakaan dan menciptakan perilaku yang tidak terduga?
Ehtesh Choudhury
18
Jika suatu proses bercabang dengan koneksi mysql terbuka, hal-hal buruk terjadi, karena soket dibagi antara dua proses. Bahkan jika hanya satu proses yang menggunakan koneksi, yang lain menghentikannya agar tidak ditutup.
MarkR
1
fork () system call ditentukan oleh POSIX (yang berarti tersedia pada sistem Unix), jika Anda menggunakan Linux API yang mendasarinya, yang merupakan system call clone (), maka Anda sebenarnya memiliki lebih banyak pilihan di Linux daripada hanya tiga .
Lie Ryan
2
@MarkR Pembagian soket sesuai dengan desain. Selain itu, salah satu proses dapat menutup soket menggunakan linux.die.net/man/2/shutdown sebelum memanggil tutup () pada soket.
Lelanthran
53

Itu tergantung pada banyak faktor. Proses lebih berat daripada thread, dan memiliki biaya startup dan shutdown yang lebih tinggi. Komunikasi antarproses (IPC) juga lebih keras dan lebih lambat daripada komunikasi antar cetakan.

Sebaliknya, proses lebih aman dan lebih aman daripada utas, karena setiap proses berjalan dalam ruang alamat virtualnya sendiri. Jika satu proses crash atau memiliki buffer overrun, itu tidak mempengaruhi proses lain sama sekali, sedangkan jika sebuah thread crash, itu akan menghapus semua utas lainnya dalam proses tersebut, dan jika sebuah thread memiliki buffer overrun, ia membuka lubang keamanan di semua utas.

Jadi, jika modul aplikasi Anda sebagian besar dapat berjalan secara independen dengan sedikit komunikasi, Anda mungkin harus menggunakan proses jika Anda mampu membayar biaya startup dan shutdown. Hit kinerja IPC akan minimal, dan Anda akan sedikit lebih aman terhadap bug dan lubang keamanan. Jika Anda membutuhkan setiap bit kinerja yang dapat Anda peroleh atau memiliki banyak data bersama (seperti struktur data yang kompleks), ikuti utas.

Adam Rosenfield
sumber
9
Jawaban Adam akan berfungsi sebagai penjelasan eksekutif. Untuk lebih detail, MarkR dan ephemient memberikan penjelasan yang baik. Penjelasan yang sangat rinci dengan contoh-contoh dapat ditemukan di cs.cf.ac.uk/Dave/C/node29.html tetapi tampaknya sedikit tanggal di beberapa bagian.
CyberFonic
2
CyberFonic berlaku untuk Windows. Seperti kata sesaat di bawah proses Linux tidak lebih berat. Dan di Linux semua mekanisme yang tersedia untuk komunikasi antara utas (futex, memori bersama, pipa, IPC) juga tersedia untuk proses dan berjalan pada kecepatan yang sama.
Russell Stuart
IPC lebih sulit digunakan tetapi bagaimana jika seseorang menggunakan "memori bersama"?
abhiarora
11

Yang lain telah membahas pertimbangannya.

Mungkin perbedaan penting adalah bahwa dalam proses Windows berat dan mahal dibandingkan dengan utas, dan di Linux perbedaannya jauh lebih kecil, sehingga persamaannya seimbang pada titik yang berbeda.

dmckee --- mantan kucing moderator
sumber
9

Sekali waktu ada Unix dan di Unix tua yang baik ini ada banyak overhead untuk proses, jadi apa yang dilakukan beberapa orang pintar adalah membuat utas, yang akan berbagi ruang alamat yang sama dengan proses induk dan mereka hanya perlu konteks yang dikurangi switch, yang akan membuat konteks switch lebih efisien.

Dalam Linux kontemporer (2.6.x) tidak ada banyak perbedaan dalam kinerja antara perubahan konteks dari suatu proses dibandingkan dengan utas (hanya hal-hal MMU yang ditambahkan untuk utas). Ada masalah dengan ruang alamat bersama, yang berarti bahwa pointer yang salah dalam utas dapat merusak memori proses induk atau utas lain dalam ruang alamat yang sama.

Suatu proses dilindungi oleh MMU, sehingga penunjuk yang salah hanya akan menyebabkan sinyal 11 dan tidak ada korupsi.

Secara umum saya akan menggunakan proses (tidak banyak konteks beralih overhead di Linux, tetapi perlindungan memori karena MMU), tetapi pthreads jika saya memerlukan kelas penjadwal waktu nyata, yang merupakan secangkir teh yang berbeda secara bersamaan.

Menurut Anda mengapa utas memiliki keuntungan kinerja yang begitu besar di Linux? Apakah Anda memiliki data untuk ini, atau hanya mitos?

robert.berger
sumber
1
Ya, saya punya beberapa data. Saya menjalankan tes yang menciptakan 100.000 proses dan tes yang menciptakan 100.000 utas. Versi utas berjalan sekitar 9x lebih cepat (17,38 detik untuk proses, 1,93 untuk utas). Sekarang ini hanya menguji waktu pembuatan, tetapi untuk tugas yang berumur pendek, waktu pembuatan bisa menjadi kunci.
user17918
4
@ user17918 - Apakah mungkin bagi Anda untuk membagikan kode yang digunakan oleh Anda untuk menghitung timing yang disebutkan di atas ..
codingfreak
satu perbedaan besar, dengan proses kernel membuat tabel halaman untuk setiap proses dan thead hanya menggunakan satu tabel halaman, jadi saya pikir adalah normal bahwa utas lebih cepat daripada proses
c4f4t0r
Cara sederhana lain untuk melihatnya adalah TCB lebih kecil dari PCB sehingga jelas bahwa proses konteks yang melibatkan PCB akan menghabiskan lebih banyak waktu daripada beralih dari utas.
Karthik Balaguru
5

Seberapa erat tugas Anda?

Jika mereka dapat hidup secara independen satu sama lain, maka gunakan proses. Jika mereka saling mengandalkan, maka gunakan utas. Dengan begitu Anda dapat membunuh dan memulai kembali proses yang buruk tanpa mengganggu pengoperasian tugas lainnya.

Robert
sumber
4

Untuk memperumit masalah lebih lanjut, ada yang namanya penyimpanan thread-lokal , dan memori bersama Unix.

Penyimpanan thread-local memungkinkan setiap utas untuk memiliki instance terpisah dari objek global. Satu-satunya waktu saya menggunakannya adalah ketika membangun lingkungan emulasi di linux / windows, untuk kode aplikasi yang berjalan dalam RTOS. Dalam RTOS setiap tugas adalah proses dengan ruang alamatnya sendiri, dalam lingkungan persaingan, setiap tugas adalah utas (dengan ruang alamat bersama). Dengan menggunakan TLS untuk hal-hal seperti lajang, kami dapat memiliki contoh terpisah untuk setiap utas, sama seperti di bawah lingkungan RTOS 'nyata'.

Memori bersama dapat (jelas) memberi Anda manfaat kinerja memiliki banyak proses mengakses memori yang sama, tetapi dengan biaya / risiko harus menyinkronkan proses dengan benar. Salah satu cara untuk melakukannya adalah memiliki satu proses membuat struktur data dalam memori bersama, dan kemudian mengirim pegangan ke struktur itu melalui komunikasi antar-proses tradisional (seperti pipa bernama).

KeyserSoze
sumber
1
Saya menggunakan penyimpanan utas-lokal untuk beberapa pertemuan statistik, terakhir kali saya menulis program jaringan berulir: setiap utas menulis ke penghitungnya sendiri, tidak ada kunci yang diperlukan, dan hanya jika dikirim, masing-masing utas menggabungkan statistiknya ke dalam total global. Tapi ya, TLS tidak terlalu umum digunakan atau diperlukan. Memori bersama, di sisi lain ... selain mengirim data secara efisien, Anda juga dapat berbagi semafor POSIX antar proses dengan menempatkannya dalam memori bersama. Sangat menakjubkan.
ephemient
4

Dalam karya terbaru saya dengan LINUX adalah satu hal yang harus diperhatikan adalah perpustakaan. Jika Anda menggunakan utas, pastikan ada perpustakaan yang Anda gunakan di utas aman. Ini membakar saya beberapa kali. Terutama libxml2 tidak aman di luar kotak. Itu dapat dikompilasi dengan thread aman tetapi bukan itu yang Anda dapatkan dengan aptitude install.

a8
sumber
3

Saya harus setuju dengan apa yang telah Anda dengar. Saat kami membandingkan cluster kami (xhpl dan semacamnya), kami selalu mendapatkan kinerja yang jauh lebih baik dengan proses di atas utas.</anecdote>

bengkak
sumber
3

Keputusan antara utas / proses sedikit tergantung pada apa yang akan Anda gunakan. Salah satu manfaat dari suatu proses adalah memiliki PID dan dapat dibunuh tanpa juga menghentikan orang tua.

Untuk contoh dunia nyata dari server web, apache 1.3 yang digunakan hanya mendukung beberapa proses, tetapi di 2.0 mereka menambahkan abstraksi sehingga Anda dapat beralih di antara keduanya. Komentar tampaknya untuk setuju bahwa proses lebih kuat tapi benang dapat memberikan sedikit memagut kinerja yang lebih baik (kecuali untuk jendela di mana kinerja untuk proses menyebalkan dan Anda hanya ingin menggunakan benang).

hlovdal
sumber
2

Untuk sebagian besar kasus, saya lebih suka proses daripada utas. utas dapat berguna ketika Anda memiliki tugas yang relatif lebih kecil (proses overhead >> waktu yang diambil oleh masing-masing unit tugas yang dibagi) dan ada kebutuhan berbagi memori di antara mereka. Pikirkan array yang besar. Juga (offtopic), perhatikan bahwa jika utilisasi CPU Anda 100 persen atau mendekati itu, tidak akan ada manfaat dari multithreading atau pemrosesan. (pada kenyataannya itu akan memburuk)

aise neal
sumber
Apa maksud Anda tanpa manfaat? Bagaimana dengan melakukan perhitungan berat di utas GUI? Memindahkan mereka ke thread paralel akan jauh lebih baik dari sudut pengalaman pengguna, tidak peduli bagaimana CPU dimuat.
olegst
2

Threads -> Threads berbagi ruang memori, ini adalah abstraksi dari CPU, itu ringan. Proses -> Proses memiliki ruang memori sendiri, ini adalah abstraksi komputer. Untuk memparalelkan tugas, Anda perlu mengabstraksi CPU. Namun keuntungan menggunakan proses di atas utas adalah keamanan, stabilitas sementara utas menggunakan memori yang lebih rendah daripada proses dan menawarkan latensi yang lebih rendah. Contoh dalam hal web adalah chrome dan firefox. Dalam hal Chrome, masing-masing tab adalah proses baru maka penggunaan memori chrome lebih tinggi daripada firefox, sementara keamanan dan stabilitas yang diberikan lebih baik daripada firefox. Keamanan di sini yang disediakan oleh chrome lebih baik, karena setiap tab adalah proses baru, tab yang berbeda tidak dapat menyelinap ke ruang memori dari proses yang diberikan.

Jubin Antony Thykattil
sumber
2

Saya pikir semua orang telah melakukan pekerjaan dengan baik menanggapi pertanyaan Anda. Saya hanya menambahkan informasi lebih lanjut tentang utas versus proses di Linux untuk mengklarifikasi dan merangkum beberapa tanggapan sebelumnya dalam konteks kernel. Jadi, tanggapan saya berkaitan dengan kode khusus kernel di Linux. Menurut dokumentasi Kernel Linux, tidak ada perbedaan yang jelas antara utas versus proses kecuali utas menggunakan ruang alamat virtual bersama tidak seperti proses. Perhatikan juga, Kernel Linux menggunakan istilah "tugas" untuk merujuk pada proses dan utas secara umum.

"Tidak ada struktur internal yang mengimplementasikan proses atau utas, sebaliknya ada struct task_struct yang menggambarkan unit penjadwalan abstrak yang disebut tugas"

Juga menurut Linus Torvalds, Anda TIDAK boleh berpikir tentang proses versus utas sama sekali dan karena terlalu membatasi dan satu-satunya perbedaan adalah COE atau Konteks Eksekusi dalam hal "pisahkan ruang alamat dari orang tua" atau ruang alamat bersama. Bahkan ia menggunakan contoh server web untuk menyampaikan maksudnya di sini (yang sangat merekomendasikan membaca).

Kredit penuh untuk dokumentasi kernel linux

grepit
sumber
-3

Jika Anda perlu berbagi sumber daya, Anda harus menggunakan utas.

Juga pertimbangkan fakta bahwa sakelar konteks antar thread jauh lebih murah daripada sakelar konteks antar proses.

Saya tidak melihat alasan untuk secara eksplisit menjalankan proses terpisah kecuali jika Anda memiliki alasan yang baik untuk melakukannya (keamanan, uji kinerja terbukti, dll ...)

Yuval Adam
sumber
3
Saya memiliki perwakilan untuk mengedit, tetapi saya tidak setuju. Switch konteks antar proses di Linux hampir semurah konteks beralih di antara thread.
ephemient