কোনও "ডিফার" ফ্যাশনে কোনও সিটিআরএল + সি সংকেত ক্যাপচার এবং একটি ক্লিনআপ ফাংশন চালানো কি সম্ভব?


207

আমি কনসোল থেকে প্রেরিত Ctrl+C( SIGINT) সিগন্যালটি ক্যাপচার করতে এবং কিছু আংশিক রান মোট সংগ্রহ করতে চাই।

গোলাং এ কি সম্ভব?

দ্রষ্টব্য: আমি যখন প্রথম প্রশ্ন পোস্ট করি তখন আমি Ctrl+Cতার SIGTERMপরিবর্তে থাকার বিষয়ে বিভ্রান্ত হয়ে পড়েছিলাম SIGINT

উত্তর:


259

আগত সংকেতগুলি পরিচালনা করতে আপনি ওএস / সিগন্যাল প্যাকেজটি ব্যবহার করতে পারেন । Ctrl+ + Cহয় SIGINT যাতে আপনি ফাঁদ এই ব্যবহার করতে পারেন, os.Interrupt

c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func(){
    for sig := range c {
        // sig is a ^C, handle it
    }
}()

আপনি আপনার প্রোগ্রামটি যেভাবে সমাপ্ত করে এবং তথ্য মুদ্রণ করে তা সম্পূর্ণ আপনার উপর নির্ভর করে।


ধন্যবাদ! সুতরাং ... it's সে এটা সিগন্যটার নয়, তাহলে? আপডেট: দুঃখিত, আপনি যে লিঙ্কটি দিয়েছেন তা যথেষ্ট বিস্তারিত!
সেবাস্তিয়ান গ্রিগনলি

19
পরিবর্তে for sig := range g {, আপনি <-sigchanএই আগের উত্তরের মতোও
ডেনিস সাগুরেট

3
@ অডিস্ট্রয়: অবশ্যই, যদি আপনি প্রকৃতপক্ষে প্রথম সংকেতের প্রতিক্রিয়াতে প্রোগ্রামটি শেষ করতে চলেছেন। লুপটি ব্যবহার করে আপনি প্রোগ্রামটি বন্ধ না করার সিদ্ধান্ত গ্রহণের ক্ষেত্রে সমস্ত সংকেত ধরতে পারবেন ।
লিলি ব্যালার্ড

79
দ্রষ্টব্য: এটি কাজ করার জন্য আপনাকে অবশ্যই প্রোগ্রামটি তৈরি করতে হবে। আপনি যদি go runকনসোলের মাধ্যমে প্রোগ্রামটি চালনা করেন এবং ডিগ্রি সেন্টিগ্রেডের মাধ্যমে একটি সংকেত প্রেরণ করেন তবে চ্যানেলটিতে সিগন্যাল লেখা রয়েছে এবং প্রোগ্রামটি প্রতিক্রিয়া জানায়, তবে অপ্রত্যাশিতভাবে লুপটি বাদ পড়েছে বলে মনে হয়। এটি কারণ SIGRERM go runপাশাপাশি যায় ! (এটি আমার যথেষ্ট বিভ্রান্তির কারণ করেছে!)
উইলিয়াম পার্সেল

5
নোট করুন যে গোরোটিনটি সিগন্যালটি পরিচালনা করার জন্য প্রসেসরের সময় পাওয়ার জন্য, প্রধান গোরোটিন অবশ্যই একটি ব্লকিং অপারেশন কল করতে হবে বা runtime.Goschedএকটি উপযুক্ত জায়গায় কল করতে হবে (আপনার প্রোগ্রামের প্রধান লুপে, যদি এটি থাকে)
মিস্টারবি

107

এইটা কাজ করে:

package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time" // or "runtime"
)

func cleanup() {
    fmt.Println("cleanup")
}

func main() {
    c := make(chan os.Signal)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    go func() {
        <-c
        cleanup()
        os.Exit(1)
    }()

    for {
        fmt.Println("sleeping...")
        time.Sleep(10 * time.Second) // or runtime.Gosched() or similar per @misterbee
    }
}

1
অন্যান্য পাঠকদের জন্য: আপনি কেন ওএস ধরতে চান সে সম্পর্কে একটি তথ্যের জন্য @ অ্যাডামান্টির জবাবটি দেখুন n আন্তঃবিহীন এবং সিস্টোল .SIGTERM এই উত্তরটিতে তাঁর ব্যাখ্যাটি অন্তর্ভুক্ত করা ভাল হত, বিশেষত যেহেতু তিনি আপনার আগে কয়েক মাস আগে পোস্ট করেছিলেন।
ক্রিস

1
আপনি কেন একটি অবরুদ্ধ চ্যানেল ব্যবহার করছেন? এটি কি প্রয়োজনীয়?
এএনআর

4
@ ব্যারি 1 এর পরিবর্তে বাফার আকার 2 কেন?
pdeva

