前言
最近学Linux,有一个Linux进程树的作业,本来是有C源码的,为了练手,写了一个Java带有GUI版本的。
本博客和源代码原创,转载请注明!
本文链接
个人博客:https://ronglin.fun/?p=138
PDF链接:见博客网站
CSDN: https://blog.csdn.net/RongLin02/article/details/116244641
先看成果,可配合一起食用:Linux学习之打印进程树
源代码
工程目录结构如下:
源码一共700多行
Fxml文件
Main.fxml 文件,用Scene Builder生成的
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller.MainController">
<top>
<HBox alignment="CENTER" prefHeight="50.0" prefWidth="600.0" BorderPane.alignment="CENTER">
<children>
<HBox alignment="CENTER" nodeOrientation="LEFT_TO_RIGHT" prefHeight="50.0" prefWidth="325.0">
<children>
<Label fx:id="Label_Time" style="-fx-background-color: #D4F2E7;" text="1970年01月01日00:00:00" underline="true">
<font>
<Font size="18.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0">
<children>
<Button fx:id="button_fresh" mnemonicParsing="false" onAction="#freshPage" prefHeight="50.0" prefWidth="150.0" text="刷新" textAlignment="CENTER" textFill="RED" HBox.hgrow="ALWAYS">
<font>
<Font size="24.0" />
</font>
</Button>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="50.0" prefWidth="325.0">
<children>
<Label fx:id="Label_IP" style="-fx-background-color: #D4F2E7;" text="192.168.255.255" underline="true">
<font>
<Font size="18.0" />
</font>
</Label>
</children>
</HBox>
</children>
</HBox>
</top>
<left>
<ScrollPane fitToHeight="true" fitToWidth="true" prefWidth="400.0">
<content>
<VBox>
<children>
<TreeView fx:id="treeView_procs" VBox.vgrow="ALWAYS" />
</children>
</VBox>
</content>
</ScrollPane>
</left>
<center>
<GridPane alignment="CENTER" gridLinesVisible="true" style="-fx-background-color: #87CEFA;" BorderPane.alignment="CENTER">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0">
<children>
<Label text="Name:">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1">
<children>
<Label fx:id="Label_Name" text="systemd" textAlignment="CENTER" underline="true">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="1">
<children>
<Label text="State:">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="1">
<children>
<Label fx:id="Label_State" text="S" underline="true">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="2">
<children>
<Label text="Pid:" textAlignment="CENTER">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="2">
<children>
<Label fx:id="Label_Pid" text="1" underline="true">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="3">
<children>
<Label text="PPid:" textAlignment="CENTER">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="3">
<children>
<Label fx:id="Label_PPid" text="0" underline="true">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="4">
<children>
<Label text="NStgid:">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="4">
<children>
<Label fx:id="Label_NStgid" text="1" underline="true">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="5">
<children>
<Label text="NSpid:">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="5">
<children>
<Label fx:id="Label_NSpid" text="1" underline="true">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="6">
<children>
<Label text="NSpgid:">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="6">
<children>
<Label fx:id="Label_NSpgid" text="1" underline="true">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="7">
<children>
<Label text="NSsid:">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="7">
<children>
<Label fx:id="Label_NSsid" text="1" underline="true">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="8">
<children>
<Label text="Threads:">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="8">
<children>
<Label fx:id="Label_Threads" text="1" underline="true">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="9">
<children>
<Label text="By:">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="9">
<children>
<Label text="RongLin">
<font>
<Font size="24.0" />
</font>
</Label>
</children>
</HBox>
</children>
</GridPane>
</center>
</BorderPane>
controller
MainController.java文件
package Controller;
import application.Readproc;
import application.Process;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ResourceBundle;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.util.Duration;
import javafx.fxml.Initializable;
public class MainController implements Initializable{
final String ROOT = "/proc";
@FXML
private Label Label_IP;
@FXML
private Label Label_Time;
@FXML
private Label Label_Name;
@FXML
private Label Label_State;
@FXML
private Label Label_Pid;
@FXML
private Label Label_PPid;
@FXML
private Label Label_NStgid;
@FXML
private Label Label_NSpid;
@FXML
private Label Label_NSpgid;
@FXML
private Label Label_NSsid;
@FXML
private Label Label_Threads;
@FXML
private Button button_fresh;
@FXML
private TreeView<Process> treeView_procs;
private TreeItem<Process> rootItem = null;
@Override
public void initialize(URL location, ResourceBundle resources) {
//ip地址初始化
InetAddress localAddress=null;
try {
localAddress=InetAddress.getLocalHost();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Label_IP.setText(localAddress.getHostAddress());
//时间初始化
EventHandler<ActionEvent> timeUpdate =e->{
final String TimePattern="yyyy年MM月dd日 HH:mm:ss";
SimpleDateFormat dateFormat=new SimpleDateFormat(TimePattern);
Label_Time.setText(dateFormat.format(new Date(System.currentTimeMillis())));
};
//500ms获取一下系统时间
Timeline animation = new Timeline(new KeyFrame(Duration.millis(500), timeUpdate));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play();
//初始化树视图
initTreeView();
//初始化表格
initGridPane();
}
private void initTreeView()
{
//System.out.println("初始化了树视图");
Readproc.readFile(ROOT);
Process zero = new Process();
zero.setPid(0);
zero.setName("zero");
rootItem = new TreeItem<Process> (zero);
treeView_procs.setRoot(rootItem);
treeView_procs.setShowRoot(false);
//设置点击节点触发效果
treeView_procs.getSelectionModel().selectedItemProperty().addListener(
new ChangeListener<TreeItem <Process>>() {
@Override
public void changed(ObservableValue<? extends TreeItem<Process>> observable,
TreeItem<Process> oldValue, TreeItem<Process> newValue) {
// TODO Auto-generated method stub
if(newValue != null)
showGridPane(newValue.getValue());
}
});
DFSSetupTreeItem(rootItem,0);
}
private void DFSSetupTreeItem(TreeItem<Process> pitem,int ppid)
{
for(int i=0;i<Readproc.procNum;i++)
{
if(Readproc.procs[i].getPPid() == ppid)
{
TreeItem<Process> item = new TreeItem<Process> (Readproc.procs[i]);
pitem.getChildren().add(item);
DFSSetupTreeItem(item,Readproc.procs[i].getPid());
}
}
}
private void initGridPane()
{
//System.out.println("初始化了表格");
Label_Name.setText("0");
Label_State.setText("0");
Label_Pid.setText("0");
Label_PPid.setText("0");
Label_NStgid.setText("0");
Label_NSpid.setText("0");
Label_NSpgid.setText("0");
Label_NSsid.setText("0");
Label_Threads.setText("0");
}
private void showGridPane(Process proc)
{
//System.out.println("初始化了表格");
Label_Name.setText(proc.getName());
Label_State.setText(proc.getState());
Label_Pid.setText(Integer.toString(proc.getPid()));
Label_PPid.setText(Integer.toString(proc.getPPid()));
Label_NStgid.setText(Integer.toString(proc.getNStgid()));
Label_NSpid.setText(Integer.toString(proc.getNSpid()));
Label_NSpgid.setText(Integer.toString(proc.getNSpgid()));
Label_NSsid.setText(Integer.toString(proc.getNSsid()));
Label_Threads.setText(Integer.toString(proc.getThreads()));
}
@FXML
void freshPage(ActionEvent event) {
Readproc.procNum = 0;
Readproc.readFile(ROOT);
initGridPane();
if(rootItem != null)
{
rootItem.getChildren().clear();
DFSSetupTreeItem(rootItem,0);
}
}
}
控制器的难点在于TreeView的初始化,点击TreeView节点的触发和左上角的时间自动更新,源码上都有,JavaFX的模板性很强,就这样用就可以,要想知道原理,可以去查询API。
Application
这中间有3个类,一个是运行类
Main.java
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
BorderPane mainPane = FXMLLoader.load(getClass().getResource("/Fxml/Main.fxml"));
Scene mainScene = new Scene(mainPane,800,600);
primaryStage.setScene(mainScene);
primaryStage.setTitle("Linux进程树");
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
一个是进程类,Process.java
package application;
public class Process {
String Name;
String State;
int Pid;
int PPid;
int NStgid;
int NSpid;
int NSpgid;
int NSsid;
int Threads;
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getState() {
return State;
}
public void setState(String state) {
State = state;
}
public int getPid() {
return Pid;
}
public void setPid(int pid) {
Pid = pid;
}
public int getPPid() {
return PPid;
}
public void setPPid(int pPid) {
PPid = pPid;
}
public int getNStgid() {
return NStgid;
}
public void setNStgid(int nStgid) {
NStgid = nStgid;
}
public int getNSpid() {
return NSpid;
}
public void setNSpid(int nSpid) {
NSpid = nSpid;
}
public int getNSpgid() {
return NSpgid;
}
public void setNSpgid(int nSpgid) {
NSpgid = nSpgid;
}
public int getNSsid() {
return NSsid;
}
public void setNSsid(int nSsid) {
NSsid = nSsid;
}
public int getThreads() {
return Threads;
}
public void setThreads(int threads) {
Threads = threads;
}
// public String toString()
// {
// String str ="Name:" + this.Name + "\n" +
// "State:" + this.State + "\n" +
// "Pid:" + this.Pid + "\n" +
// "PPid:" + this.PPid + "\n" +
// "NStgid:" + this.NStgid + "\n" +
// "NSpid:" + this.NSpid + "\n" +
// "NSpgid:" + this.NSpgid + "\n" +
// "NSsid:" + this.NSsid + "\n" +
// "Threads:" + this.Threads + "\n";
// return str;
// }
public String toString()
{
return this.Pid+":"+this.Name;
}
}
一个是从文件中读取进程信息的类,定义的都是静态方法
Readproc.java
package application;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Readproc {
final static int MAX = 1024*1024;
final static int MAX_PROC_NUM= 1024;
public static Process[] procs = new Process[MAX_PROC_NUM];
public static int procNum = 0;
static byte[] buff = new byte[MAX];
public static boolean readFile(String path)
{
File rootFile = new File(path);
if(! rootFile.exists())
{
System.out.println("目录不存在");
return false;
}
procNum = 0;
File[] procFiles = rootFile.listFiles();
for(File file : procFiles)
{
if(isNumeric(file.getName()) == true)
{
procs[procNum] = getProcessFromStatus(file);
if(procs[procNum] != null)
procNum++;
}
}
return true;
}
private static boolean isNumeric(String str)
{
Pattern pattern = Pattern.compile("[0-9]*");
Matcher isNum = pattern.matcher(str);
if (!isNum.matches())
{
return false;
}
else
{
return true;
}
}
private static Process getProcessFromStatus(File file)
{
Process proc = new Process();
int len = 0;
FileInputStream input = null ;
StringBuffer buffer = new StringBuffer();
String path = file.getPath() + "/status";
try {
input = new FileInputStream(path);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println(path);
System.out.println("status文件没找到");
return null;
}
try {
while(( (len = input.read(buff) )!= -1 ))
{
buffer.append(new String(buff,0,len));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("status文件读写失败");
return null;
}
String[] statusPart = buffer.toString().split("\\s+");
for(int i=0;i<statusPart.length;i++)
{
if(statusPart[i].compareTo("Name:") == 0)
{
proc.Name = new String(statusPart[i+1]);
}
else if(statusPart[i].compareTo("State:") == 0)
{
proc.State = new String(statusPart[i+1] + statusPart[i+2]);
}
else if(statusPart[i].compareTo("Pid:") == 0)
{
proc.Pid = Integer.parseInt(statusPart[i+1]);
}
else if(statusPart[i].compareTo("PPid:") == 0)
{
proc.PPid = Integer.parseInt(statusPart[i+1]);
}
else if(statusPart[i].compareTo("NStgid:") == 0)
{
proc.NStgid = Integer.parseInt(statusPart[i+1]);
}
else if(statusPart[i].compareTo("NSpid:") == 0)
{
proc.NSpid = Integer.parseInt(statusPart[i+1]);
}
else if(statusPart[i].compareTo("NSpgid:") == 0)
{
proc.NSpgid = Integer.parseInt(statusPart[i+1]);
}
else if(statusPart[i].compareTo("NSsid:") == 0)
{
proc.NSsid = Integer.parseInt(statusPart[i+1]);
}
else if(statusPart[i].compareTo("Threads:") == 0)
{
proc.Threads = Integer.parseInt(statusPart[i+1]);
}
}
try {
input.close();
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println("input流关闭失败");
return null;
}
return proc;
}
// public static void main(String[] args) {
// Readproc.readFile("F:/Desktop/proc");
//
// System.out.println("共有"+procNum+"个进程");
//
// for(int i=0;i<procNum;i++)
// {
// System.out.println(procs[i]);
// }
//
// }
}
总结
原理很简单,难点在于JavaFX基本控件的使用和GUI的布局,丑了一点,不过重点在于熟悉Linux系统的进程概念。
代码原创,欢迎指点,=w=