日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

top命令源码分析

發布時間:2023/12/3 综合教程 26 生活家
生活随笔 收集整理的這篇文章主要介紹了 top命令源码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

閱讀top命令源碼

課設要求實現一個系統監控器,本來打算在mac上實現一個類似activity monitor的程序,但似乎mac系統并沒有這么開源。還是linux系統下的/proc文件系統方便。
看了一部分top命令的實現(看了兩三個小時頭都大了。。。),稍微記錄一下,便于未來翻閱。

top.c

首先查看main函數前面的配置部分函數,主要是窗口初始化和對命令對選項解析。
之前以為應該只需要一個窗口,不過代碼中初始化了四個窗口,以便于后面的擴展
(沒有了解過top命令的用法,但是猜測這里應該是可以通過加參數擴展)

// Set up the raw/incomplete field group windows --
// they'll be finished off after startup completes.
// [ and very likely that will override most/all of our efforts ]
// [               --- life-is-NOT-fair ---                     ]
static void windows_stage1 (void)
{WIN_t *w;int i;for (i = 0; i < GROUPSMAX; i++) {w = &Winstk[i];w->winnum = i + 1;w->rc = Rc.win[i];w->captab[0] = Cap_norm;w->captab[1] = Cap_norm;w->captab[2] = w->cap_bold;w->captab[3] = w->capclr_sum;w->captab[4] = w->capclr_msg;w->captab[5] = w->capclr_pmt;w->captab[6] = w->capclr_hdr;w->captab[7] = w->capclr_rowhigh;w->captab[8] = w->capclr_rownorm;w->next = w + 1;w->prev = w - 1;++w;}/* fixup the circular chains... */Winstk[3].next = &Winstk[0];Winstk[0].prev = &Winstk[3];Curwin = Winstk;
}

看了注釋小哥這說話風格有點不確認自己下的是不是官方源碼了。。。

再來看下輸出格式設計,用結構體的模式有利于未來擴展和復用。

// This structure consolidates the information that's used
// in a variety of display roles.
typedef struct FLD_t {const char    keys [4];      // order: New-on New-off Old-on Old-off// misaligned on 64-bit, but part of a table -- oh wellconst char   *head;          // name for col heads + toggle/reorder fieldsconst char   *fmts;          // sprintf format string for field displayconst int     width;         // field width, if applicableconst int     scale;         // scale_num type, if applicableconst QFP_t   sort;          // sort functionconst char   *desc;          // description for toggle/reorder fieldsconst int     lflg;          // PROC_FILLxxx flag(s) needed by this field
} FLD_t;

具體在top指令中的實現格式如下,數組組織形式:

static FLD_t Fieldstab[] = {
/* .lflg anomolies:P_UID, L_NONE  - natural outgrowth of 'stat()' in readproc        (euid)P_CPU, L_stat  - never filled by libproc, but requires times      (pcpu)P_CMD, L_stat  - may yet require L_CMDLINE in reframewins  (cmd/cmdline)L_EITHER       - must L_status, else 64-bit math, __udivdi3 on 32-bit !keys   head           fmts     width   scale  sort   desc                     lflg------  -----------    -------  ------  -----  -----  ----------------------   -------- */{ "AaAa", "   PID",      " %5u",     -1,    -1, SF(PID), "Process Id",           L_NONE   },{ "BbBb", "  PPID",      " %5u",     -1,    -1, SF(PPD), "Parent Process Pid",   L_EITHER },{ "CcQq", " RUSER   ",   " %-8.8s",  -1,    -1, SF(URR), "Real user name",       L_RUSER  },{ "DdCc", "  UID",       " %4u",     -1,    -1, SF(UID), "User Id",              L_NONE   },{ "EeDd", " USER    ",   " %-8.8s",  -1,    -1, SF(URE), "User Name",            L_EUSER  },{ "FfNn", " GROUP   ",   " %-8.8s",  -1,    -1, SF(GRP), "Group Name",           L_GROUP  },{ "GgGg", " TTY     ",   " %-8.8s",   8,    -1, SF(TTY), "Controlling Tty",      L_stat   },{ "HhHh", "  PR",        " %3d",     -1,    -1, SF(PRI), "Priority",             L_stat   },{ "IiIi", "  NI",        " %3d",     -1,    -1, SF(NCE), "Nice value",           L_stat   },{ "JjYy", " #C",         " %2u",     -1,    -1, SF(CPN), "Last used cpu (SMP)",  L_stat   },{ "KkEe", " %CPU",       " %#4.1f",  -1,    -1, SF(CPU), "CPU usage",            L_stat   },{ "LlWw", "   TIME",     " %6.6s",    6,    -1, SF(TME), "CPU Time",             L_stat   },{ "MmRr", "    TIME+ ",  " %9.9s",    9,    -1, SF(TME), "CPU Time, hundredths", L_stat   },{ "NnFf", " %MEM",       " %#4.1f",  -1,    -1, SF(RES), "Memory usage (RES)",   L_statm  },{ "OoMm", "  VIRT",      " %5.5s",    5, SK_Kb, SF(VRT), "Virtual Image (kb)",   L_statm  },{ "PpOo", " SWAP",       " %4.4s",    4, SK_Kb, SF(SWP), "Swapped size (kb)",    L_statm  },{ "QqTt", "  RES",       " %4.4s",    4, SK_Kb, SF(RES), "Resident size (kb)",   L_statm  },{ "RrKk", " CODE",       " %4.4s",    4, SK_Kb, SF(COD), "Code size (kb)",       L_statm  },{ "SsLl", " DATA",       " %4.4s",    4, SK_Kb, SF(DAT), "Data+Stack size (kb)", L_statm  },{ "TtPp", "  SHR",       " %4.4s",    4, SK_Kb, SF(SHR), "Shared Mem size (kb)", L_statm  },{ "UuJj", " nFLT",       " %4.4s",    4, SK_no, SF(FLT), "Page Fault count",     L_stat   },{ "VvSs", " nDRT",       " %4.4s",    4, SK_no, SF(DRT), "Dirty Pages count",    L_statm  },{ "WwVv", " S",          " %c",      -1,    -1, SF(STA), "Process Status",       L_EITHER },// next entry's special: '.head' will be formatted using table entry's own//                       '.fmts' plus runtime supplied conversion args!{ "XxXx", " COMMAND",    " %-*.*s",  -1,    -1, SF(CMD), "Command name/line",    L_EITHER },{ "YyUu", " WCHAN    ",  " %-9.9s",  -1,    -1, SF(WCH), "Sleeping in Function", L_stat   },// next entry's special: the 0's will be replaced with '.'!{ "ZzZz", " Flags   ",   " %08lx",   -1,    -1, SF(FLG), "Task Flags <sched.h>", L_stat   },
#if 0{ "..Qq", "   A",        " %4.4s",    4, SK_no, SF(PID), "Accessed Page count",  L_stat   },{ "..Nn", "  TRS",       " %4.4s",    4, SK_Kb, SF(PID), "Code in memory (kb)",  L_stat   },{ "..Rr", "  WP",        " %4.4s",    4, SK_no, SF(PID), "Unwritable Pages",     L_stat   },{ "Jj[{", " #C",         " %2u",     -1,    -1, SF(CPN), "Last used cpu (SMP)",  L_stat   },{ "..\\|"," Bad",        " %2u",     -1,    -1, SF(CPN), "-- must ignore | --",  0        },{ "..]}", " Bad",        " %2u",     -1,    -1, SF(CPN), "-- not used --",       0        },{ "..^~", " Bad",        " %2u",     -1,    -1, SF(CPN), "-- not used --",       0        },
#endif
};

main函數在完成配置和初始化后進入loop,其中有一個函數frame_make()

