อะไรคือความแตกต่างที่แน่นอนระหว่าง“ subshell” และ“ กระบวนการลูก”


16

ตามนี้และนี้เป็น subshell (…)จะเริ่มต้นโดยใช้วงเล็บ

( echo "Hello" )

ตามนี้ , นี้และนี้เป็นกระบวนการที่คดเคี้ยวเมื่อคำสั่งจะถูกเริ่มต้นด้วย&

echo "Hello" &

ข้อกำหนด Posix ใช้คำsubshellในหน้านี้แต่ไม่ได้กำหนดและยังอยู่ในหน้าเดียวกันที่ไม่ได้กำหนด "กระบวนการเด็ก"

ทั้งสองกำลังใช้fork()ฟังก์ชันเคอร์เนลใช่ไหม

อะไรคือความแตกต่างที่แน่นอนในการเรียกส้อม "sub-shell" และส้อมอื่น ๆ เรียกว่า "child process"


ไม่ชัดเจนว่าทำไมคุณถึงเชื่อมโยง POSIX เหตุผล: นิยามพื้นฐานแทนนิยามพื้นฐาน : 3.93 Child Process "กระบวนการใหม่ที่สร้าง (โดย fork (), posix_spawn (), หรือ ... ) โดยกระบวนการที่กำหนด" ; 3.376 subshell "สภาพแวดล้อมการดำเนินเปลือกแตกต่างจากสภาพแวดล้อมการดำเนินเปลือกหลักหรือในปัจจุบัน" ดังนั้นไม่ใช่กรณีของสิ่งเดียวกัน นี่คือความแตกต่างที่คุณกำลังมองหา?
fra-san

@ Fra-แซchild processอาจมีสภาพแวดล้อมที่แตกต่างกันกว่าmain: ( LANG=C eval 'echo "$LANG"' )เช่นเดียวกับใน กระบวนการลูกนั้น (อยู่ในวงเล็บ) เป็น subshell (สภาพแวดล้อมที่แตกต่างกัน) หรือไม่?
Isaac

การแสดงออกใน( )คือโดยนิยาม subshell กับสภาพแวดล้อมการดำเนินการของตัวเอง ประเด็นของฉันคือไม่ต้องใช้ subshell เป็นกระบวนการย่อย (ตามที่Stéphaneชี้ในคำตอบของเขาด้วยตัวอย่าง ksh93) ดูเหมือนว่าsubshellและกระบวนการลูกจะต้องไม่เป็นทั้งผลลัพธ์ของการfork()โทร ดังนั้นการมองหาความแตกต่างระหว่างทางแยกสองประเภทนั้นไม่ได้เป็นมุมมองที่ถูกต้องสำหรับฉัน นั่นเป็นเหตุผลที่ฉันพยายามเข้าใจคำถามของคุณดีขึ้น
fra-san

อ่าตอนนี้ฉันเห็นว่าหน้าtldp ที่คุณเชื่อมโยงไปพูดจริง ๆ ว่า subshell เป็นกระบวนการย่อย ในความเห็นของฉันว่าคำจำกัดความเป็นการทำให้เข้าใจผิดง่ายอาจเป็นไปได้
fra-san

คำตอบ:


15

ในคำศัพท์ POSIX สภาพแวดล้อม subshell มีการเชื่อมโยงกับความคิดของเชลล์ประมวลสภาพแวดล้อม

สภาพแวดล้อม subshell เป็นสภาพแวดล้อมการทำงานของเชลล์แยกต่างหากที่สร้างขึ้นซ้ำซ้อนกับสภาพแวดล้อมพาเรนต์ สภาพแวดล้อมการดำเนินการนั้นรวมถึงสิ่งต่าง ๆ เช่นไฟล์ที่เปิด, umask, ไดเร็กทอรีการทำงาน, ตัวแปรเชลล์ / ฟังก์ชัน / นามแฝง

การเปลี่ยนแปลงสภาพแวดล้อม subshell นั้นไม่ส่งผลกระทบต่อสภาพแวดล้อมหลัก

ตามเนื้อผ้าใน Bourne เชลล์หรือ ksh88 ซึ่งเป็นไปตามข้อกำหนด POSIX ซึ่งทำโดยการฟอร์กกระบวนการลูก

พื้นที่ที่ POSIX ต้องการหรืออนุญาตให้คำสั่งรันในสภาพแวดล้อม subshell เป็นพื้นที่ที่ ksh88 ตามปกติจะแยกกระบวนการ child shell

อย่างไรก็ตามมันไม่ได้บังคับให้การใช้งานเพื่อใช้กระบวนการลูกสำหรับสิ่งนั้น

เชลล์สามารถเลือกที่จะใช้สภาพแวดล้อมการดำเนินการแยกต่างหากแทนได้ตามที่พวกเขาต้องการ

