システムモニターの作成(Linux)

この記事では、独自のシステムモニターを作成するプロセスについて説明します。 このシステムモニターは以下を示します。



最後から2番目のポイントにいる誰かが、キャッシュされたデータなしでその意味を理解していない場合は、 これを読んで、これが何を意味するかを説明します。 また、このシステムモニターには、プロセスを完了する機能が実装されています。

その結果、このようなシステムモニターを取得する必要があります。

画像画像

その主な機能が定義され、コードの説明を開始できます。その機能のほとんどすべてが/ procフォルダーの解析に基づいているため、コードはLinuxでのみ機能することをすぐに警告します。 システムモニターを3つのクラスに分割し、最初のクラスでは一般的なシステム情報を実装し、2番目のクラスではプロセステーブルを、3番目のクラスではこれを1つの全体に結合します。 これが最初のクラスフレームワークです。私はそれをInfoと呼びました。

#ifndef INFO_H #define INFO_H #include <QtGui> #include <iostream> #include <fstream> #include <pwd.h> #include <unistd.h> #include <sys/sysinfo.h> #include <vector> using namespace std; class Info : public QWidget { Q_OBJECT public: explicit Info(QWidget *parent = 0); private: QLabel * hostname; QLabel * user; QLabel * uptime; QLabel * proc; QLabel * freq; QLabel * cpuload; QLabel * mem; QLabel * memload; QProgressBar * cpubar; QProgressBar * membar; QVBoxLayout * layout; QHBoxLayout * hlayout; vector<float> readCpuStats(); int getCpuLoad(double dt); public slots: void update(); }; #endif // INFO_H
      
      





ヘッダーファイルpwd.hおよびunistd.hは、ユーザー名、およびアップタイム用のヘッダーファイルsys / sysinfo.hを取得するために使用されます。 次に、Infoクラスの実装について説明します。Infoクラスはinfo.cppファイルにあります。 info.cppファイルの最初に、info.h(infoクラスフレームワーク)を含める必要があります。 Infoクラスのコンストラクターは次のとおりです。

 Info::Info(QWidget *parent) : QWidget(parent) { hostname = new QLabel("Hostname: "); user = new QLabel(": "); uptime = new QLabel("Uptime: "); proc = new QLabel(": "); freq = new QLabel(":"); cpuload = new QLabel(" : "); mem = new QLabel(" : "); memload = new QLabel("  : "); cpubar = new QProgressBar; membar = new QProgressBar; layout = new QVBoxLayout; hlayout = new QHBoxLayout; cpubar->setMaximumHeight(21); membar->setMaximumHeight(21); hlayout->addWidget(cpuload); hlayout->addWidget(cpubar); layout->addWidget(hostname); layout->addWidget(user); layout->addWidget(uptime); layout->addWidget(proc); layout->addWidget(freq); layout->addLayout(hlayout); layout->addWidget(mem); layout->addWidget(memload); layout->addWidget(membar); setLayout(layout); update(); QTimer *timer = new QTimer; connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(2000); }
      
      





最初にすべてのグラフィック要素を初期化します。 次のステップは、グラフィック要素をレイアウトに配置することです。 すべてのラベルとプログレスバーに値が入力されるように、更新スロットを呼び出した後。 情報を定期的に更新するために、QTimerクラスのオブジェクトを作成し、そのタイムアウト信号を既知の更新スロットに接続します。

更新スロットを次のように記述するのは論理的だと思います。そのコードは次のとおりです。

 void Info::update() { ifstream stream("/proc/sys/kernel/hostname"); string str; getline(stream,str); hostname->setText("Hostname: " + QString::fromStdString(str)); uid_t uid = geteuid(); passwd *pw = getpwuid(uid); user->setText(": " + QString::fromAscii(pw->pw_name)); struct sysinfo o; sysinfo(&o); long up = o.uptime; int hour = up/60/60; int min = (up - hour*60*60) / 60; int sec = ((up - hour*60*60) - min*60); QString e = QString::number(hour) + QString(" h. ") + QString::number(min) + QString(" m. ") + QString::number(sec) + QString(" s."); uptime->setText("Uptime: " + e); stream.close(); stream.open("/proc/cpuinfo"); for(int i = 0; i < 16;i++) stream >> str; getline(stream,str); proc->setText(": " + QString::fromStdString(str)); for(int i = 0; i< 7; i++) stream >> str; freq->setText(": " + QString::fromStdString(str) + " MHz"); cpubar->setValue(getCpuLoad(0.3)); stream.close(); stream.open("/proc/meminfo"); stream >> str; stream >> str; int num = atoi(str.c_str()); int percent = num / 100; int gb = (num / 1024) / 1024; int mb = (num-gb*1024*1024) /1024; int kb = (num - (gb*1024*1024+mb*1024)); if (gb > 0) e = QString::number(gb) + QString(" Gb "); else e = QString(""); if (mb > 0) e += QString::number(mb) + QString(" Mb "); if (kb > 0) e += QString::number(kb) + QString(" Kb "); mem->setText(" : " + e); int free = 0; for (int i = 0 ; i < 3 ; i++) { stream >> str; stream >> str; stream >> str; free += atoi(str.c_str()); } num -= free; gb = num / 1024 / 1024; mb = (num - gb*1024*1024) / 1024; kb = (num - ((mb*1024) + (gb * 1024 * 1024))); if (gb > 0) e = QString::number(gb) + QString(" Gb "); else e = QString(""); if (mb > 0) e += QString::number(mb) + QString(" Mb "); if (kb > 0) e += QString::number(kb) + QString(" Kb "); memload->setText("  : " + e); percent = num / percent; membar->setValue(percent); }
      
      





