2016-05-06 40 views
9

Próbuję rozwidlić procesy za pomocą mojego demona i próbuję go odrzucić w przypadku awarii mojego demona. Regularne os/exec jest na wysokim poziomie, dlatego poszedłem na syscall.ForkExec i produkowane następujący kod:Proces wykonywania Golanga i jego wyrzucenie

package main 

import (
    "fmt" 
    "os" 
    "os/exec" 
    "syscall" 
    "time" 
) 

func main() { 

    cmd := "myproc" 
    binary, lookErr := exec.LookPath(cmd) 
    if lookErr != nil { 
     panic(lookErr) 
    } 
    fmt.Println(binary) 

    os.Remove("/tmp/stdin") 
    os.Remove("/tmp/stdout") 
    os.Remove("/tmp/stderr") 

    fstdin, err1 := os.Create("/tmp/stdin") 
    fstdout, err2 := os.Create("/tmp/stdout") 
    fstderr, err3 := os.Create("/tmp/stderr") 
    if err1 != nil || err2 != nil || err3 != nil { 
     fmt.Println(err1, err2, err3) 
     panic("WOW") 
    } 

    argv := []string{"hi"} 
    procAttr := syscall.ProcAttr{ 
     Dir: "/tmp", 
     Files: []uintptr{fstdin.Fd(), fstdout.Fd(), fstderr.Fd()}, 
     Env: []string{"VAR1=ABC123"}, 
     Sys: &syscall.SysProcAttr{ 
      Foreground: false, 
     }, 
    } 

    pid, err := syscall.ForkExec(binary, argv, &procAttr) 
    fmt.Println("Spawned proc", pid, err) 

    time.Sleep(time.Second * 100) 
} 

Mam również wykonany prosty wniosek, że śpi i drukuje Hello World i umieścić go na ścieżce.

#include <stdio.h> 

int main(){ 
    while(1){ 
     printf("hello world"); 
     fflush(stdout); 
     usleep(300000); 
     } 
} 

Działa, jednak proces nie jest wysyłany do tła, jak się spodziewałem, mój proces go nadal jest właścicielem dziecka. SysProcAttr ma następujące wartości w systemie Linux:

type SysProcAttr struct { 
    Chroot  string   // Chroot. 
    Credential *Credential // Credential. 
    Ptrace  bool   // Enable tracing. 
    Setsid  bool   // Create session. 
    Setpgid  bool   // Set process group ID to Pgid, or, if Pgid == 0, to new pid. 
    Setctty  bool   // Set controlling terminal to fd Ctty (only meaningful if Setsid is set) 
    Noctty  bool   // Detach fd 0 from controlling terminal 
    Ctty  int   // Controlling TTY fd 
    Foreground bool   // Place child's process group in foreground. (Implies Setpgid. Uses Ctty as fd of controlling TTY) 
    Pgid  int   // Child's process group ID if Setpgid. 
    Pdeathsig Signal   // Signal that the process will get when its parent dies (Linux only) 
    Cloneflags uintptr  // Flags for clone calls (Linux only) 
    UidMappings []SysProcIDMap // User ID mappings for user namespaces. 
    GidMappings []SysProcIDMap // Group ID mappings for user namespaces. 
    // GidMappingsEnableSetgroups enabling setgroups syscall. 
    // If false, then setgroups syscall will be disabled for the child process. 
    // This parameter is no-op if GidMappings == nil. Otherwise for unprivileged 
    // users this should be set to false for mappings work. 
    GidMappingsEnableSetgroups bool 
} 

Próbowałem również ale on spowodował błąd:

Sys: &syscall.SysProcAttr{ 
    Setsid:  true, 
    Setctty: true, 
    Foreground: false, 
}, 

Spawned proc 0 inappropriate ioctl for device 

również:

Sys: &syscall.SysProcAttr{ 
    Setsid:  true, 
    Setctty: true, 
    Foreground: false, 
    Noctty:  true, 
    Setpgid: true, 
}, 

Spawned proc 0 operation not permitted (with root privilleges) 

Co robię/zakładając, że źle? Uwaga: Mimo że mówię, że os/exec jest na wysokim poziomie, próbowałem również następujących rzeczy, ale przyniosły one takie same wyniki.

cs := exec.Command(binary) 
cs.SysProcAttr = &syscall.SysProcAttr{ 
    Setctty: true, 
} 
err := cs.Run() 
fmt.Println(err) 
+0

Dlaczego sądzisz 'exec.Cmd' jest zbyt wysoki poziom? Masz również dostęp do SysProcAttr. Co masz na myśli, mówiąc, że Twój proces wciąż jest właścicielem dziecka? Czy jest w tej samej grupie procesów? Czy nie zostanie ponownie przypisany do PID 1, jeśli rodzic wyjdzie? – JimB