ยกตัวอย่างเช่น ksh93 ทำได้โดยบันทึกคุณลักษณะของสภาพแวดล้อมการประมวลผลหลักและเรียกคืนเมื่อสิ้นสุดสภาพแวดล้อม subshell ในบริบทที่สามารถหลีกเลี่ยงการฟอร์กได้

ตัวอย่างเช่นใน:

cd /foo; pwd
(cd /bar; pwd)
pwd

POSIX จำเป็นต้องใช้cd /fooเพื่อทำงานในสภาพแวดล้อมที่แยกต่างหากและสิ่งที่ต้องการออก:

/foo
/bar
/foo

มันไม่ต้องการให้มันทำงานในกระบวนการที่แยกต่างหาก ตัวอย่างเช่นหาก stdout กลายเป็นไพพ์ที่ขาดการpwdรันในสภาพแวดล้อม subshell อาจส่ง SIGPIPE ไปยังกระบวนการเชลล์เพียงหนึ่งกระบวนการเท่านั้น

เชลล์ส่วนใหญ่รวมถึงbashจะใช้มันโดยการประเมินโค้ดที่อยู่(...)ในกระบวนการลูก (ในขณะที่กระบวนการหลักรอการยกเลิก) แต่ ksh93 จะทำงานแทนเมื่อเรียกใช้โค้ดภายใน(...)ทั้งหมดในกระบวนการเดียวกัน:

  • จำไว้ว่ามันอยู่ในสภาพแวดล้อม subshell
  • ให้cdบันทึกไดเร็กตอรี่ทำงานก่อนหน้านี้ (โดยทั่วไปคือ file descriptor ที่เปิดด้วย O_CLOEXEC), บันทึกค่าของตัวแปร OLDPWD, PWD และสิ่งที่cdอาจแก้ไขและทำchdir("/bar")
  • เมื่อกลับมาจาก subshell ไดเร็กทอรีการทำงานปัจจุบันจะถูกกู้คืน (พร้อมกับfchdir()fd ที่บันทึกไว้) และทุกอย่างอื่นที่ subshell อาจมีการแก้ไข

มีบริบทที่ไม่สามารถหลีกเลี่ยงกระบวนการลูกได้ ksh93 ไม่แยก:

  • var=$(subshell)
  • (subshell)

แต่ทำค่ะ

  • { subshell; } &
  • { subshell; } | other command

นั่นคือกรณีที่สิ่งต่าง ๆ ต้องทำงานในกระบวนการแยกต่างหากเพื่อให้สามารถทำงานพร้อมกันได้

การเพิ่มประสิทธิภาพ ksh93 ไปไกลกว่านั้น ตัวอย่างเช่นในขณะที่

var=$(pwd)

เปลือกหอยส่วนใหญ่จะแยกกระบวนการที่มีเด็กเรียกใช้pwdคำสั่งกับ stdout มันเปลี่ยนเส้นทางไปยังท่อpwdเขียนไดเรกทอรีการทำงานปัจจุบันไปยังท่อนั้นและการปกครองอ่านผลที่ปลายอีกด้านของท่อksh93เสมือนจริงทั้งหมดว่าด้วยค่า ต้องใช้ส้อมหรือท่อ ส้อมและไพพ์จะใช้สำหรับคำสั่งที่ไม่ได้สร้างขึ้นเท่านั้น

โปรดทราบว่ามีบริบทอื่น ๆ ที่ subshells ที่เปลือกแยกกระบวนการเด็ก ตัวอย่างเช่นในการเรียกใช้คำสั่งที่เก็บไว้ในไฟล์สั่งการแยกต่างหาก (และไม่ใช่สคริปต์ที่มีไว้สำหรับตัวแปลเชลล์เดียวกัน) เชลล์จะต้องแยกกระบวนการเพื่อประมวลผลคำสั่งนั้นมิฉะนั้นจะไม่เป็นเช่นนั้น สามารถเรียกใช้คำสั่งเพิ่มเติมหลังจากที่คำสั่งนั้นกลับมา

ใน:

/bin/echo "$((n += 1))"

นั่นไม่ใช่ subshell คำสั่งจะถูกประเมินในสภาพแวดล้อมการประมวลผลเชลล์ปัจจุบันnตัวแปรของสภาพแวดล้อมการดำเนินการเชลล์ปัจจุบันจะเพิ่มขึ้น แต่เชลล์จะแยกกระบวนการลูกเพื่อประมวลผล/bin/echoคำสั่งนั้นด้วยการขยาย$((n += 1))เป็นอาร์กิวเมนต์ .

