preface
We know that Linux has three special processes: orphan processes, zombie processes, and daemons.
-
An orphan process is a process that continues to run after its parent has completed execution or been terminated. These orphan processes are adopted by the init process (process number 1), which collects state for them.
-
Zombie process A child process that exits before its parent has called wait() or waitpid(). This child process is the zombie process. Any child process (with the exception of init) does not disappear immediately after exit(), but leaves behind a data structure called a Zombie, waiting to be processed by the parent process.
-
Daemon THE parent of a daemon is init, and because its real parent forks out the child before the child exits, it is an orphaned process inherited by init. Daemons are non-interactive programs with no control terminal, so any output, whether to stdout, the standard output device, or stderr, the standard error device, requires special handling.
From the process description above, in Linux zombie process is the parent process has not had time to wait(i.e., tell the kernel, this is my son, I will collect his corpse. The child process completes by telling the kernel that it will not collect the dead body for it. Going back to the orphan process issue, you can see that both orphaned processes, zombie processes, and daemons end up receiving the init process, so there is no such thing as an orphan process. A daemon is also an orphan process, in which the child is adopted by the init process after the parent exits and takes care of output devices. This adoptive process is called orphan, and the following article describes how to create a daemon-like child process by implementing a child process orphan and controlling its output device.
Process orphan implementation
- The demo code uses the goGF scaffolding, which I have personally applied to the actual development, and many commonly used tools are well encapsulated.
Official documentation: goframe.org/ source address: github.com/gogf/gf
Example 1
- The demo processEntrust an orphan toHow does the GO language control its output device
- through
go run t1.go
Run the code, first usinggproc
Will start a main process, through the main processfork
The current process starts the child process. The main process does not wait for the child process to exit, and the code below shows that I did not call itwait
The child process. Child process passesgproc.IsChild
Discovers that it is a child process and enters the child process code block, in which the main control output device can be separated from the terminal, called hereos.Pipe
Creates a file pipeline that creates two pairs of reads and writes, and willw
Write pipe assignment toos.Stdout
,w1
Assign a value toos.Stderr
, thus taking over the results of the two standard outputs. The next operation is readr
andr1
For the purposes of this demonstration, a new pipe file of type STdout is assigned toos.Stdout
, let the terminal receiveprint
The results;
t1.go
package main
import (
"fmt"
"github.com/gogf/gf/os/glog"
"github.com/gogf/gf/os/gproc"
"io"
"os"
"syscall"
"time"
)
func main (a) {
if gproc.IsChild() {
// Create a file pipeline for stdout
r, w, _ := os.Pipe()
// Create a file pipeline for stderr
r1, w1, _ := os.Pipe()
defer func(a) {
_ = w.Close()
_ = w1.Close()
}()
os.Stdout = w
os.Stderr = w1
glog.Printf("%d: Hi, I am child, waiting 30 seconds to die", gproc.Pid())
time.Sleep(time.Second)
glog.Printf("%d: 1", gproc.Pid())
time.Sleep(time.Second)
glog.Printf("%d: 2", gproc.Pid())
time.Sleep(time.Second)
glog.Printf("%d: 3", gproc.Pid())
time.Sleep(time.Second * 30)
glog.Printf("end")
_ = w.Close()
_ = w1.Close()
var (
outBuf = make([]byte.1)
errBuf = make([]byte.1)
output string
)
// Read stdout information
for {
_, err := r.Read(outBuf)
if err == io.EOF {
break
}
iferr ! =nil {
panic(err)
}
if string(outBuf) ! ="" {
output += string(outBuf)
}
}
// Read stderr's message
for {
_, err := r1.Read(errBuf)
if err == io.EOF {
break
}
iferr ! =nil {
panic(err)
}
if string(errBuf) ! ="" {
output += string(errBuf)
}
}
// Reassign to create a stdout pipe so that data can be printed to the terminal via stdout
os.Stdout = os.NewFile(uintptr(syscall.Stdout), "/dev/stdout")
fmt.Println(output)
} else {
m := gproc.NewManager()
p := m.NewProcess(os.Args[0], os.Args, os.Environ())
p.Start()
//p.Wait()
glog.Printf("Parent PID: %d", gproc.Pid())
}
}
Copy the code
- You can see that the pid is 806
# go run t1.go
2021-01-10 21:51:24.545 Parent PID: 806
Copy the code
- The parent starts and exits, and init 1 takes over the child.
# ps l|grep t1
0 0 813 1 20 0 743752 7288 - Sl pts/1 0:00 /tmp/go-build311777737/b001/exe/t1
0 0 820 389 20 0 112748 2300 - S+ pts/3 0:00 grep --color=auto t1
Copy the code
- After 30 seconds, you see output on the terminal
# 2021-01-10 21:51:24.548 813: Hi, I am child, waiting 30 seconds to die2021-01-10 21:51:25.548 813:1 2021-01-10 21:51:26.549 813:2 2021-01-10 21:51:27.549 813:3 2021-01-10 21:51:57.549 endCopy the code
Example 2 (subreaper)
- There’s another thing I want to introduce here
subreaper
, a system call from the linux3.4 kernel,subreaper
The name means reaper of a subprocess, which means throughPR_SET_CHILD_SUBREAPER
This system call sets a process to the ancestor processinit
Processes can also adopt orphan processes (arg2 should be greater than 1, as shown in the code below). The way a child is adopted is by its nearest ancestor.
Reference: man7.org/linux/man-p…
- The following
ancestor_process.go
, set yourself up as the ancestor process, and startsub_process_1.go
The first child process, and 60 seconds wait and call upwait
Functions;sub_process_1
Continue to launchsub_process_2
butsub_process_1
Don’t callwait
The function exits and letssub_process_2
He went into an orphan state. In the usual case where the ancestor process is not setsub_process_2
Will beinit
Adoption, but we have an ancestry process in place, sosub_process_2
Should have been the nearestancestor_process
Adoption.
ancestor_process.go
package main
import (
"github.com/gogf/gf/os/glog"
"github.com/gogf/gf/os/gproc"
"golang.org/x/sys/unix"
"os"
"time"
)
//
func SetSubreaper(i int) error {
return unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, uintptr(i), 0.0.0)}func main (a) {
// If the parameter is greater than 1, it can be set as the ancestor process
if err := SetSubreaper(1) ; err != nil {
panic(err)
}
glog.Printf("subreaper started....")
m := gproc.NewManager()
// Call the first child process
p := m.NewProcess("/usr/local/go/bin/go"And []string{"run"."sub_process_1.go"}, os.Environ())
_ , err := p.Start()
iferr ! =nil {
panic(err)
}
glog.Printf("ancestor: %d", gproc.Pid())
time.Sleep(60 * time.Second)
iferr := p.Wait(); err ! =nil {
panic(err)
}
}
Copy the code
sub_process_1.go
package main
import (
"github.com/gogf/gf/os/glog"
"github.com/gogf/gf/os/gproc"
"os"
)
func main (a) {
glog.Printf("sub process 1....")
m := gproc.NewManager()
p := m.NewProcess("/usr/local/go/bin/go"And []string{"run"."sub_process_2.go"}, os.Environ())
_ , err := p.Start()
iferr ! =nil {
panic(err)
}
glog.Printf("sub process 1: %d", gproc.Pid())
}
Copy the code
sub_process_2.go
package main
import (
"github.com/gogf/gf/os/glog"
"github.com/gogf/gf/os/gproc"
"os"
"time"
)
func main(a) {
glog.Printf("sub process 2.... started")
glog.Printf("sub process 2: %d", gproc.Pid())
glog.Printf("sub process 2: ppid %d", os.Getppid())
time.Sleep(30 * time.Second)
glog.Printf("sub process 2.... finished")}Copy the code
- The output, this is weird
ancestor
The PID turns out to be andsub_porecess_2
The ppID of is inconsistent, actually because we are usinggo run
Start, so another process is forked to execute.
# go run ancestor_process.goThe 2021-01-10 23:27:44. 109 subreaper started... 2021-01-10 23:27:44.114 sub Process 1.... 2021-01-10 23:27:44.625 Sub Process 1: 1608 2021-01-10 23:27:45.120 sub Process 2.... 2021-01-10 23:27:45.120 sub Process 2: 1648 2021-01-10 23:27:45.120 sub Process 2: 1648 2021-01-10 23:27:45. Ppid 1613 2021-01-10 23:28:15.120 sub Process 2.... finishedCopy the code
- You can see who’s doing it by looking at the previous method
sub_process_2.go
Pid 1571 isancestor
And its child process is 1613, which is 1613go run
This process, and the child of 1613 is 1648,
The final relationship is1571(acestor) -> 1613(go run) -> 1648(sub_process_2);
# ps l |grep sub_process_2
0 0 1613 1571 20 0 836060 30040 - Sl+ pts/1 0:00 /usr/local/go/bin/go run sub_process_2.go
0 0 1648 1613 20 0 745092 9420 - Sl+ pts/1 0:00 /tmp/go-build141590160/b001/exe/sub_process_2
0 0 1662 1512 20 0 112748 2360 - S+ pts/2 0:00 grep --color=auto sub_process_2
Copy the code
Write in the last
Here we are. Just give us a “like” before we leave. Since contacting the GO language, I have made up a lot of knowledge at the system level and network knowledge, and now I have to turn over the knowledge points that have been brought before. There will be an update on Containerd, and Docker’s love-hate relationship with Google(K8S) is now on the table.