Odpowiedz

2

Proces rozwidlony z Start() będzie kontynuowane nawet po śmierci jego rodzica.

func forker() { 
    cmd := exec.Command("sleep", "3") 
    cmd.Start() 
    time.Sleep(2 * time.Second) 
    os.Exit(1) 
} 

Tutaj proces sleep będą żyć szczęśliwie na 3 sekundy, nawet jeśli proces rodzic żyje tylko przez 2 sekundy:

$ forker &; while true; do ps -f; sleep 1; done 

    UID PID PPID C STIME TTY   TIME CMD 
    501 71423 69892 0 3:01PM ttys003 0:00.07 forker 
    501 71433 71432 0 3:01PM ttys003 0:00.00 sleep 3 

    UID PID PPID C STIME TTY   TIME CMD 
    501 71423 69892 0 3:01PM ttys003 0:00.07 forker 
    501 71433 71432 0 3:01PM ttys003 0:00.00 sleep 3 

    UID PID PPID C STIME TTY   TIME CMD 
    501 71433  1 0 3:01PM ttys003 0:00.00 sleep 3 

zauważyć jak identyfikator procesu nadrzędnego (PPID) procesu sleep stał 1, gdy proces nadrzędny zakończył się 71432. Oznacza to, że proces sleep został osierocony.

+0

Celem jest uczynienie 71432 równym 1 w powyższym przykładzie (natychmiast). – sheerun

+0

Pytanie jest sformułowane: "[...] w przypadku mojego demona ulega awarii [sic]". Nie jest konieczne natychmiastowe osierocenie procesu. Z 'Start()' będzie osierocone tylko wtedy, gdy i rodzic umrze. – augustzf

2

Po otrzymaniu odpowiedzi na numer Start() method należy podać to, czego szukasz. Mój skrypt dołączyć prosty plik tekstowy po moim przykładowym programie zakończonego Go nadal uruchomić:

package main 

import (
    "os/exec" 
) 

func main() { 
    cmd := exec.Command("./appender.sh") 
    cmd.Start() 
} 
+1

Należy zauważyć, że jeśli polecenie używa stdin/stdout od rodzica, jeśli rodzic zakończy, proces może stać się zombie. – AJPennster

2

Wydaje się, że dobrą strategią dla natychmiastowego odrzucenia jest proces odradzania się (os.Argv[0]), następnie pojawienie się procesu docelowego (z Setsid: true), a następnie wyjście z pierwszego procesu odrodzenia. W ten sposób proces gradchild natychmiast dostaje ppid 1. Opcjonalnie pierwszy proces odradzania może komunikować pid z granchild przez stdout do rodzica przed jego wyjściem.

1

Demonstracja nie jest zgodna z harmonogramem go go goroutine, więc szczegóły stają się dość gnarly. godaemon ma małą implementację z dobrą dyskusją i linkami do bardziej dobrej dyskusji, a także jest bardziej popularny i bardziej tradycyjnie wdrażany go-daemon.

Błędy, które otrzymujesz prawie na pewno pochodzą z użycia Setctty.

Sys: &syscall.SysProcAttr{ Foreground: false, Setsid: true, },

powinno Ci semantykę jak os/exec „s Start, gdzie proces dziecko nie jest natychmiast reparented, ale bezpiecznie zostanie podniesiona jeśli/kiedy umiera rodzic.

operation not permitted widzisz, jak sądzę, o rurach w /tmp/, z systemem operacyjnym strace: openat(AT_FDCWD, "/tmp/stdin", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3 epoll_create1(EPOLL_CLOEXEC) = 4 epoll_ctl(4, EPOLL_CTL_ADD, 3, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=471047936, u64=140179613654784}}) = -1 EPERM (Operation not permitted) epoll_ctl(4, EPOLL_CTL_DEL, 3, 0xc420039c24) = -1 EPERM (Operation not permitted) openat(AT_FDCWD, "/tmp/stdout", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 5 epoll_ctl(4, EPOLL_CTL_ADD, 5, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=471047936, u64=140179613654784}}) = -1 EPERM (Operation not permitted) epoll_ctl(4, EPOLL_CTL_DEL, 5, 0xc420039c24) = -1 EPERM (Operation not permitted) openat(AT_FDCWD, "/tmp/stderr", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 6 epoll_ctl(4, EPOLL_CTL_ADD, 6, {EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET, {u32=471047936, u64=140179613654784}}) = -1 EPERM (Operation not permitted) epoll_ctl(4, EPOLL_CTL_DEL, 6, 0xc420039c24) = -1 EPERM (Operation not permitted)