最初に、ファイル/ proc / sys / kernel / hostnameを読み取り用に開き(プログラムがスーパーユーザー権限を必要としない理由です)、マシンの名前を取得します。その後、ホスト名ラベルを入力します。 その後、passwdおよびuid_t構造体のインスタンスを作成し、その助けを借りて、ユーザーラベルに入力するユーザー名を取得します。 ユーザー名を確認する方法に関する情報は、whoamiプログラムのソースコードから取得されました。 次に、sysinfo構造体のインスタンスを使用して値を作成し、そこからシステムの稼働時間に関する情報を取得します。 その後、作成済みのストリームstreamをファイル/ proc / cpuinfoにリンクします。このファイルには、プロセッサモデルの名前とその周波数に関する情報があります。 getCpuLoadメソッドを使用してプロセッサの負荷を取得します。これについては、以下で説明します。 ここで、ストリームストリームをファイル/ proc / meminfoに関連付け、そこからRAMの量を取得し、読み取り可能な形式にします。 次に、同じファイルから、空きRAMの量とキャッシュデータの量を取得します。 占有RAMの量を取得するには、空きRAMとキャッシュされたデータの量をすべてのRAMから差し引いてから、読み取り可能な形式にします。 さて、上で約束したように、getCpuLoad関数の説明。 私はこの関数を自分で書いたわけではありません( ここから取りまし )ので、非常に詳細な説明はできません。一般的な用語で説明します。 ここにあります:

 int Info::getCpuLoad(double dt) { vector<float> stats1 = readCpuStats(); QProcess::execute("sleep",QStringList() << QString::number(dt)); vector<float> stats2 = readCpuStats(); int size1 = stats1.size(); int size2 = stats2.size(); if (!size1 || !size2 || size1 != size2) return 2; for (int i = 0; i < size1; ++i) stats2[i] -= stats1[i]; int sum = 1; for (int i = 0; i < size1; ++i) sum += stats2[i]; int load = 100 - (stats2[size2 - 1] * 100 / sum); return load; }
      
      





2回、readCpuStats関数を使用して3つの値を取得します(以下でも説明します)が、特定の時間間隔でこの関数を呼び出します。 これらの値に基づいてプロセッサの負荷が計算された後。 そして、readCpuStats関数の説明:

 vector<float> Info::readCpuStats() { vector<float> ret; ifstream stat_file("/proc/stat"); if (!stat_file.is_open()) { cout << "Unable to open /proc/stat" << std::endl; return ret; } int val; string tmp; stat_file >> tmp; for (int i = 0; i < 4; ++i) { stat_file >> val; ret.push_back(val); } stat_file.close(); return ret; }
      
      





この関数は、getCpuLoad関数と同じサイトから取得されました。 この関数では、ファイル/ proc / statから値を読み取り、それらをベクトルに入力してから、returnステートメントで返します。

これでInfoクラスが作成され、ProcessTableクラスの作成に進むことができます。 このクラスのフレームワークは次のとおりです。

 #ifndef PROCESSTABLE_H #define PROCESSTABLE_H #include <QtGui> #include <iostream> #include <fstream> using namespace std; class ProcessTable : public QWidget { Q_OBJECT public: explicit ProcessTable(QWidget *parent = 0); private: QTableWidget * table; QHBoxLayout* hlayout; QPushButton* button; QVBoxLayout* layout; public slots: void update(); void kill(); }; #endif // PROCESSTABLE_H
      
      





更新スロットはテーブルにデータを入力するために使用され、キルスロットはプロセスを終了するために使用されます。 このクラスの実装ファイルでは、まずスケルトンとともにファイルを含める必要があります。 次に、このクラスのコンストラクターの説明に戻ります。コンストラクターのコードは次のとおりです。

 ProcessTable::ProcessTable(QWidget *parent) : QWidget(parent) { hlayout = new QHBoxLayout; button = new QPushButton(""); button->setToolTip("       PID     \"\""); connect(button,SIGNAL(clicked()),this,SLOT(kill())); hlayout->addStretch(); hlayout->addWidget(button); layout = new QVBoxLayout; table = new QTableWidget; update(); layout->addWidget(table); layout->addLayout(hlayout); this->setLayout(layout); QTimer *timer = new QTimer; connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(4000); }
      
      





