github.com/runcom/containerd@v0.0.0-20160708090337-9bff9f934c0d/supervisor/monitor_linux.go (about)

     1  package supervisor
     2  
     3  import (
     4  	"sync"
     5  	"syscall"
     6  
     7  	"github.com/Sirupsen/logrus"
     8  	"github.com/docker/containerd/archutils"
     9  	"github.com/docker/containerd/runtime"
    10  )
    11  
    12  // NewMonitor starts a new process monitor and returns it
    13  func NewMonitor() (*Monitor, error) {
    14  	m := &Monitor{
    15  		receivers: make(map[int]interface{}),
    16  		exits:     make(chan runtime.Process, 1024),
    17  		ooms:      make(chan string, 1024),
    18  	}
    19  	fd, err := archutils.EpollCreate1(0)
    20  	if err != nil {
    21  		return nil, err
    22  	}
    23  	m.epollFd = fd
    24  	go m.start()
    25  	return m, nil
    26  }
    27  
    28  // Monitor represents a runtime.Process monitor
    29  type Monitor struct {
    30  	m         sync.Mutex
    31  	receivers map[int]interface{}
    32  	exits     chan runtime.Process
    33  	ooms      chan string
    34  	epollFd   int
    35  }
    36  
    37  // Exits returns the channel used to notify of a process exit
    38  func (m *Monitor) Exits() chan runtime.Process {
    39  	return m.exits
    40  }
    41  
    42  // OOMs returns the channel used to notify of a container exit due to OOM
    43  func (m *Monitor) OOMs() chan string {
    44  	return m.ooms
    45  }
    46  
    47  // Monitor adds a process to the list of the one being monitored
    48  func (m *Monitor) Monitor(p runtime.Process) error {
    49  	m.m.Lock()
    50  	defer m.m.Unlock()
    51  	fd := p.ExitFD()
    52  	event := syscall.EpollEvent{
    53  		Fd:     int32(fd),
    54  		Events: syscall.EPOLLHUP,
    55  	}
    56  	if err := archutils.EpollCtl(m.epollFd, syscall.EPOLL_CTL_ADD, fd, &event); err != nil {
    57  		return err
    58  	}
    59  	EpollFdCounter.Inc(1)
    60  	m.receivers[fd] = p
    61  	return nil
    62  }
    63  
    64  // MonitorOOM adds a container to the list of the ones monitored for OOM
    65  func (m *Monitor) MonitorOOM(c runtime.Container) error {
    66  	m.m.Lock()
    67  	defer m.m.Unlock()
    68  	o, err := c.OOM()
    69  	if err != nil {
    70  		return err
    71  	}
    72  	fd := o.FD()
    73  	event := syscall.EpollEvent{
    74  		Fd:     int32(fd),
    75  		Events: syscall.EPOLLHUP | syscall.EPOLLIN,
    76  	}
    77  	if err := archutils.EpollCtl(m.epollFd, syscall.EPOLL_CTL_ADD, fd, &event); err != nil {
    78  		return err
    79  	}
    80  	EpollFdCounter.Inc(1)
    81  	m.receivers[fd] = o
    82  	return nil
    83  }
    84  
    85  // Close cleans up resources allocated by NewMonitor()
    86  func (m *Monitor) Close() error {
    87  	return syscall.Close(m.epollFd)
    88  }
    89  
    90  func (m *Monitor) start() {
    91  	var events [128]syscall.EpollEvent
    92  	for {
    93  		n, err := archutils.EpollWait(m.epollFd, events[:], -1)
    94  		if err != nil {
    95  			if err == syscall.EINTR {
    96  				continue
    97  			}
    98  			logrus.WithField("error", err).Fatal("containerd: epoll wait")
    99  		}
   100  		// process events
   101  		for i := 0; i < n; i++ {
   102  			fd := int(events[i].Fd)
   103  			m.m.Lock()
   104  			r := m.receivers[fd]
   105  			switch t := r.(type) {
   106  			case runtime.Process:
   107  				if events[i].Events == syscall.EPOLLHUP {
   108  					delete(m.receivers, fd)
   109  					if err = syscall.EpollCtl(m.epollFd, syscall.EPOLL_CTL_DEL, fd, &syscall.EpollEvent{
   110  						Events: syscall.EPOLLHUP,
   111  						Fd:     int32(fd),
   112  					}); err != nil {
   113  						logrus.WithField("error", err).Error("containerd: epoll remove fd")
   114  					}
   115  					if err := t.Close(); err != nil {
   116  						logrus.WithField("error", err).Error("containerd: close process IO")
   117  					}
   118  					EpollFdCounter.Dec(1)
   119  					m.exits <- t
   120  				}
   121  			case runtime.OOM:
   122  				// always flush the event fd
   123  				t.Flush()
   124  				if t.Removed() {
   125  					delete(m.receivers, fd)
   126  					// epoll will remove the fd from its set after it has been closed
   127  					t.Close()
   128  					EpollFdCounter.Dec(1)
   129  				} else {
   130  					m.ooms <- t.ContainerID()
   131  				}
   132  			}
   133  			m.m.Unlock()
   134  		}
   135  	}
   136  }