2
এখানে ডকুমেন্টেশন থেকে একটি অংশ । "প্যাকেজ সিগন্যাল সি তে প্রেরণে বাধা দেবে না: কলকারীকে অবশ্যই নিশ্চিত করতে হবে যে সি প্রত্যাশিত সিগন্যাল রেট ধরে রাখতে পর্যাপ্ত বাফার স্থান রয়েছে। মাত্র একটি সংকেত মানের বিজ্ঞপ্তির জন্য ব্যবহৃত একটি চ্যানেলের জন্য, আকারের 1 বাফার যথেষ্ট" "
বিএমডিলেক্রুজ

25

অন্যান্য উত্তরের সাথে কিছুটা যুক্ত করতে, আপনি যদি সত্যিই সিগ্টারএম (কিল কমান্ডের মাধ্যমে প্রেরিত ডিফল্ট সিগন্যাল) ধরতে চান তবে আপনি ওএসের syscall.SIGTERMজায়গায় ব্যবহার করতে পারেন nঅন্তঘাতী। সাবধান যে সিস্কেল ইন্টারফেসটি সিস্টেম-নির্দিষ্ট এবং এটি সর্বত্র কাজ না করে (যেমন উইন্ডোতে)। তবে এটি উভয়কেই ধরার জন্য দুর্দান্তভাবে কাজ করে:

c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
....

7
signal.Notifyফাংশন একবারে অনেক সংকেত নির্দিষ্ট করার অনুমতি দেয়। সুতরাং, আপনি আপনার কোড সহজ করতে পারেন signal.Notify(c, os.Interrupt, syscall.SIGTERM)
জোচেন

আমি মনে করি পোস্টের পরে আমি এটি খুঁজে পেয়েছি। সংশোধন করা হয়েছে!
আদমলমার

2
ওস.কিল সম্পর্কে কী?
শস্যশূক

2
@ ক্লিপস দুর্দান্ত প্রশ্ন! os.Kill অনুরূপ করতে syscall.Killযা সংকেত যে পাঠানো যেতে পারে কিন্তু ধরা নয়। এটি আদেশের সমতুল্য kill -9 <pid>। আপনি যদি ধরতে চান kill <pid>এবং গ্রেটলি শটডাউন করতে পারেন, আপনাকে ব্যবহার করতে হবে syscall.SIGTERM
আদমলমার

@ অ্যাডম্লামার আহ, তা বোঝা যায়। ধন্যবাদ!
অ্যাভন

18

উপরের গৃহীত উত্তরে একটি বা দুটি ছোট টাইপ ছিল (পোস্ট করার সময়), সুতরাং এটি ক্লিন আপ করা সংস্করণ। এই উদাহরণে আমি সিপিইউ প্রোফাইলারটি থামিয়ে দিচ্ছি যখন Ctrl+ প্রাপ্তি হয় C

// capture ctrl+c and stop CPU profiler                            
c := make(chan os.Signal, 1)                                       
signal.Notify(c, os.Interrupt)                                     
go func() {                                                        
  for sig := range c {                                             
    log.Printf("captured %v, stopping profiler and exiting..", sig)
    pprof.StopCPUProfile()                                         
    os.Exit(1)                                                     
  }                                                                
}()    

2
নোট করুন যে গোরোটিনটি প্রসেসরটি হ্যান্ডেল করার জন্য প্রসেসরের সময় পাওয়ার জন্য, প্রধান গোরোটিন অবশ্যই একটি ব্লকিং অপারেশন কল করতে হবে বা runtime.Goschedএকটি উপযুক্ত জায়গায় কল করতে হবে (আপনার প্রোগ্রামের প্রধান লুপে, যদি এটি থাকে)
মিস্টারবি

8

উপরের সমস্তগুলি স্প্লাইস করা অবস্থায় কাজ করে বলে মনে হয়, তবে গোবাইক্সেম্পলারের সিগন্যাল পৃষ্ঠায় সিগন্যাল ক্যাপচারের একটি সত্যই পরিষ্কার এবং সম্পূর্ণ উদাহরণ রয়েছে। এই তালিকায় যুক্ত হওয়া মূল্যবান।


0

মৃত্যু হ'ল একটি সহজ লাইব্রেরি যা শাটডাউন সিগন্যালের জন্য অপেক্ষা করতে চ্যানেল এবং একটি ওয়েট গ্রুপ ব্যবহার করে। একবার সিগন্যাল পাওয়ার পরে এটি আপনার সমস্ত স্ট্রাক্টের একটি ঘনিষ্ঠ পদ্ধতিতে কল করবে যা আপনি পরিষ্কার করতে চান।


3
কোডের অনেক লাইন এবং একটি বহিরাগত লাইব্রেরি নির্ভর করতে কোড চার লাইনে কী করা যায়? (স্বীকৃত উত্তর অনুসারে)
জ্যাকব 6