เชลล์จำนวนมากใช้การปรับให้เหมาะสมโดยที่พวกเขาจะไม่แยกกระบวนการย่อยเพื่อรันคำสั่งภายนอกนั้นหากเป็นคำสั่งสุดท้ายของสคริปต์หรือ subshell (สำหรับเชลล์ย่อยที่ถูกใช้เป็นกระบวนการย่อย) ( bashอย่างไรก็ตามจะทำได้ก็ต่อเมื่อคำสั่งนั้นเป็นคำสั่งเดียวของ subshell)

สิ่งที่หมายถึงคือถ้าใช้เชลล์เหล่านั้นหากคำสั่งสุดท้ายใน subshell เป็นคำสั่งภายนอก subshell จะไม่ทำให้กระบวนการพิเศษเกิดขึ้นอีก ถ้าคุณเปรียบเทียบ:

a=1; /bin/echo "$a"; a=2; /bin/echo "$a"

กับ

a=1; /bin/echo "$a"; (a=2; /bin/echo "$a")

จะมีจำนวนกระบวนการเท่ากันที่สร้างขึ้นเฉพาะในกรณีที่สองเท่านั้นที่จะทำการแยกที่สองก่อนหน้านี้เพื่อให้a=2มีการเรียกใช้ในสภาพแวดล้อม subshell


1

subshell

เปลือกลูกจะเรียกว่า subshell เชลล์ย่อยสามารถสร้างจากพาเรนต์เชลล์และจากเชลล์อื่น สามารถสร้างเชลล์ย่อยโดยใช้:

1. รายการกระบวนการ

รายการกระบวนการคือการจัดกลุ่มคำสั่งที่อยู่ในวงเล็บ ตัวอย่าง:

( pwd ; (echo $BASH_SUBSHELL)) 

นี้จะพิมพ์ไดเรกทอรีการทำงานปัจจุบันและจำนวนของเปลือกหอยที่เกิด หมายเหตุการเรียกใช้ subshell มีราคาแพง

2. กระบวนการร่วม

มันวางไข่ subshell ในโหมดพื้นหลังและดำเนินการคำสั่งภายใน subshell นั้น

coproc sleep 10

หากคุณพิมพ์jobsคำสั่ง

[1]+  Running                 coproc COPROC sleep 10 &

คุณจะเห็นโหมดสลีปเป็นกระบวนการพื้นหลังที่ทำงานในพื้นหลัง

การแยกกระบวนการเด็ก

กระบวนการลูกในการคำนวณเป็นกระบวนการที่สร้างขึ้นโดยกระบวนการอื่น เมื่อใดก็ตามที่คำสั่งภายนอกถูกดำเนินการกระบวนการลูกจะถูกสร้างขึ้น การกระทำนี้เรียกว่าการฟอร์ก

$ps -f
UID        PID  PPID  C STIME TTY          TIME CMD  
umcr7     3647  3638  0 13:54 pts/0    00:00:00 bash
umcr7     3749  3647  0 13:59 pts/0    00:00:00 ps -f

ในฐานะที่ps -fเป็นคำสั่งภายนอก (เช่นคำสั่งภายนอกบางครั้งเรียกว่าคำสั่งระบบไฟล์เป็นโปรแกรมที่มีอยู่ด้านนอกของเปลือก bash.) นี้จะสร้างกระบวนการเด็กที่มีรหัสผู้ปกครองของเปลือกหอยที่มันจะถูกดำเนินการ


0

ทั้ง (subshell และ child shell) เป็นกระบวนการที่แยกต่างหากจาก parent shell (ทั้งคู่เป็น childs ของ parent shell) นั่นคือพวกเขามี PID ที่แตกต่างกัน และทั้งคู่เริ่มต้นด้วย fork (copy) ของ parent parent

เชลล์ย่อยคือสำเนาของพาเรนต์เชลล์ซึ่งตัวแปรฟังก์ชันการตั้งค่าสถานะและทุกอย่างพร้อมใช้งานเหมือนที่อยู่ในพาเรนต์เชลล์ การแก้ไขค่าดังกล่าวไม่ส่งผลกระทบต่อผู้ปกครอง

เปลือกลูกเริ่มต้นเป็นทางแยก แต่มันจะถูกรีเซ็ตเป็นค่าเริ่มต้นของเปลือกที่กำหนดโดยเริ่มต้น configs มันกลายเป็นกระบวนการที่ใช้เรียกใช้โค้ดบางอย่าง (ทั้งเชลล์หรือคำสั่ง)

เชลล์ย่อยสามารถเข้าถึงค่าตัวแปร:

$ x=123; ( echo "$x")
123

เปลือกลูกไม่สามารถ (ตัวแปรยกเลิกการส่งออก):

$ x=234; sh -c 'echo "x=$x"'
x=
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.