ควบคุมว่ากระบวนการใดที่จะถูกยกเลิกโดย Ctrl + C


13

ฉันมีซีดีสดที่บูทเข้าสู่ Linux และรันสคริปต์ Bash ขนาดเล็ก สคริปต์ค้นหาและเรียกใช้โปรแกรมที่สอง (ซึ่งมักจะเป็นไบนารี C ++ ที่คอมไพล์แล้ว)

คุณควรจะสามารถที่จะยกเลิกโปรแกรมที่สองโดยการกด+Ctrl Cสิ่งที่ควรเกิดขึ้นก็คือโปรแกรมที่สองหยุดทำงานและสคริปต์ Bash ยังคงทำงานต่อไป สิ่งที่เกิดขึ้นจริงคือทั้งแอปพลิเคชันหลักและสคริปต์ Bash ยุติ ซึ่งเป็นปัญหา

ดังนั้นฉันจึงใช้trapbuiltin เพื่อบอก Bash ให้เพิกเฉย SIGINT และตอนนี้Ctrl+ Cยกเลิกแอปพลิเคชัน C ++ แต่ Bash ยังคงทำงานต่อไป ยิ่งใหญ่

โอ้ใช่ ... บางครั้ง "แอปพลิเคชั่นที่สอง" เป็นสคริปต์ Bash อื่น และในที่กรณีCtrl+ Cตอนนี้จะไม่มีอะไร แต่อย่างใด

เห็นได้ชัดว่าฉันเข้าใจว่าสิ่งนี้ทำงานอย่างไร ... ฉันจะควบคุมกระบวนการใดที่จะได้รับ SIGINT เมื่อผู้ใช้กดCtrl+ C? ฉันต้องการนำสัญญาณนี้ไปยังกระบวนการที่ระบุเพียงกระบวนการเดียว

คำตอบ:


11

หลังจากผ่านไปหลายชั่วโมงในการค้นหาใบหน้าของอินเทอร์เน็ตฉันได้พบคำตอบแล้ว

  1. ลินุกซ์มีความคิดของการที่กลุ่มกระบวนการ

  2. ไดรเวอร์ TTY มีแนวคิดของกลุ่มกระบวนการด้านหน้า

  3. เมื่อคุณกดCtrl+ CTTY จะส่งSIGINTไปยังทุกกระบวนการในกลุ่มกระบวนการด้านหน้า (ดูรายการบล็อกนี้ด้วย)

นี่คือเหตุผลที่ทั้งไบนารีคอมไพล์และสคริปต์ที่เปิดใช้งานนั้นทั้งคู่ได้รับการบล็อก อันที่จริงฉันแค่ต้องการให้แอปพลิเคชันหลักรับสัญญาณนี้ไม่ใช่สคริปต์เริ่มต้น

วิธีแก้ปัญหาชัดเจนแล้ว: เราต้องใส่แอปพลิเคชันลงในกลุ่มกระบวนการใหม่และทำให้เป็นกลุ่มกระบวนการพื้นหน้าสำหรับ TTY นี้ เห็นได้ชัดว่าคำสั่งที่จะทำคือ

setsid -c <applcation>

และนั่นคือทั้งหมด ตอนนี้เมื่อผู้ใช้กดCtrl+ CSIGINT จะถูกส่งไปยังแอปพลิเคชัน (และเด็ก ๆ อาจมี) และไม่มีใครอื่น ฉันต้องการอะไร

  • setsid ด้วยตัวเองทำให้แอปพลิเคชันในกลุ่มกระบวนการใหม่ (แน่นอน "เซสชัน" ใหม่ทั้งหมดซึ่งเห็นได้ชัดว่าเป็นกลุ่มของกลุ่มกระบวนการ)

  • การเพิ่ม-cแฟล็กทำให้กลุ่มกระบวนการใหม่นี้กลายเป็นกลุ่มกระบวนการ "เบื้องหน้า" สำหรับ TTY ปัจจุบัน (เช่นมันจะได้รับSIGINTเมื่อคุณกดCtrl+ C)

ฉันเห็นข้อมูลที่ขัดแย้งกันมากมายเกี่ยวกับเมื่อ Bash ทำหรือไม่เรียกใช้กระบวนการในกลุ่มกระบวนการใหม่ (โดยเฉพาะดูเหมือนว่าจะแตกต่างกันสำหรับเปลือกหอย "แบบโต้ตอบ" และ "แบบไม่โต้ตอบ") ฉันเคยเห็นคำแนะนำที่คุณอาจจะได้รับสิ่งนี้เพื่อทำงานกับเล่ห์เหลี่ยมท่อฉลาด ... ฉันไม่รู้ แต่วิธีการข้างต้นดูเหมือนจะได้ผลสำหรับฉัน


