ProcfsBasedProcessTree.java
上传用户:quxuerui
上传日期:2018-01-08
资源大小:41811k
文件大小:13k
源码类别:

网格计算

开发平台:

Java

  1. /**
  2.  * Licensed to the Apache Software Foundation (ASF) under one
  3.  * or more contributor license agreements.  See the NOTICE file
  4.  * distributed with this work for additional information
  5.  * regarding copyright ownership.  The ASF licenses this file
  6.  * to you under the Apache License, Version 2.0 (the
  7.  * "License"); you may not use this file except in compliance
  8.  * with the License.  You may obtain a copy of the License at
  9.  *
  10.  *     http://www.apache.org/licenses/LICENSE-2.0
  11.  *
  12.  * Unless required by applicable law or agreed to in writing, software
  13.  * distributed under the License is distributed on an "AS IS" BASIS,
  14.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15.  * See the License for the specific language governing permissions and
  16.  * limitations under the License.
  17.  */
  18. package org.apache.hadoop.util;
  19. import java.io.BufferedReader;
  20. import java.io.File;
  21. import java.io.FileNotFoundException;
  22. import java.io.FileReader;
  23. import java.io.IOException;
  24. import java.util.ArrayList;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.HashMap;
  28. import java.util.regex.Matcher;
  29. import java.util.regex.Pattern;
  30. import java.util.Arrays;
  31. import java.util.LinkedList;
  32. import org.apache.commons.logging.Log;
  33. import org.apache.commons.logging.LogFactory;
  34. import org.apache.hadoop.util.Shell.ExitCodeException;
  35. import org.apache.hadoop.util.Shell.ShellCommandExecutor;
  36. /**
  37.  * A Proc file-system based ProcessTree. Works only on Linux.
  38.  */
  39. public class ProcfsBasedProcessTree {
  40.   private static final Log LOG = LogFactory
  41.       .getLog("org.apache.hadoop.mapred.ProcfsBasedProcessTree");
  42.   private static final String PROCFS = "/proc/";
  43.   public static final long DEFAULT_SLEEPTIME_BEFORE_SIGKILL = 5000L;
  44.   private long sleepTimeBeforeSigKill = DEFAULT_SLEEPTIME_BEFORE_SIGKILL;
  45.   private static final Pattern PROCFS_STAT_FILE_FORMAT = Pattern
  46.       .compile("^([0-9-]+)\s([^\s]+)\s[^\s]\s([0-9-]+)\s([0-9-]+)\s([0-9-]+)\s([0-9-]+\s){16}([0-9]+)(\s[0-9-]+){16}");
  47.   private Integer pid = -1;
  48.   private Map<Integer, ProcessInfo> processTree = new HashMap<Integer, ProcessInfo>();
  49.   public ProcfsBasedProcessTree(String pid) {
  50.     this.pid = getValidPID(pid);
  51.   }
  52.   public void setSigKillInterval(long interval) {
  53.     sleepTimeBeforeSigKill = interval;
  54.   }
  55.   /**
  56.    * Checks if the ProcfsBasedProcessTree is available on this system.
  57.    * 
  58.    * @return true if ProcfsBasedProcessTree is available. False otherwise.
  59.    */
  60.   public static boolean isAvailable() {
  61.     try {
  62.       String osName = System.getProperty("os.name");
  63.       if (!osName.startsWith("Linux")) {
  64.         LOG.info("ProcfsBasedProcessTree currently is supported only on "
  65.             + "Linux.");
  66.         return false;
  67.       }
  68.     } catch (SecurityException se) {
  69.       LOG.warn("Failed to get Operating System name. " + se);
  70.       return false;
  71.     }
  72.     return true;
  73.   }
  74.   /**
  75.    * Get the process-tree with latest state. If the root-process is not alive,
  76.    * an empty tree will be returned.
  77.    * 
  78.    * @return the process-tree with latest state.
  79.    */
  80.   public ProcfsBasedProcessTree getProcessTree() {
  81.     if (pid != -1) {
  82.       // Get the list of processes
  83.       List<Integer> processList = getProcessList();
  84.       Map<Integer, ProcessInfo> allProcessInfo = new HashMap<Integer, ProcessInfo>();
  85.       processTree.clear();
  86.       ProcessInfo me = null;
  87.       for (Integer proc : processList) {
  88.         // Get information for each process
  89.         ProcessInfo pInfo = new ProcessInfo(proc);
  90.         if (constructProcessInfo(pInfo) != null) {
  91.           allProcessInfo.put(proc, pInfo);
  92.           if (proc.equals(this.pid)) {
  93.             me = pInfo; // cache 'me'
  94.             processTree.put(proc, pInfo);
  95.           }
  96.         }
  97.       }
  98.       if (me == null) {
  99.         return this; 
  100.       }
  101.       // Add each process to its parent.
  102.       for (Map.Entry<Integer, ProcessInfo> entry : allProcessInfo.entrySet()) {
  103.         Integer pID = entry.getKey();
  104.         if (pID != 1) {
  105.           ProcessInfo pInfo = entry.getValue();
  106.           ProcessInfo parentPInfo = allProcessInfo.get(pInfo.getPpid());
  107.           if (parentPInfo != null) {
  108.             parentPInfo.addChild(pInfo);
  109.           }
  110.         }
  111.       }
  112.       // now start constructing the process-tree
  113.       LinkedList<ProcessInfo> pInfoQueue = new LinkedList<ProcessInfo>();
  114.       pInfoQueue.addAll(me.getChildren());
  115.       while (!pInfoQueue.isEmpty()) {
  116.         ProcessInfo pInfo = pInfoQueue.remove();
  117.         if (!processTree.containsKey(pInfo.getPid())) {
  118.           processTree.put(pInfo.getPid(), pInfo);
  119.         }
  120.         pInfoQueue.addAll(pInfo.getChildren());
  121.       }
  122.       if (LOG.isDebugEnabled()) {
  123.         // Log.debug the ProcfsBasedProcessTree
  124.         LOG.debug(this.toString());
  125.       }
  126.     }
  127.     return this;
  128.   }
  129.   /**
  130.    * Is the process-tree alive? Currently we care only about the status of the
  131.    * root-process.
  132.    * 
  133.    * @return true if the process-true is alive, false otherwise.
  134.    */
  135.   public boolean isAlive() {
  136.     if (pid == -1) {
  137.       return false;
  138.     } else {
  139.       return this.isAlive(pid);
  140.     }
  141.   }
  142.   /**
  143.    * Destroy the process-tree. Currently we only make sure the root process is
  144.    * gone. It is the responsibility of the root process to make sure that all
  145.    * its descendants are cleaned up.
  146.    */
  147.   public void destroy() {
  148.     LOG.debug("Killing ProcfsBasedProcessTree of " + pid);
  149.     if (pid == -1) {
  150.       return;
  151.     }
  152.     ShellCommandExecutor shexec = null;
  153.     if (isAlive(this.pid)) {
  154.       try {
  155.         String[] args = { "kill", this.pid.toString() };
  156.         shexec = new ShellCommandExecutor(args);
  157.         shexec.execute();
  158.       } catch (IOException ioe) {
  159.         LOG.warn("Error executing shell command " + ioe);
  160.       } finally {
  161.         LOG.info("Killing " + pid + " with SIGTERM. Exit code "
  162.             + shexec.getExitCode());
  163.       }
  164.     }
  165.     SigKillThread sigKillThread = new SigKillThread();
  166.     sigKillThread.setDaemon(true);
  167.     sigKillThread.start();
  168.   }
  169.   /**
  170.    * Get the cumulative virtual memory used by all the processes in the
  171.    * process-tree.
  172.    * 
  173.    * @return cumulative virtual memory used by the process-tree in bytes.
  174.    */
  175.   public long getCumulativeVmem() {
  176.     long total = 0;
  177.     for (ProcessInfo p : processTree.values()) {
  178.       if (p != null) {
  179.         total += p.getVmem();
  180.       }
  181.     }
  182.     return total;
  183.   }
  184.   /**
  185.    * Get PID from a pid-file.
  186.    * 
  187.    * @param pidFileName
  188.    *          Name of the pid-file.
  189.    * @return the PID string read from the pid-file. Returns null if the
  190.    *         pidFileName points to a non-existing file or if read fails from the
  191.    *         file.
  192.    */
  193.   public static String getPidFromPidFile(String pidFileName) {
  194.     BufferedReader pidFile = null;
  195.     FileReader fReader = null;
  196.     String pid = null;
  197.     try {
  198.       fReader = new FileReader(pidFileName);
  199.       pidFile = new BufferedReader(fReader);
  200.     } catch (FileNotFoundException f) {
  201.       LOG.debug("PidFile doesn't exist : " + pidFileName);
  202.       return pid;
  203.     }
  204.     try {
  205.       pid = pidFile.readLine();
  206.     } catch (IOException i) {
  207.       LOG.error("Failed to read from " + pidFileName);
  208.     } finally {
  209.       try {
  210.         if (fReader != null) {
  211.           fReader.close();
  212.         }
  213.         try {
  214.           if (pidFile != null) {
  215.             pidFile.close();
  216.           }
  217.         } catch (IOException i) {
  218.           LOG.warn("Error closing the stream " + pidFile);
  219.         }
  220.       } catch (IOException i) {
  221.         LOG.warn("Error closing the stream " + fReader);
  222.       }
  223.     }
  224.     return pid;
  225.   }
  226.   private Integer getValidPID(String pid) {
  227.     Integer retPid = -1;
  228.     try {
  229.       retPid = Integer.parseInt((String) pid);
  230.       if (retPid <= 0) {
  231.         retPid = -1;
  232.       }
  233.     } catch (NumberFormatException nfe) {
  234.       retPid = -1;
  235.     }
  236.     return retPid;
  237.   }
  238.   /**
  239.    * Get the list of all processes in the system.
  240.    */
  241.   private List<Integer> getProcessList() {
  242.     String[] processDirs = (new File(PROCFS)).list();
  243.     List<Integer> processList = new ArrayList<Integer>();
  244.     for (String dir : processDirs) {
  245.       try {
  246.         int pd = Integer.parseInt(dir);
  247.         if ((new File(PROCFS + dir)).isDirectory()) {
  248.           processList.add(Integer.valueOf(pd));
  249.         }
  250.       } catch (NumberFormatException n) {
  251.         // skip this directory
  252.       } catch (SecurityException s) {
  253.         // skip this process
  254.       }
  255.     }
  256.     return processList;
  257.   }
  258.   /**
  259.    * 
  260.    * Construct the ProcessInfo using the process' PID and procfs and return the
  261.    * same. Returns null on failing to read from procfs,
  262.    */
  263.   private ProcessInfo constructProcessInfo(ProcessInfo pinfo) {
  264.     ProcessInfo ret = null;
  265.     // Read "/proc/<pid>/stat" file
  266.     BufferedReader in = null;
  267.     FileReader fReader = null;
  268.     try {
  269.       fReader = new FileReader(PROCFS + pinfo.getPid() + "/stat");
  270.       in = new BufferedReader(fReader);
  271.     } catch (FileNotFoundException f) {
  272.       // The process vanished in the interim!
  273.       return ret;
  274.     }
  275.     ret = pinfo;
  276.     try {
  277.       String str = in.readLine(); // only one line
  278.       Matcher m = PROCFS_STAT_FILE_FORMAT.matcher(str);
  279.       boolean mat = m.find();
  280.       if (mat) {
  281.         // Set ( name ) ( ppid ) ( pgrpId ) (session ) (vsize )
  282.         pinfo.update(m.group(2), Integer.parseInt(m.group(3)), Integer
  283.             .parseInt(m.group(4)), Integer.parseInt(m.group(5)), Long
  284.             .parseLong(m.group(7)));
  285.       }
  286.     } catch (IOException io) {
  287.       LOG.warn("Error reading the stream " + io);
  288.       ret = null;
  289.     } finally {
  290.       // Close the streams
  291.       try {
  292.         if (fReader != null) {
  293.           fReader.close();
  294.         }
  295.         try {
  296.           if (in != null) {
  297.             in.close();
  298.           }
  299.         } catch (IOException i) {
  300.           LOG.warn("Error closing the stream " + in);
  301.         }
  302.       } catch (IOException i) {
  303.         LOG.warn("Error closing the stream " + fReader);
  304.       }
  305.     }
  306.     return ret;
  307.   }
  308.   /**
  309.    * Is the process with PID pid still alive?
  310.    */
  311.   private boolean isAlive(Integer pid) {
  312.     // This method assumes that isAlive is called on a pid that was alive not
  313.     // too long ago, and hence assumes no chance of pid-wrapping-around.
  314.     ShellCommandExecutor shexec = null;
  315.     try {
  316.       String[] args = { "kill", "-0", pid.toString() };
  317.       shexec = new ShellCommandExecutor(args);
  318.       shexec.execute();
  319.     } catch (ExitCodeException ee) {
  320.       return false;
  321.     } catch (IOException ioe) {
  322.       LOG.warn("Error executing shell command "
  323.           + Arrays.toString(shexec.getExecString()) + ioe);
  324.       return false;
  325.     }
  326.     return (shexec.getExitCode() == 0 ? true : false);
  327.   }
  328.   /**
  329.    * Helper thread class that kills process-tree with SIGKILL in background
  330.    */
  331.   private class SigKillThread extends Thread {
  332.     public void run() {
  333.       this.setName(this.getClass().getName() + "-" + String.valueOf(pid));
  334.       ShellCommandExecutor shexec = null;
  335.       try {
  336.         // Sleep for some time before sending SIGKILL
  337.         Thread.sleep(sleepTimeBeforeSigKill);
  338.       } catch (InterruptedException i) {
  339.         LOG.warn("Thread sleep is interrupted.");
  340.       }
  341.       // Kill the root process with SIGKILL if it is still alive
  342.       if (ProcfsBasedProcessTree.this.isAlive(pid)) {
  343.         try {
  344.           String[] args = { "kill", "-9", pid.toString() };
  345.           shexec = new ShellCommandExecutor(args);
  346.           shexec.execute();
  347.         } catch (IOException ioe) {
  348.           LOG.warn("Error executing shell command " + ioe);
  349.         } finally {
  350.           LOG.info("Killing " + pid + " with SIGKILL. Exit code "
  351.               + shexec.getExitCode());
  352.         }
  353.       }
  354.     }
  355.   }
  356.   /**
  357.    * Returns a string printing PIDs of process present in the
  358.    * ProcfsBasedProcessTree. Output format : [pid pid ..]
  359.    */
  360.   public String toString() {
  361.     StringBuffer pTree = new StringBuffer("[ ");
  362.     for (Integer p : processTree.keySet()) {
  363.       pTree.append(p);
  364.       pTree.append(" ");
  365.     }
  366.     return pTree.substring(0, pTree.length()) + "]";
  367.   }
  368.   /**
  369.    * 
  370.    * Class containing information of a process.
  371.    * 
  372.    */
  373.   private static class ProcessInfo {
  374.     private Integer pid; // process-id
  375.     private String name; // command name
  376.     private Integer pgrpId; // process group-id
  377.     private Integer ppid; // parent process-id
  378.     private Integer sessionId; // session-id
  379.     private Long vmem; // virtual memory usage
  380.     private List<ProcessInfo> children = new ArrayList<ProcessInfo>(); // list of children
  381.     public ProcessInfo(int pid) {
  382.       this.pid = Integer.valueOf(pid);
  383.     }
  384.     public Integer getPid() {
  385.       return pid;
  386.     }
  387.     public String getName() {
  388.       return name;
  389.     }
  390.     public Integer getPgrpId() {
  391.       return pgrpId;
  392.     }
  393.     public Integer getPpid() {
  394.       return ppid;
  395.     }
  396.     public Integer getSessionId() {
  397.       return sessionId;
  398.     }
  399.     public Long getVmem() {
  400.       return vmem;
  401.     }
  402.     public boolean isParent(ProcessInfo p) {
  403.       if (pid.equals(p.getPpid())) {
  404.         return true;
  405.       }
  406.       return false;
  407.     }
  408.     public void update(String name, Integer ppid, Integer pgrpId,
  409.         Integer sessionId, Long vmem) {
  410.       this.name = name;
  411.       this.ppid = ppid;
  412.       this.pgrpId = pgrpId;
  413.       this.sessionId = sessionId;
  414.       this.vmem = vmem;
  415.     }
  416.     public boolean addChild(ProcessInfo p) {
  417.       return children.add(p);
  418.     }
  419.     public List<ProcessInfo> getChildren() {
  420.       return children;
  421.     }
  422.   }
  423. }