基于proc文件系统用Electron实现一个任务管理器

1,076 阅读3分钟

前言

我们有一个操作系统的课程设计,看到了基于proc文件系统实现一个任务管理器的课题,感觉这个很有意思。一开始准备用c++写,后来写了一点,才知道高估了自己的C++水平....,然后就想到了用 Node + Electron实现一个任务管理器。这样在图形界面上,就可以用HTML+CSS写了,任务量一下子就下来了!!

proc文件系统介绍

/proc 文件系统是一个虚拟文件系统,通过它可以使用一种新的方法在 Linux内核空间和用户间之间进行通信。在 /proc 文件系统中,我们可以将对虚拟文件的读写作为与内核中实体进行通信的一种手段,但是与普通文件不同的是,这些虚拟文件的内容都是动态创建的。

proc文件系统文档

Electron 介绍

一个使用 JavaScript,HTML 和 CSS 构建跨平台的桌面应用程序

Electron文档

准备工作

需要安装 Node.js。 我们建议您安装最新可用的 LTSCurrent 版本

要检查 Node.js 是否正确安装,请在您的终端输入以下命令:

node -v
npm -v

开始搭建

mkdir electron-app && cd electron-app
npm init -y
npm i --save-dev electron

然后应该有的目录结构

|-- main.js
|-- package.json
|-- node_modules
|-- config
|-- tem
|-- utils

修改一下package.json的内容

"main": "main.js",
 "scripts": {
  	"start": "electron .",
 },

目录内容介绍

  • /utils/event.js
// 主要是做事件的派发,根据不同的事件触发不同的任务
const EventEmitter = require('events');

class MyEmitter extends EventEmitter { }

const Emitter = new MyEmitter();

module.exports = {
  Emitter
}
  • /utils/index.js
.......


// 所有的函数的功能和这个功能类似,都是去读/proc里面的相应的文件,文件处于内存中,实时更新着,记录着操作系统相应的信息,我们只要读出相应的信息,就可以获取当前系统的信息
/**
 * @description 获取每个进程的信息
 */
function getProcessInfo() {
  const ProcesssInfos = fs.readdirSync('/proc');
  const processes = []
  ProcesssInfos.forEach(item => {

    let finallPath = path.join('/proc', item)
    let stat = fs.lstatSync(finallPath)
    if (stat.isDirectory && !isNaN(Number(item))) {
      const processinfo = fs.readFileSync(path.join(finallPath, '/status'), { encoding: 'utf8' })
      processes.push(...stringToJson(processinfo)) // stringToJson 自己写的一个字符串解析的方法
    }
  })
  return processes;
}

.....

  • /utils/render.js
.....

/**
 * @description deal with the processinfopage render
 */

function renderProcessInfo(processInfo) {

  let template = `
    <div class="processinfo-box">
      <div class="header">
        <span class="name">NAME</span>
        <span class="pid">PID</span>
        <span class="state">STATE</span>
        <span class="memory">MEMORY</span>
        <span class="share-memeory-size">SHAREED MEMORY</span>
      </div>
      <div class="info-content">
        <% processInfo.forEach(item => {%>
          <div class="info-content-box" data-pid=<%= item.Pid %>>
            <span class="name"><%= item.Name%></span>
            <span class="pid"><%= item.Pid%></span>
            <span class="state"><%= item.State%></span>
            <span class="memory"><%= KBToMB(item["VmRSS"])%></span>
            <span class="share-memeory-size"><%= KBToMB(item["VmLib"])%></span>
          </div>
        <%}) %>
      </div>
    </div>
  `
  template = ejs.render(template, { processInfo, KBToMB })
  return template;
}

.....
这里的方法都是用ejs去解析模板,然后添加到相应的界面上去

  • /config/config.js
const { Emitter } = require('../utils/events')
// electron菜单的配置
let MenuItems = [
  .....
]

// echarts的配置
function getMemoryOrSawpOption(data) {
  return option = {
    ......
  };
}
// echarts的配置
function getReceiveOrSendOption(data = 0, type) {
  let option = {
   .....
  }
}
  • /tem/**

这里的东西都是相应的界面 展示不同的信息

  • main.js
const { app, BrowserWindow, Menu, MenuItem } = require('electron')
const { MenuItems } = require('./config/config')
const { Emitter } = require('./utils/events')


function createWindow() { // 创建窗口实例
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  })

  const items = MenuItems.map(item => {
    return new MenuItem(item)
  })

  const menu = new Menu();
  items.forEach(item => {
    menu.append(item)
  })
  // 设置 菜单
  win.setMenu(menu)

  win.loadFile('./tem/source/index.html')
  win.webContents.openDevTools();


  // 监听不同的事件 感觉按钮点击 加载不同的界面
  
  // source page
  Emitter.on('source', function () {
    win.loadFile('./tem/source/index.html');
  })

  // memory page
  Emitter.on('innerMemory', function () {
    win.loadFile('./tem/innermemory/index.html')
  })

  // informatin page
  Emitter.on('information', function () {
    win.loadFile('./tem/versionInfomation/index.html')
  })
  // disk infomation
  Emitter.on('disk', function () {
    win.loadFile('./tem/diskInformation/index.html')
  })

  Emitter.on('process', function () {
    win.loadFile('./tem/process/index.html')

  })
}

app.whenReady().then(createWindow)

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow()
  }
})

效果展示

资源信息

内存信息

进程信息

磁盘信息

系统信息

打包

关于打包 我自己也不是十分清晰,大神们可以自己研究,我列出我的打包配置

  "build": {
    "appId": "XXXX",
    "productName": "XXXX",
    "directories": {
      "output": "build"
    },
    "linux": {
      "target": [
        "deb"
      ],
      "icon": "./icon.png",
      "synopsis": "simple",
      "maintainer": ".deb"
    }
  }

写在最后

我对于Electron也不是十分的会,只能说理解了一个皮毛,为了写图形界面临时看了一眼文档才写出来的,项目中的错误还有很多,希望各位大佬不要喷小弟。。。。 如果我写的东西对您有帮助,麻烦给我的github来个star

项目地址