まず、グラフィック要素を初期化して構成し、「完了」ボタンからクリックされた信号をクラスのスロット-killに接続します。 また、更新スロットを呼び出してテーブルにデータを入力し、タイマーを使用してテーブルの更新を実装します。 更新スロットの説明は次のとおりです:

 void ProcessTable::update() { table->setColumnCount(2); table->setRowCount(0); QStringList list; list << "Name" << "PID"; table->setHorizontalHeaderLabels(list); QDir * dir = new QDir("/proc"); list = dir->entryList(QStringList("*"),QDir::AllDirs); foreach(QString str, list) { if(str.toInt()) { ifstream stream; stream.open("/proc/" + str.toAscii() + "/comm"); string s; getline(stream,s); int lastRow = table->rowCount(); QString icon = "/usr/share/icons/hicolor/32x32/apps/" + QString::fromStdString(s) + ".png"; QFile file(icon); table->insertRow(lastRow); table->setColumnWidth(0,150); if(!file.exists()) { icon = ":/binary.png"; } table->setItem(lastRow,0,new QTableWidgetItem(QPixmap(icon),QString::fromStdString(s))); table->setItem(lastRow,1,new QTableWidgetItem(str)); } else { continue; } } }
      
      





最初に、テーブルの列と行の数、および列の名前を設定します。 次に、/ procフォルダー内のディレクトリのリストを取得します。 テーブルを埋めるために、プロセスに属さないディレクトリをスキップして、リスト全体をループします。 ファイル/ proc / pid / commからプロセス名を取得します。 フォルダ/ usr / share / icons / hicolor / 32x32 / appsからアイコンを取得します。アイコンが存在しない場合は、バイナリに縫い付けられたbinary.png画像が使用されます。 これが写真です:

画像

ソースに縫い付けるために、私はrccを使用しました。MaxSchleeの著書「Qt-Professional C ++ Programming」でrccの使用について読むことができます。または、グーグルで検索することができます。

キルスロットの説明に進みます。コードは次のとおりです。

 void ProcessTable::kill() { QList<QTableWidgetItem*> list = table->selectedItems(); QTableWidgetItem* item = list.value(0); QString str = item->text(); QProcess::execute("kill", QStringList() << str); update(); }
      
      





プロセスを完了するには、すべてのLinuxにあるプログラムを使用します(Linuxにない場合は、どのようなLinuxなのか想像もできません)。QProcessクラスのexecuteメソッドを使用してこのプログラムを起動します。この関数の2番目の引数はパラメーターのリストですプログラムの場合、完了させるプロセスのPIDのみを渡し、このプログラムを呼び出した後、完了したプロセスがテーブルから消えるようにテーブルを更新します。 次に、2つのクラスを接続するSystemMonitorクラスの説明に戻ります。 このクラスのフレームワークは非常に簡単です。

 #ifndef SYSTEMMONITOR_H #define SYSTEMMONITOR_H #include <QtGui> #include "info.h" #include "processtable.h" class SystemMonitor : public QWidget { Q_OBJECT public: explicit SystemMonitor(QWidget *parent = 0); }; #endif // SYSTEMMONITOR_H
      
      





さて、このクラスにはメソッドが1つしかないため、ここで説明します。 このコンストラクターメソッドのコードを次に示します(実装ファイルの先頭にワイヤーフレームファイル接続を追加します)。

 SystemMonitor::SystemMonitor(QWidget *parent) : QWidget(parent) { QTabWidget * tab = new QTabWidget; Info* info = new Info; ProcessTable* pt = new ProcessTable; tab->addTab(info,"  "); tab->addTab(pt,""); QVBoxLayout * layout = new QVBoxLayout; layout->addWidget(tab); this->setLayout(layout); this->show(); }
      
      





コンストラクターでは、単にタブウィジェットを作成し、ウィジェットに情報を追加し、ウィジェットにテーブルを追加します。addTabメソッドはウィジェットを引数として受け入れるため、InfoおよびProcessTableクラスをQWidgetから継承しました。 そして最後に、プログラムの開始点、つまりmain.cppファイルを作成します。 彼のコードは次のとおりです。

 #include "systemmonitor.h" int main(int argc,char** argv) { QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); QApplication app(argc,argv); QStyle * style = QStyleFactory::create("Cleanlooks"); app.setStyle(style); SystemMonitor sys; return app.exec(); }
      
      





最初の3行により、プログラム内のロシア語の文字が正しく表示されます。 8行目と9行目は、どのシステムでもアプリケーションが同じように見えるようにします。

さて、これでシステムモニターの準備は完了です。



All Articles