for (;;) {if (need_resize){need_resize = 0;wins_resize();}frame_make();

大致上是先獲取了信息再放入窗口顯示。那么這個summary_show()就是核心函數了。

static void frame_make (void)
{proc_t **ppt;int i, scrlins;// note: all libproc flags are managed by//       reframewins(), who also builds each window's column headersif (!Frames_libflags) {reframewins();memset(Pseudo_scrn, '\0', Pseudo_size);}Pseudo_row = Msg_row = scrlins = 0;ppt = summary_show();Max_lines = (Screen_rows - Msg_row) - 1;if (CHKw(Curwin, EQUWINS_cwo))wins_reflag(Flags_OFF, EQUWINS_cwo);// sure hope each window's columns header begins with a newline...putp(tg2(0, Msg_row));if (!Rc.mode_altscr) {// only 1 window to show so, piece o' cakeCurwin->winlines = Curwin->rc.maxtasks;window_show(ppt, Curwin, &scrlins);} else {// maybe NO window is visible but assume, pieces o' cakesfor (i = 0 ; i < GROUPSMAX; i++) {if (CHKw(&Winstk[i], VISIBLE_tsk)) {framehlp(i, Max_lines - scrlins);window_show(ppt, &Winstk[i], &scrlins);}if (Max_lines <= scrlins) break;}}// clear to end-of-screen (critical if last window is 'idleps off'),// then put the cursor in-its-place, and rid us of any prior frame's msg// (main loop must iterate such that we're always called before sleep)PUTT("%s%s%s%s",scrlins < Max_lines ? "\n"        : "",scrlins < Max_lines ? Cap_clr_eos : "",tg2(0, Msg_row),Cap_clr_eol);fflush(stdout);
}

當然window_show()也有一個要注意的地方,top命令不是顯示所有進程,只顯示部分的前提下需要顯示對當前系統最重要的那部分,所以需要進行排序再輸出。排序很簡單,直接調用qsort即可,但應該按照哪種信息排序呢?

qsort(ppt, Frame_maxtask, sizeof(proc_t *), Fieldstab[q->rc.sortindx].sort);

這里的實現是可擴展的(可見應該可以通過調用時添加選項控制排序方式),可以看到在上面格式化控制的表格中也有排序信息,每個sortindx對應一種指標,默認配置存放在系統配置文件中,在初始化時讀入。

#define SYS_RCFILESPEC  "/etc/toprc"// read part of an old-style config in /etc/toprc
fd = open(SYS_RCFILESPEC, O_RDONLY);

summary_show()函數,逐個重點來看

      // sleep for half a secondtv.tv_sec = 0;tv.tv_usec = 500000;select(0, NULL, NULL, NULL, &tv);  // ought to loop until done

select函數這個我乍一眼沒想起來是哪用過,看注釋明白功能后想起來Socket編程里面用過這個函數。

//等待用戶連接請求或用戶數據到來或會話socke可發送數據
if((this->numOfSocketSignaled = select(0,&this->rfds,&this->wfds,NULL,NULL)) == SOCKET_ERROR){ //select函數返回有可讀或可寫的socket的總數,保存在rtn里.最后一個參數設定等待時間,如為NULL則為阻塞模式cout << "select() failed with error!\n";return -1;
}

誒我這個菜雞。。。腦子里只想到用sleep函數去實現。。。
然后是核心的進程函數更新

 p_table = procs_refresh(p_table, Frames_libflags);

而在更新進程的眾多調用函數中核心的又是

if (unlikczely(!(ptsk = readproc(PT, table[curmax])))) break;

如此跳轉到

readproc.c

//
/* readproc: return a pointer to a proc_t filled with requested info about the
* next process available matching the restriction set.  If no more such
* processes are available, return a null pointer (boolean false).  Use the
* passed buffer instead of allocating space if it is non-NULL.  *//* This is optimized so that if a PID list is given, only those files are
* searched for in /proc.  If other lists are given in addition to the PID list,
* the same logic can follow through as for the no-PID list case.  This is
* fairly complex, but it does try to not to do any unnecessary work.
*/
proc_t* readproc(PROCTAB *restrict const PT, proc_t *restrict p) {proc_t *ret;proc_t *saved_p;PT->did_fake=0;
//  if (PT->taskdir) {
//    closedir(PT->taskdir);
//    PT->taskdir = NULL;
//    PT->taskdir_user = -1;
//  }saved_p = p;if(!p) p = xcalloc(p, sizeof *p); /* passed buf or alloced mem */for(;;){// fills in the path, plus p->tid and p->tgidif (unlikely(! PT->finder(PT,p) )) goto out;// go read the process dataret = PT->reader(PT,p);if(ret) return ret;}out:if(!saved_p) free(p);// FIXME: maybe set tid to -1 here, for "-" in display?return NULL;
}

finderreader都是默認設置
查看其源碼發現似乎非常簡單。。。似乎沒有很特別的地方了。

//
// This finds tasks in /proc/*/task/ in the traditional way.
// Return non-zero on success.
static int simple_nexttid(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {static struct direct *ent;		/* dirent handle */if(PT->taskdir_user != p->tgid){if(PT->taskdir){closedir(PT->taskdir);}// use "path" as some tmp spacesnprintf(path, PROCPATHLEN, "/proc/%d/task", p->tgid);PT->taskdir = opendir(path);if(!PT->taskdir) return 0;PT->taskdir_user = p->tgid;}for (;;) {ent = readdir(PT->taskdir);if(unlikely(unlikely(!ent) || unlikely(!ent->d_name))) return 0;if(likely( likely(*ent->d_name > '0') && likely(*ent->d_name <= '9') )) break;}t->tid = strtoul(ent->d_name, NULL, 10);t->tgid = p->tgid;t->ppid = p->ppid;  // cover for kernel behavior? we want both actually...?snprintf(path, PROCPATHLEN, "/proc/%d/task/%s", p->tgid, ent->d_name);return 1;
}
//
// This reads /proc/*/task/* data, for one task.
// p is the POSIX process (task group summary) (not needed by THIS implementation)
// t is the POSIX thread (task group member, generally not the leader)
// path is a path to the task, with some room to spare.
static proc_t* simple_readtask(PROCTAB *restrict const PT, const proc_t *restrict const p, proc_t *restrict const t, char *restrict const path) {static struct stat sb;		// stat() bufferstatic char sbuf[1024];	// buffer for stat,statmunsigned flags = PT->flags;//printf("hhh\n");if (unlikely(stat(path, &sb) == -1))	/* no such dirent (anymore) */goto next_task;//    if ((flags & PROC_UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
//	goto next_task;			/* not one of the requested uids */t->euid = sb.st_uid;			/* need a way to get real uid */t->egid = sb.st_gid;			/* need a way to get real gid *///printf("iii\n");if (flags & PROC_FILLSTAT) {         /* read, parse /proc/#/stat */if (unlikely( file2str(path, "stat", sbuf, sizeof sbuf) == -1 ))goto next_task;			/* error reading /proc/#/stat */stat2proc(sbuf, t);				/* parse /proc/#/stat */}if (unlikely(flags & PROC_FILLMEM)) {	/* read, parse /proc/#/statm */
#if 0if (likely( file2str(path, "statm", sbuf, sizeof sbuf) != -1 ))statm2proc(sbuf, t);		/* ignore statm errors here */
#elset->size     = p->size;t->resident = p->resident;t->share    = p->share;t->trs      = p->trs;t->lrs      = p->lrs;t->drs      = p->drs;t->dt       = p->dt;
#endif}						/* statm fields just zero */if (flags & PROC_FILLSTATUS) {         /* read, parse /proc/#/status */if (likely( file2str(path, "status", sbuf, sizeof sbuf) != -1 )){status2proc(sbuf, t, 0);}}/* some number->text resolving which is time consuming */if (flags & PROC_FILLUSR){memcpy(t->euser,   user_from_uid(t->euid), sizeof t->euser);if(flags & PROC_FILLSTATUS) {memcpy(t->ruser,   user_from_uid(t->ruid), sizeof t->ruser);memcpy(t->suser,   user_from_uid(t->suid), sizeof t->suser);memcpy(t->fuser,   user_from_uid(t->fuid), sizeof t->fuser);}}/* some number->text resolving which is time consuming */if (flags & PROC_FILLGRP){memcpy(t->egroup, group_from_gid(t->egid), sizeof t->egroup);if(flags & PROC_FILLSTATUS) {memcpy(t->rgroup, group_from_gid(t->rgid), sizeof t->rgroup);memcpy(t->sgroup, group_from_gid(t->sgid), sizeof t->sgroup);memcpy(t->fgroup, group_from_gid(t->fgid), sizeof t->fgroup);}}#if 0if ((flags & PROC_FILLCOM) || (flags & PROC_FILLARG))	/* read+parse /proc/#/cmdline */t->cmdline = file2strvec(path, "cmdline");elset->cmdline = NULL;if (unlikely(flags & PROC_FILLENV))			/* read+parse /proc/#/environ */t->environ = file2strvec(path, "environ");elset->environ = NULL;
#elset->cmdline = p->cmdline;  // better not free these until done with all threads!t->environ = p->environ;
#endift->ppid = p->ppid;  // ought to put the per-task ppid somewherereturn t;
next_task:return NULL;
}

后面便是系統信息的函數了

sysinfo.c

// Display Uptime and Loadavgif (CHKw(Curwin, View_LOADAV)) {if (!Rc.mode_altscr) {show_special(0, fmtmk(LOADAV_line, Myname, sprint_uptime()));} else {show_special(0,fmtmk(CHKw(Curwin, VISIBLE_tsk) ? LOADAV_line_alt : LOADAV_line,Curwin->grpname,sprint_uptime()));}Msg_row += 1;}// Display Task and Cpu(s) Statesif (CHKw(Curwin, View_STATES)) {show_special(0,fmtmk(STATES_line1,Frame_maxtask, Frame_running, Frame_sleepin, Frame_stopped, Frame_zombied));Msg_row += 1;smpcpu = cpus_refresh(smpcpu);if (CHKw(Curwin, View_CPUSUM)) {// display just the 1st /proc/stat linesummaryhlp(&smpcpu[Cpu_tot], "Cpu(s):");} else {int i;char tmp[SMLBUFSIZ];// display each cpu's states separatelyfor (i = 0; i < Cpu_tot; i++) {snprintf(tmp, sizeof(tmp), "Cpu%-3d:", smpcpu[i].id);summaryhlp(&smpcpu[i], tmp);}}}// Display Memory and Swap statsmeminfo();if (CHKw(Curwin, View_MEMORY)) {show_special(0, fmtmk(MEMORY_line1, kb_main_total, kb_main_used, kb_main_free, kb_main_buffers));show_special(0, fmtmk(MEMORY_line2, kb_swap_total, kb_swap_used, kb_swap_free, kb_main_cached));Msg_row += 2;}

大概看了一遍功能函數,解析字符串–>在表中對應位置轉化數據存入–>計算
還是沒啥復雜的,不過這個寫法值得借鑒一哈,FILE_TO_BUF,我在課設中寫的是直接對文件流進行操作的,而不是將文件流信息全部讀入buf(而且是公有的)再去解析。

  const int mem_table_count = sizeof(mem_table)/sizeof(mem_table_struct);FILE_TO_BUF(MEMINFO_FILE,meminfo_fd);kb_inactive = ~0UL;head = buf;for(;;){tail = strchr(head, ':');if(!tail) break;*tail = '\0';//超過緩存區大小(應該是一種特殊情況,和任務名的大小無關)if(strlen(head) >= sizeof(namebuf)){head = tail+1;goto nextline;}strcpy(namebuf,head);//二分法檢索出對應名字found = bsearch(&findme, mem_table, mem_table_count,sizeof(mem_table_struct), compare_mem_table_structs);head = tail+1;if(!found) goto nextline;//將:后的數據轉為長整型輸出,同時將tail更新(0)*(found->slot) = strtoul(head,&tail,10);
nextline:tail = strchr(head, '\n');if(!tail) break;head = tail+1;}if(!kb_low_total){  /* low==main except with large-memory support */kb_low_total = kb_main_total;kb_low_free  = kb_main_free;}if(kb_inactive==~0UL){kb_inactive = kb_inact_dirty + kb_inact_clean + kb_inact_laundry;}kb_swap_used = kb_swap_total - kb_swap_free;kb_main_used = kb_main_total - kb_main_free;
}

并沒有全看完這些源碼。。。整體框架大概看了一下,寫法是不錯,用c寫工具–還是得有很多底層知識才知道怎樣減少開銷和維護的。同時這些代碼的架構真的很厲害,值得學習。


= =還是太naive了,發現可以直接用readproc函數獲取進程所有信息,以為課設不能這么干呢,原來是要求這么干的。這樣top指令所用的頭文件系統函數似乎都可以直接調用(這樣想來還是很科學的,時空效率上就有了更高的保障)。

總結

以上是生活随笔為你收集整理的top命令源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。