5
คุณเกือบจะได้มัน ... การควบคุมงานถูกปิดใช้งานโดยค่าเริ่มต้นเมื่อทำงานสคริปต์ set -mแต่คุณสามารถเปิดใช้งานได้ด้วย มันค่อนข้างสะอาดและง่ายกว่าการใช้setsidทุกครั้งที่คุณเป็นเด็ก
psusi

@psusi ขอบคุณสำหรับเคล็ดลับ! ฉันแค่ต้องวิ่งหนีลูกคนเดียวดังนั้นจึงไม่ใช่เรื่องใหญ่ ตอนนี้ฉันรู้แล้วว่าจะดูคู่มือ Bash ได้อย่างไร ...
MathematicalOrchid

กระแทกแดกดันฉันมีปัญหาย้อนกลับที่ฉันต้องการให้ผู้ปกครองจับ sigint แต่มันไม่ได้เพราะ "กลอุบายท่อฉลาด" กลุ่มความคืบหน้า -> กลุ่มกระบวนการ
Andrew Domaszek

2

ดังที่ฉันได้กล่าวถึงในความคิดเห็นถึง f01 คุณควรจะส่ง SIGTERM ไปยังกระบวนการลูก นี่เป็นสคริปต์สองสามตัวที่แสดงวิธีดักจับ ^ C และส่งสัญญาณไปยังกระบวนการลูก

ครั้งแรกที่ผู้ปกครอง

traptest

#!/bin/bash

# trap test
# Written by PM 2Ring 2014.10.23

myname=$(basename "$0")
child=sleeploop

set_trap()
{
    sig=$1
    msg="echo -e \"\n$myname received ^C, sending $sig to $child, $pid\""
    trap "$msg; kill -s $sig $pid" SIGINT
}
trap "echo \"bye from $myname\"" EXIT

echo "running $child..."
./$child 5  &
pid=$!

# set_trap SIGINT
set_trap SIGTERM
echo "$child pid = $pid"

wait $pid
echo "$myname finished waiting"

และตอนนี้เด็ก

sleeploop

#!/bin/bash

# child script for traptest
# Written by PM 2Ring 2014.10.23

myname=$(basename "$0")
delay="$1"

set_trap()
{
    sig=$1
    trap "echo -e '\n$myname received $sig signal';exit 0" $sig
}

trap "echo \"bye from $myname\"" EXIT
set_trap SIGTERM
set_trap SIGINT

#Select sleep mode
if false
then
    echo "Using foreground sleep"
    Sleep()
    {
        sleep $delay
    }
else
    echo "Using background sleep"
    Sleep()
    {
        sleep "$delay" &
        wait $!
    }
fi

#Time to snooze :)
for ((i=0; i<5; i++));
do
    echo "$i: sleeping for $delay"
    Sleep
done

echo "$myname terminated normally"

หาก traptest ส่งสิ่งที่ SIGTERM ทำงานได้ดี แต่ถ้า traptest ส่ง SIGINT แล้ว sleeploop จะไม่เห็นเลย

หาก sleeploop traps SIGTERM และโหมด sleep นั้นอยู่เบื้องหน้าแสดงว่าไม่สามารถตอบสนองต่อสัญญาณได้จนกว่าจะตื่นขึ้นจาก sleep sleep ปัจจุบัน แต่ถ้าโหมดสลีปเป็นพื้นหลังมันจะตอบสนองทันที


ขอขอบคุณสำหรับตัวอย่างที่ดีนี้มันช่วยให้ฉันมากในการทำความเข้าใจวิธีการที่ฉันสามารถปรับปรุงสคริปต์ของฉัน :)
TabeaKischka

2

ในสคริปต์เริ่มต้นทุบตีของคุณ

  • ติดตาม PID ของโปรแกรมที่สอง

  • จับ SIGINT

  • เมื่อคุณจับ SIGINT ได้ให้ส่ง SIGINT ไปยัง PID โปรแกรมที่สอง


1
ที่อาจช่วยไม่ได้ หากคุณดักจับ SIGINT ในสคริปต์หลักแล้วสคริปต์ลูกจะไม่สามารถรับ SIGINT ไม่ว่าคุณจะส่งมันจากผู้ปกครองหรือจากเชลล์อื่น แต่นั่นก็ไม่ได้เลวร้ายอย่างที่มันฟังเพราะคุณสามารถส่ง SIGTERM ไปให้เด็กซึ่งเห็นได้ชัดว่าเป็นสัญญาณที่ต้องการใช้
PM 2Ring

1
ทำไม SIGTERM ถึง“ ชอบ”?
ทางคณิตศาสตร์

พวกมันเกือบจะคล้ายกัน SIGINT เป็นสัญญาณที่ส่งโดยสถานีควบคุม / ผู้ใช้เช่น Ctrl + C SIGTERM คือสิ่งที่คุณสามารถส่งได้หากคุณต้องการให้กระบวนการยุติ ความคิดเพิ่มเติมได้ที่นี่en.wikipedia.org/wiki/Unix_signal
f01
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.