[{"data":1,"prerenderedAt":965},["ShallowReactive",2],{"post-\u002Fposts\u002Fgolang-exec-kill":3,"surround-\u002Fposts\u002Fgolang-exec-kill":956},{"id":4,"title":5,"body":6,"cover":942,"date":943,"description":944,"draft":945,"extension":946,"meta":947,"minutes":66,"navigation":948,"path":949,"seo":950,"stem":951,"tags":952,"updated":942,"__hash__":955},"posts\u002Fposts\u002Fgolang-exec-kill.md","Golang exec.Cmd在Kill后不能关闭进程",{"type":7,"value":8,"toc":940},"minimark",[9,13,463,466,469,933,936],[10,11,12],"p",{},"出现的场景，在golang项目中需要管理shell脚本的执行，主要实现关闭进程的代码如下：",[14,15,20],"pre",{"className":16,"code":17,"language":18,"meta":19,"style":19},"language-go shiki shiki-themes tokyo-night github-light","func (s *Streamer) Kill() error {\n    s.mu.Lock()\n    defer s.mu.Unlock()\n    \u002F\u002F 首先关闭输出流\n    if closer, ok := s.Cmd.Stdout.(io.Closer); ok {\n        if err := closer.Close(); err != nil {\n            log.Printf(\"Failed to close stdout: %v\", err)\n        }\n    }\n    if closer, ok := s.Cmd.Stderr.(io.Closer); ok {\n        if err := closer.Close(); err != nil {\n            log.Printf(\"Failed to close stderr: %v\", err)\n        }\n    }\n    if s.Cmd.Process != nil {\n        \u002F\u002F Kill the process\n        if err := s.Cmd.Process.Kill(); err != nil {\n            return err\n        }\n    }\n    \u002F\u002F 等待进程关闭\n    return s.Cmd.Wait()\n}\n","go","",[21,22,23,64,86,106,113,165,198,232,238,244,286,313,339,344,349,371,377,413,422,427,432,438,457],"code",{"__ignoreMap":19},[24,25,28,32,36,40,44,48,51,55,58,61],"span",{"class":26,"line":27},"line",1,[24,29,31],{"class":30},"sBxw3","func",[24,33,35],{"class":34},"sjXpA"," (",[24,37,39],{"class":38},"sgjcI","s ",[24,41,43],{"class":42},"sNP9S","*",[24,45,47],{"class":46},"sirr2","Streamer",[24,49,50],{"class":34},")",[24,52,54],{"class":53},"sxkyg"," Kill",[24,56,57],{"class":34},"()",[24,59,60],{"class":30}," error",[24,62,63],{"class":34}," {\n",[24,65,67,71,75,78,80,83],{"class":26,"line":66},2,[24,68,70],{"class":69},"s4do5","    s",[24,72,74],{"class":73},"szu1u",".",[24,76,77],{"class":69},"mu",[24,79,74],{"class":73},[24,81,82],{"class":53},"Lock",[24,84,85],{"class":34},"()\n",[24,87,89,92,95,97,99,101,104],{"class":26,"line":88},3,[24,90,91],{"class":30},"    defer",[24,93,94],{"class":69}," s",[24,96,74],{"class":73},[24,98,77],{"class":69},[24,100,74],{"class":73},[24,102,103],{"class":53},"Unlock",[24,105,85],{"class":34},[24,107,109],{"class":26,"line":108},4,[24,110,112],{"class":111},"sSWvG","    \u002F\u002F 首先关闭输出流\n",[24,114,116,119,122,125,128,131,133,135,138,140,143,145,148,151,153,156,158,161,163],{"class":26,"line":115},5,[24,117,118],{"class":30},"    if",[24,120,121],{"class":69}," closer",[24,123,124],{"class":73},",",[24,126,127],{"class":69}," ok",[24,129,130],{"class":42}," :=",[24,132,94],{"class":69},[24,134,74],{"class":73},[24,136,137],{"class":69},"Cmd",[24,139,74],{"class":73},[24,141,142],{"class":69},"Stdout",[24,144,74],{"class":73},[24,146,147],{"class":34},"(",[24,149,150],{"class":46},"io",[24,152,74],{"class":73},[24,154,155],{"class":46},"Closer",[24,157,50],{"class":34},[24,159,160],{"class":73},";",[24,162,127],{"class":69},[24,164,63],{"class":34},[24,166,168,171,174,176,178,180,183,185,187,189,192,196],{"class":26,"line":167},6,[24,169,170],{"class":30},"        if",[24,172,173],{"class":69}," err",[24,175,130],{"class":42},[24,177,121],{"class":69},[24,179,74],{"class":73},[24,181,182],{"class":53},"Close",[24,184,57],{"class":34},[24,186,160],{"class":73},[24,188,173],{"class":69},[24,190,191],{"class":30}," !=",[24,193,195],{"class":194},"sjYRd"," nil",[24,197,63],{"class":34},[24,199,201,204,206,209,211,215,219,223,225,227,229],{"class":26,"line":200},7,[24,202,203],{"class":69},"            log",[24,205,74],{"class":73},[24,207,208],{"class":53},"Printf",[24,210,147],{"class":34},[24,212,214],{"class":213},"sbW-k","\"",[24,216,218],{"class":217},"snP_1","Failed to close stdout: ",[24,220,222],{"class":221},"sAus6","%v",[24,224,214],{"class":213},[24,226,124],{"class":73},[24,228,173],{"class":69},[24,230,231],{"class":34},")\n",[24,233,235],{"class":26,"line":234},8,[24,236,237],{"class":34},"        }\n",[24,239,241],{"class":26,"line":240},9,[24,242,243],{"class":34},"    }\n",[24,245,247,249,251,253,255,257,259,261,263,265,268,270,272,274,276,278,280,282,284],{"class":26,"line":246},10,[24,248,118],{"class":30},[24,250,121],{"class":69},[24,252,124],{"class":73},[24,254,127],{"class":69},[24,256,130],{"class":42},[24,258,94],{"class":69},[24,260,74],{"class":73},[24,262,137],{"class":69},[24,264,74],{"class":73},[24,266,267],{"class":69},"Stderr",[24,269,74],{"class":73},[24,271,147],{"class":34},[24,273,150],{"class":46},[24,275,74],{"class":73},[24,277,155],{"class":46},[24,279,50],{"class":34},[24,281,160],{"class":73},[24,283,127],{"class":69},[24,285,63],{"class":34},[24,287,289,291,293,295,297,299,301,303,305,307,309,311],{"class":26,"line":288},11,[24,290,170],{"class":30},[24,292,173],{"class":69},[24,294,130],{"class":42},[24,296,121],{"class":69},[24,298,74],{"class":73},[24,300,182],{"class":53},[24,302,57],{"class":34},[24,304,160],{"class":73},[24,306,173],{"class":69},[24,308,191],{"class":30},[24,310,195],{"class":194},[24,312,63],{"class":34},[24,314,316,318,320,322,324,326,329,331,333,335,337],{"class":26,"line":315},12,[24,317,203],{"class":69},[24,319,74],{"class":73},[24,321,208],{"class":53},[24,323,147],{"class":34},[24,325,214],{"class":213},[24,327,328],{"class":217},"Failed to close stderr: ",[24,330,222],{"class":221},[24,332,214],{"class":213},[24,334,124],{"class":73},[24,336,173],{"class":69},[24,338,231],{"class":34},[24,340,342],{"class":26,"line":341},13,[24,343,237],{"class":34},[24,345,347],{"class":26,"line":346},14,[24,348,243],{"class":34},[24,350,352,354,356,358,360,362,365,367,369],{"class":26,"line":351},15,[24,353,118],{"class":30},[24,355,94],{"class":69},[24,357,74],{"class":73},[24,359,137],{"class":69},[24,361,74],{"class":73},[24,363,364],{"class":69},"Process",[24,366,191],{"class":30},[24,368,195],{"class":194},[24,370,63],{"class":34},[24,372,374],{"class":26,"line":373},16,[24,375,376],{"class":111},"        \u002F\u002F Kill the process\n",[24,378,380,382,384,386,388,390,392,394,396,398,401,403,405,407,409,411],{"class":26,"line":379},17,[24,381,170],{"class":30},[24,383,173],{"class":69},[24,385,130],{"class":42},[24,387,94],{"class":69},[24,389,74],{"class":73},[24,391,137],{"class":69},[24,393,74],{"class":73},[24,395,364],{"class":69},[24,397,74],{"class":73},[24,399,400],{"class":53},"Kill",[24,402,57],{"class":34},[24,404,160],{"class":73},[24,406,173],{"class":69},[24,408,191],{"class":30},[24,410,195],{"class":194},[24,412,63],{"class":34},[24,414,416,419],{"class":26,"line":415},18,[24,417,418],{"class":30},"            return",[24,420,421],{"class":69}," err\n",[24,423,425],{"class":26,"line":424},19,[24,426,237],{"class":34},[24,428,430],{"class":26,"line":429},20,[24,431,243],{"class":34},[24,433,435],{"class":26,"line":434},21,[24,436,437],{"class":111},"    \u002F\u002F 等待进程关闭\n",[24,439,441,444,446,448,450,452,455],{"class":26,"line":440},22,[24,442,443],{"class":30},"    return",[24,445,94],{"class":69},[24,447,74],{"class":73},[24,449,137],{"class":69},[24,451,74],{"class":73},[24,453,454],{"class":53},"Wait",[24,456,85],{"class":34},[24,458,460],{"class":26,"line":459},23,[24,461,462],{"class":34},"}\n",[10,464,465],{},"在前端调用接口触发方法Kill时发现时不时出现接口请求超时，一开始以为是网络的问题，后面经过过排查，发现Cmd.Wait()一直没有结束，导致接口请求超时。在查找资料的过程中发现，Cmd.Wait()会一直等待进程结束，如果进程一直没有结束，那么Cmd.Wait()也会一直等待，然而实际情况是shell已经执行完毕，经过排查是产生了僵尸子进程。",[10,467,468],{},"为了解决这个问题，对代码做了一些修改，首先启动一个 goroutine 来处理超时，然后Cmd.Wait()不能无限制等待，可以通过time.After设置一个超时时间，如果超过这个时间还没有结束，将通过发送 SIGTERM 信号，具体实现如下：",[14,470,472],{"className":16,"code":471,"language":18,"meta":19,"style":19},"func (s *Streamer) Stop() error {\n    s.mu.Lock()\n    defer s.mu.Unlock()\n    \u002F\u002F ...\n    \u002F\u002F 设置超时时间\n    timeout := time.Duration(5 * time.Second)\n    done := make(chan error, 1)\n    \u002F\u002F 启动一个 goroutine 来处理超时\n    go func() {\n        err := s.Cmd.Wait()\n        done \u003C- err\n    }()\n    \u002F\u002F 等待命令完成或超时\n    select {\n    case err := \u003C-done:\n        if err != nil {\n            fmt.Println(\"命令执行失败:\", err)\n        } else {\n            fmt.Println(\"命令执行成功\")\n        }\n    case \u003C-time.After(timeout):\n        \u002F\u002F 发送 SIGTERM 信号\n        if err := exec.Command(\"kill\", \"-SIGTERM\", fmt.Sprintf(\"-%d\", s.Cmd.Process.Pid)).Run(); err != nil {\n            fmt.Println(\"发送 SIGTERM 信号失败:\", err)\n        }\n        fmt.Println(\"命令超时，已终止\")\n    }\n    return nil\n}\n",[21,473,474,497,511,527,532,537,569,593,598,610,629,639,644,649,656,673,685,710,720,739,743,766,771,865,889,894,915,920,928],{"__ignoreMap":19},[24,475,476,478,480,482,484,486,488,491,493,495],{"class":26,"line":27},[24,477,31],{"class":30},[24,479,35],{"class":34},[24,481,39],{"class":38},[24,483,43],{"class":42},[24,485,47],{"class":46},[24,487,50],{"class":34},[24,489,490],{"class":53}," Stop",[24,492,57],{"class":34},[24,494,60],{"class":30},[24,496,63],{"class":34},[24,498,499,501,503,505,507,509],{"class":26,"line":66},[24,500,70],{"class":69},[24,502,74],{"class":73},[24,504,77],{"class":69},[24,506,74],{"class":73},[24,508,82],{"class":53},[24,510,85],{"class":34},[24,512,513,515,517,519,521,523,525],{"class":26,"line":88},[24,514,91],{"class":30},[24,516,94],{"class":69},[24,518,74],{"class":73},[24,520,77],{"class":69},[24,522,74],{"class":73},[24,524,103],{"class":53},[24,526,85],{"class":34},[24,528,529],{"class":26,"line":108},[24,530,531],{"class":111},"    \u002F\u002F ...\n",[24,533,534],{"class":26,"line":115},[24,535,536],{"class":111},"    \u002F\u002F 设置超时时间\n",[24,538,539,542,544,547,549,552,554,557,560,562,564,567],{"class":26,"line":167},[24,540,541],{"class":69},"    timeout",[24,543,130],{"class":42},[24,545,546],{"class":69}," time",[24,548,74],{"class":73},[24,550,551],{"class":53},"Duration",[24,553,147],{"class":34},[24,555,556],{"class":194},"5",[24,558,559],{"class":42}," *",[24,561,546],{"class":69},[24,563,74],{"class":73},[24,565,566],{"class":69},"Second",[24,568,231],{"class":34},[24,570,571,574,576,579,581,584,586,588,591],{"class":26,"line":200},[24,572,573],{"class":69},"    done",[24,575,130],{"class":42},[24,577,578],{"class":53}," make",[24,580,147],{"class":34},[24,582,583],{"class":30},"chan",[24,585,60],{"class":30},[24,587,124],{"class":73},[24,589,590],{"class":194}," 1",[24,592,231],{"class":34},[24,594,595],{"class":26,"line":234},[24,596,597],{"class":111},"    \u002F\u002F 启动一个 goroutine 来处理超时\n",[24,599,600,603,606,608],{"class":26,"line":240},[24,601,602],{"class":30},"    go",[24,604,605],{"class":30}," func",[24,607,57],{"class":34},[24,609,63],{"class":34},[24,611,612,615,617,619,621,623,625,627],{"class":26,"line":246},[24,613,614],{"class":69},"        err",[24,616,130],{"class":42},[24,618,94],{"class":69},[24,620,74],{"class":73},[24,622,137],{"class":69},[24,624,74],{"class":73},[24,626,454],{"class":53},[24,628,85],{"class":34},[24,630,631,634,637],{"class":26,"line":288},[24,632,633],{"class":69},"        done",[24,635,636],{"class":42}," \u003C-",[24,638,421],{"class":69},[24,640,641],{"class":26,"line":315},[24,642,643],{"class":34},"    }()\n",[24,645,646],{"class":26,"line":341},[24,647,648],{"class":111},"    \u002F\u002F 等待命令完成或超时\n",[24,650,651,654],{"class":26,"line":346},[24,652,653],{"class":30},"    select",[24,655,63],{"class":34},[24,657,658,661,663,665,667,670],{"class":26,"line":351},[24,659,660],{"class":30},"    case",[24,662,173],{"class":69},[24,664,130],{"class":42},[24,666,636],{"class":42},[24,668,669],{"class":69},"done",[24,671,672],{"class":73},":\n",[24,674,675,677,679,681,683],{"class":26,"line":373},[24,676,170],{"class":30},[24,678,173],{"class":69},[24,680,191],{"class":30},[24,682,195],{"class":194},[24,684,63],{"class":34},[24,686,687,690,692,695,697,699,702,704,706,708],{"class":26,"line":379},[24,688,689],{"class":69},"            fmt",[24,691,74],{"class":73},[24,693,694],{"class":53},"Println",[24,696,147],{"class":34},[24,698,214],{"class":213},[24,700,701],{"class":217},"命令执行失败:",[24,703,214],{"class":213},[24,705,124],{"class":73},[24,707,173],{"class":69},[24,709,231],{"class":34},[24,711,712,715,718],{"class":26,"line":415},[24,713,714],{"class":34},"        }",[24,716,717],{"class":30}," else",[24,719,63],{"class":34},[24,721,722,724,726,728,730,732,735,737],{"class":26,"line":424},[24,723,689],{"class":69},[24,725,74],{"class":73},[24,727,694],{"class":53},[24,729,147],{"class":34},[24,731,214],{"class":213},[24,733,734],{"class":217},"命令执行成功",[24,736,214],{"class":213},[24,738,231],{"class":34},[24,740,741],{"class":26,"line":429},[24,742,237],{"class":34},[24,744,745,747,749,752,754,757,759,762,764],{"class":26,"line":434},[24,746,660],{"class":30},[24,748,636],{"class":42},[24,750,751],{"class":69},"time",[24,753,74],{"class":73},[24,755,756],{"class":53},"After",[24,758,147],{"class":34},[24,760,761],{"class":69},"timeout",[24,763,50],{"class":34},[24,765,672],{"class":73},[24,767,768],{"class":26,"line":440},[24,769,770],{"class":111},"        \u002F\u002F 发送 SIGTERM 信号\n",[24,772,773,775,777,779,782,784,787,789,791,794,796,798,801,804,806,808,811,813,816,818,820,823,826,828,830,832,834,836,838,840,842,845,848,850,853,855,857,859,861,863],{"class":26,"line":459},[24,774,170],{"class":30},[24,776,173],{"class":69},[24,778,130],{"class":42},[24,780,781],{"class":69}," exec",[24,783,74],{"class":73},[24,785,786],{"class":53},"Command",[24,788,147],{"class":34},[24,790,214],{"class":213},[24,792,793],{"class":217},"kill",[24,795,214],{"class":213},[24,797,124],{"class":73},[24,799,800],{"class":213}," \"",[24,802,803],{"class":217},"-SIGTERM",[24,805,214],{"class":213},[24,807,124],{"class":73},[24,809,810],{"class":69}," fmt",[24,812,74],{"class":73},[24,814,815],{"class":53},"Sprintf",[24,817,147],{"class":34},[24,819,214],{"class":213},[24,821,822],{"class":217},"-",[24,824,825],{"class":221},"%d",[24,827,214],{"class":213},[24,829,124],{"class":73},[24,831,94],{"class":69},[24,833,74],{"class":73},[24,835,137],{"class":69},[24,837,74],{"class":73},[24,839,364],{"class":69},[24,841,74],{"class":73},[24,843,844],{"class":69},"Pid",[24,846,847],{"class":34},"))",[24,849,74],{"class":73},[24,851,852],{"class":53},"Run",[24,854,57],{"class":34},[24,856,160],{"class":73},[24,858,173],{"class":69},[24,860,191],{"class":30},[24,862,195],{"class":194},[24,864,63],{"class":34},[24,866,868,870,872,874,876,878,881,883,885,887],{"class":26,"line":867},24,[24,869,689],{"class":69},[24,871,74],{"class":73},[24,873,694],{"class":53},[24,875,147],{"class":34},[24,877,214],{"class":213},[24,879,880],{"class":217},"发送 SIGTERM 信号失败:",[24,882,214],{"class":213},[24,884,124],{"class":73},[24,886,173],{"class":69},[24,888,231],{"class":34},[24,890,892],{"class":26,"line":891},25,[24,893,237],{"class":34},[24,895,897,900,902,904,906,908,911,913],{"class":26,"line":896},26,[24,898,899],{"class":69},"        fmt",[24,901,74],{"class":73},[24,903,694],{"class":53},[24,905,147],{"class":34},[24,907,214],{"class":213},[24,909,910],{"class":217},"命令超时，已终止",[24,912,214],{"class":213},[24,914,231],{"class":34},[24,916,918],{"class":26,"line":917},27,[24,919,243],{"class":34},[24,921,923,925],{"class":26,"line":922},28,[24,924,443],{"class":30},[24,926,927],{"class":194}," nil\n",[24,929,931],{"class":26,"line":930},29,[24,932,462],{"class":34},[10,934,935],{},"以上代码可能并不完善，仅提供一个思路，具体实现还需要根据实际情况进行调整。",[937,938,939],"style",{},"html pre.shiki code .sBxw3, html code.shiki .sBxw3{--shiki-default:#BB9AF7;--shiki-light:#D73A49}html pre.shiki code .sjXpA, html code.shiki .sjXpA{--shiki-default:#9ABDF5;--shiki-light:#24292E}html pre.shiki code .sgjcI, html code.shiki .sgjcI{--shiki-default:#E0AF68;--shiki-light:#E36209}html pre.shiki code .sNP9S, html code.shiki .sNP9S{--shiki-default:#89DDFF;--shiki-light:#D73A49}html pre.shiki code .sirr2, html code.shiki .sirr2{--shiki-default:#C0CAF5;--shiki-light:#6F42C1}html pre.shiki code .sxkyg, html code.shiki .sxkyg{--shiki-default:#7AA2F7;--shiki-light:#6F42C1}html pre.shiki code .s4do5, html code.shiki .s4do5{--shiki-default:#C0CAF5;--shiki-light:#24292E}html pre.shiki code .szu1u, html code.shiki .szu1u{--shiki-default:#89DDFF;--shiki-light:#24292E}html pre.shiki code .sSWvG, html code.shiki .sSWvG{--shiki-default:#51597D;--shiki-default-font-style:italic;--shiki-light:#6A737D;--shiki-light-font-style:inherit}html pre.shiki code .sjYRd, html code.shiki .sjYRd{--shiki-default:#FF9E64;--shiki-light:#005CC5}html pre.shiki code .sbW-k, html code.shiki .sbW-k{--shiki-default:#89DDFF;--shiki-light:#032F62}html pre.shiki code .snP_1, html code.shiki .snP_1{--shiki-default:#9ECE6A;--shiki-light:#032F62}html pre.shiki code .sAus6, html code.shiki .sAus6{--shiki-default:#C0CAF5;--shiki-light:#005CC5}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}",{"title":19,"searchDepth":88,"depth":88,"links":941},[],null,"2024-03-17","当前基于Golang 1.22.2 版本实现",false,"md",{},true,"\u002Fposts\u002Fgolang-exec-kill",{"title":5,"description":944},"posts\u002Fgolang-exec-kill",[953,954,793],"golang","exec","3zHQxrBR_cVYbQT5fCZSM-lO7-dbZ622hKm23ncnYJA",[957,961],{"title":958,"path":959,"stem":960,"children":-1},"Golang在linux跨区移动文件失败","\u002Fposts\u002Fgolang-linux-file","posts\u002Fgolang-linux-file",{"title":962,"path":963,"stem":964,"children":-1},"Golang基于Redis生成唯一序列","\u002Fposts\u002Fgolang-redis-seq","posts\u002Fgolang-redis-seq",1789524989342]