যদি এটিগুলির স্ট্যান্ডার্ড ক্লোজড ইন্টারফেস থাকে তবে এটি আপনাকে সমান্তরাল এবং স্বয়ংক্রিয়ভাবে বন্ধ স্ট্রাক্টগুলির মধ্যে সেগুলির সমস্তটি পরিষ্কার করার অনুমতি দেয়।
বেন আল্ড্রিচ 18:25

0

আপনার কাছে অন্যরকম গোরোটিন থাকতে পারে যা সিস্কাল সনাক্ত করে IG সিগিন্ট এবং সিসকল । আপনি চ্যানেলটি ব্যবহার করে সেই জোরোটিনে একটি হুক প্রেরণ করতে পারেন এবং এটি কোনও ফাংশন স্লাইসে সংরক্ষণ করতে পারেন। চ্যানেলটিতে যখন শাটডাউন সিগন্যাল সনাক্ত করা যায়, আপনি স্লাইসে সেই ফাংশনগুলি সম্পাদন করতে পারেন। এটি রিসোর্সগুলি পরিষ্কার করতে, গোরোটাইনগুলি চালানোর জন্য অপেক্ষা করতে, ডেটা অবিরত রাখতে বা আংশিক রান মোটের জন্য মুদ্রণ করতে ব্যবহৃত হতে পারে।

আমি শাটডাউনে হুক যুক্ত করতে এবং চালানোর জন্য একটি ছোট এবং সাধারণ ইউটিলিটি লিখেছি। আশা করি এটি সহায়ক হতে পারে।

https://github.com/ankit-arora/go-utils/blob/master/go-shutdown-hook/shutdown-hook.go

আপনি একটি 'মুলতুবি' ফ্যাশনে এটি করতে পারেন।

নিখুঁতভাবে একটি সার্ভার বন্ধ করার জন্য উদাহরণ:

srv := &http.Server{}

go_shutdown_hook.ADD(func() {
    log.Println("shutting down server")
    srv.Shutdown(nil)
    log.Println("shutting down server-done")
})

l, err := net.Listen("tcp", ":3090")

log.Println(srv.Serve(l))

go_shutdown_hook.Wait()

0

এটি পরিষ্কার করার জন্য আপনার কিছু কাজ করার ক্ষেত্রে এটি অন্য সংস্করণ। কোড তাদের পদ্ধতিতে পরিষ্কার প্রক্রিয়া ছাড়বে will

package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"

)



func main() {

    _,done1:=doSomething1()
    _,done2:=doSomething2()

    //do main thread


    println("wait for finish")
    <-done1
    <-done2
    fmt.Print("clean up done, can exit safely")

}

func doSomething1() (error, chan bool) {
    //do something
    done:=make(chan bool)
    c := make(chan os.Signal, 2)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    go func() {
        <-c
        //cleanup of something1
        done<-true
    }()
    return nil,done
}


func doSomething2() (error, chan bool) {
    //do something
    done:=make(chan bool)
    c := make(chan os.Signal, 2)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    go func() {
        <-c
        //cleanup of something2
        done<-true
    }()
    return nil,done
}

আপনি যদি প্রধান ফাংশনটি পরিষ্কার করতে চান তবে আপনাকে ফান () ব্যবহার করে মূল থ্রেডে সিগন্যাল ক্যাপচার করতে হবে।


-2

কেবল রেকর্ডের জন্য যদি কারও যদি উইন্ডোজে সিগন্যালগুলি হ্যান্ডেল করার কোনও উপায় প্রয়োজন হয়। আমার কাছে প্রগ থেকে হ্যান্ডেল করার প্রয়োজন ছিল একটি ওপেন / এক্সিকিউটের মাধ্যমে প্রগ বি কল করা কিন্তু প্রগ বি কখনই প্রফুল্লভাবে শেষ করতে সক্ষম হয়নি কারণ প্রাক্তন মাধ্যমে সংকেত প্রেরণ করে। cmd.Process.Signal (syscall.SIGTERM) বা অন্যান্য সংকেতগুলি উইন্ডোজে সমর্থিত নয়। আমি যেভাবে পরিচালনা করেছি তা হ'ল প্রাক্তন সংকেত হিসাবে একটি টেম্প ফাইল তৈরি করা। প্রগ এ এবং প্রগ বি এর মাধ্যমে সিগন্যাল.টার্মটি পরীক্ষা করতে হবে যে ফাইলটি অন্তর বেসে রয়েছে কিনা, ফাইল উপস্থিত থাকলে এটি প্রোগ্রামটি থেকে প্রস্থান করে এবং প্রয়োজনে একটি ক্লিনআপ পরিচালনা করবে, আমি নিশ্চিত অন্যান্য উপায় আছে তবে এটি কাজটি করেছে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.