การจัดการสัญญาณที่มีหลายเธรดใน Linux


119

ใน Linux จะเกิดอะไรขึ้นเมื่อโปรแกรม (ซึ่งอาจมีหลายเธรด) ได้รับสัญญาณเช่น SIGTERM หรือ SIGHUP

กระทู้ไหนดักฟังสัญญาณ หลายเธรดสามารถรับสัญญาณเดียวกันได้หรือไม่? มีเธรดพิเศษเฉพาะสำหรับการจัดการสัญญาณหรือไม่? ถ้าไม่เกิดอะไรขึ้นภายในเธรดที่จัดการสัญญาณ? การดำเนินการจะดำเนินต่อไปอย่างไรหลังจากรูทีนตัวจัดการสัญญาณเสร็จสิ้น

คำตอบ:


35

สิ่งนี้มีความเหมาะสมเล็กน้อยขึ้นอยู่กับเวอร์ชันของเคอร์เนล Linux ที่คุณใช้

สมมติว่ามีเธรด 2.6 posix และหากคุณกำลังพูดถึงระบบปฏิบัติการที่ส่ง SIGTERM หรือ SIGHUP สัญญาณจะถูกส่งไปยังกระบวนการซึ่งรับและจัดการโดยรูทเธรด การใช้เธรด POSIX คุณสามารถส่ง SIGTERM ไปยังแต่ละเธรดได้เช่นกัน แต่ฉันสงสัยว่าคุณกำลังถามว่าจะเกิดอะไรขึ้นเมื่อระบบปฏิบัติการส่งสัญญาณไปยังกระบวนการ

ใน 2.6 SIGTERM จะทำให้เธรดชายด์ออกจาก "อย่างสมบูรณ์" โดยที่ 2.4 เธรดย่อยจะถูกปล่อยให้อยู่ในสถานะที่ไม่แน่นอน


และจะเกิดอะไรขึ้นภายในรูทเธรดเมื่อได้รับสัญญาณ? สมมติว่าฉันเขียนตัวจัดการสัญญาณที่กำหนดเองสำหรับ SIGUSR1 และตอนนี้ฉันกำลังส่งสัญญาณนั้นไปยังกระบวนการ รูทเธรดจะได้รับสัญญาณนั้น บางทีมันอาจจะอยู่ตรงกลางของฟังก์ชันบางอย่างในขณะนั้น กำลังจะเกิดอะไรขึ้น?

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

โปรดทราบว่านี่เป็นข้อมูลเฉพาะสำหรับ SIGUSR1 ซึ่ง IIRC ไม่ขัดขวางการเรียกของระบบ ตัวอย่างเช่นหากคุณลองใช้ SIGINT อาจขัดขวางการอ่านสตรีมและเมื่อคุณกลับไปอ่านสตรีมอาจส่งคืนข้อผิดพลาดที่ถูกขัดจังหวะ
Alan

10
ฉันสับสนเล็กน้อยเกี่ยวกับความหมายของ "รูทเธรด" หมายความว่าตัวจัดการสำหรับ SIGTERM จะทำงานในเธรดหลักเสมอหรือสามารถรันในเธรดใด ๆ ได้หรือไม่?
Stephen Nutt

3
คำตอบนี้ซึ่งระบุว่ามีการเลือกเธรดโดยพลการเพื่อจัดการกับสัญญาณซึ่งขัดแย้งกับคำตอบของคุณ
user202729

135

pthreads(7) อธิบายว่า POSIX.1 ต้องการเธรดทั้งหมดในแอ็ตทริบิวต์การแชร์กระบวนการซึ่งรวมถึง:

  • การจัดการสัญญาณ

POSIX.1 ยังต้องการคุณลักษณะบางอย่างเพื่อให้แตกต่างกันสำหรับแต่ละเธรด ได้แก่ :

complete_signalรูทีนของเคอร์เนล Linux มีบล็อกโค้ดต่อไปนี้ - ความคิดเห็นมีประโยชน์มาก:

/*
 * Now find a thread we can wake up to take the signal off the queue.
 *
 * If the main thread wants the signal, it gets first crack.
 * Probably the least surprising to the average bear.
 */
if (wants_signal(sig, p))
        t = p;
else if (!group || thread_group_empty(p))
        /*
         * There is just one thread and it does not need to be woken.
         * It will dequeue unblocked signals before it runs again.
         */
        return;
else {
        /*
         * Otherwise try to find a suitable thread.
         */
        t = signal->curr_target;
        while (!wants_signal(sig, t)) {
                t = next_thread(t);
                if (t == signal->curr_target)
                        /*
                         * No thread needs to be woken.
                         * Any eligible threads will see
                         * the signal in the queue soon.
                         */
                        return;
        }
        signal->curr_target = t;
}

/*
 * Found a killable thread.  If the signal will be fatal,
 * then start taking the whole group down immediately.
 */
if (sig_fatal(p, sig) &&
    !(signal->flags & SIGNAL_GROUP_EXIT) &&
    !sigismember(&t->real_blocked, sig) &&
    (sig == SIGKILL || !p->ptrace)) {
        /*
         * This signal will be fatal to the whole group.
         */

ดังนั้นคุณจะเห็นว่าคุณเป็นผู้รับผิดชอบในการส่งสัญญาณ:

หากกระบวนการของคุณตั้งค่าการจัดการของสัญญาณเป็นSIG_IGNหรือSIG_DFLสัญญาณจะถูกละเว้น (หรือค่าเริ่มต้น - kill, core หรือไม่สนใจ) สำหรับเธรดทั้งหมด

pthread_sigmask(3)ถ้ากระบวนการของคุณได้มีการกำหนดจำหน่ายสัญญาณที่จะประจำการที่เฉพาะเจาะจงจัดการแล้วคุณสามารถควบคุมด้ายจะได้รับสัญญาณโดยการจัดการกับหัวข้อเฉพาะหน้ากากสัญญาณใช้ คุณสามารถเสนอชื่อเธรดหนึ่งเธรดเพื่อจัดการเธรดทั้งหมดหรือสร้างเธรดหนึ่งเธรดต่อสัญญาณหรือการผสมผสานของอ็อพชันเหล่านี้สำหรับสัญญาณเฉพาะหรือคุณอาศัยพฤติกรรมเริ่มต้นปัจจุบันของเคอร์เนลของลินุกซ์ในการส่งสัญญาณไปยังเธรดหลัก

อย่างไรก็ตามสัญญาณบางอย่างมีความพิเศษตามsignal(7)หน้าคน:

สัญญาณอาจถูกสร้างขึ้น (และรอดำเนินการ) สำหรับกระบวนการโดยรวม (เช่นเมื่อส่งโดยใช้kill (2) ) หรือสำหรับเธรดเฉพาะ (เช่นสัญญาณบางอย่างเช่น SIGSEGV และ SIGFPE ที่สร้างขึ้นจากการดำเนินการ คำสั่งภาษาเครื่องที่เฉพาะเจาะจงจะถูกนำไปใช้กับเธรดเช่นเดียวกับสัญญาณที่กำหนดเป้าหมายไปยังเธรดเฉพาะโดยใช้ pthread_kill (3) ) สัญญาณที่กำหนดกระบวนการอาจถูกส่งไปยังเธรดใด ๆ ที่ไม่มีสัญญาณถูกปิดกั้นในขณะนี้ หากเธรดมากกว่าหนึ่งเธรดมีสัญญาณที่ถูกยกเลิกการปิดกั้นเคอร์เนลจะเลือกเธรดโดยพลการที่จะส่งสัญญาณ

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