📄 linux.c
字号:
/* GKrellM| Copyright (C) 1999-2006 Bill Wilson|| Author: Bill Wilson billw@gkrellm.net| Latest versions might be found at: http://gkrellm.net|| This program is free software which I release under the GNU General Public| License. You may redistribute and/or modify this program under the terms| of that license as published by the Free Software Foundation; either| version 2 of the License, or (at your option) any later version.|| This program is distributed in the hope that it will be useful,| but WITHOUT ANY WARRANTY; without even the implied warranty of| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the| GNU General Public License for more details. Version 2 is in the| COPYRIGHT file in the top level directory of this distribution.| | To get a copy of the GNU General Puplic License, write to the Free Software| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA*/#include <limits.h>#include <errno.h>#include <locale.h>#include <sys/types.h>#include <dirent.h>static gboolean need_locale_fix, have_diskstats, have_partition_stats, have_sysfs, have_sysfs_stats, have_sysfs_sensors;static gchar locale_decimal_point;static gboolean kernel_2_4, kernel_2_6;static gint os_major, os_minor, os_rev;static gbooleanos_release(gint major, gint minor, gint rev) { if ( os_major > major || (os_major == major && os_minor > minor) || (os_major == os_major && os_minor == minor && os_rev >= rev) ) return TRUE; return FALSE; }voidgkrellm_sys_main_init(void) { FILE *f; gchar buf[1024]; if ((f = fopen("/proc/sys/kernel/osrelease", "r")) != NULL) { if (fgets(buf, sizeof(buf), f) != NULL) sscanf(buf, "%d.%d.%d", &os_major, &os_minor, &os_rev); fclose(f); kernel_2_4 = os_release(2, 4, 0); kernel_2_6 = os_release(2, 6, 0); } /* Various stats are in sysfs since 2.5.47, but it may not be mounted. */ if ((f = fopen("/proc/mounts", "r")) != NULL) { while (fgets(buf, sizeof(buf), f) != NULL) if (strstr(buf, "sysfs")) { have_sysfs = TRUE; break; } fclose(f); } }voidgkrellm_sys_main_cleanup(void) { } /* Linux /proc always reports floats with '.' decimal points, but sscanf() | for some locales needs commas in place of periods. So, if current | locale doesn't use periods, must insert the correct decimal point char. */static voidlocale_fix(gchar *buf) { gchar *s; for (s = buf; *s; ++s) if (*s == '.') *s = locale_decimal_point; }/* ===================================================================== *//* CPU, disk, and swap monitor interfaces might all get data from /proc/stat| (depending on kernel version) so they will share reading of /proc/stat.*/#define PROC_STAT_FILE "/proc/stat"static gint n_cpus;static gulong swapin, swapout; /* CPU, and Disk monitors call this in their update routine. | Whoever calls it first will read the data for everyone. | | /proc/stat has cpu entries like: | cpu total_user total_nice total_sys total_idle | cpu0 cpu0_user cpu0_nice cpu0_sys cpu0_idle | ... | cpuN cpuN_user cpuN_nice cpuN_sys cpuN_idle | where ticks for cpu are jiffies * smp_num_cpus | and ticks for cpu[i] are jiffies (1/CLK_TCK) */static voidlinux_read_proc_stat(void) { static FILE *f; gint n, i, ncpu; gchar *s, buf[1024]; gulong rblk[4], wblk[4]; guint64 user, nice, sys, idle, iowait; static gint data_read_tick = -1; n = gkrellm_get_timer_ticks(); if (data_read_tick == n) return; /* Just one read per tick (multiple monitors call this) */ if (!f && (f = fopen(PROC_STAT_FILE, "r")) == NULL) return; data_read_tick = n; gkrellm_disk_reset_composite(); ncpu = 0; while ((fgets(buf, sizeof(buf), f)) != NULL) { if (buf[0] == 'c' && buf[1] == 'p') { n = sscanf(buf, "%*s %Lu %Lu %Lu %Lu %Lu", &user, &nice, &sys, &idle, &iowait); if (n == 5) /* iowait is new in kernel 2.5 */ idle += iowait; if (n_cpus > 1) { if (ncpu == 0) gkrellm_cpu_assign_composite_data(user, nice, sys, idle); else gkrellm_cpu_assign_data(ncpu - 1, user, nice, sys, idle); } /* If have cpu and cpu0 on single cpu machines, won't use cpu0. */ else if (ncpu == 0) gkrellm_cpu_assign_data(0, user, nice, sys, idle); ++ncpu; continue; } if (!kernel_2_6 && !strncmp("swap", buf, 4)) { sscanf(buf + 5, "%lu %lu", &swapin, &swapout); continue; } if (buf[0] != 'd') continue; if (!strncmp("disk_rblk", buf, 9)) { s = buf + 10; for (i = 0; i < 4; ++i) rblk[i] = strtoul(s, &s, 0); } else if (!strncmp("disk_wblk", buf, 9)) { s = buf + 10; for (i = 0; i < 4; ++ i) { wblk[i] = strtoul(s, &s, 0); gkrellm_disk_assign_data_nth(i, 512 * rblk[i], 512 * wblk[i], FALSE); } } else if (!strncmp("disk_io:", buf, 8)) /* Kernel 2.4 only */ { gint major, i_disk; gulong rblk, wblk, rb1, rb2, wb1, wb2; s = strtok(buf + 9, " \t\n"); while (s) { /* disk_io lines in 2.4.x kernels have had 2 formats */ n = sscanf(s, "(%d,%d):(%*d,%lu,%lu,%lu,%lu)", &major, &i_disk, &rb1, &rb2, &wb1, &wb2); if (n == 6) /* patched as of 2.4.0-test1-ac9 */ { /* (major,disk):(total_io,rio,rblk,wio,wblk) */ rblk = rb2; wblk = wb2; } else /* 2.3.99-pre8 to 2.4.0-testX */ { /* (major,disk):(rio,rblk,wio,wblk) */ rblk = rb1; wblk = wb1; } /* floppys and CDroms don't show up in /proc/partitions. */ if (have_partition_stats) { gchar name[32]; name[0] = '\0'; if (major == 2) sprintf(name, "fd%d", i_disk); else if (major == 11) sprintf(name, "scd%d", i_disk); if (name[0]) gkrellm_disk_assign_data_by_name(name, 512 * rblk, 512 * wblk, FALSE); } else { gkrellm_disk_assign_data_by_device(major, i_disk, 512 * rblk, 512 * wblk, (major == 9) ? TRUE : FALSE); } s = strtok(NULL, " \t\n"); } } } rewind(f); }/* ===================================================================== *//* CPU monitor interface */voidgkrellm_sys_cpu_read_data(void) { /* One routine reads cpu, disk, and swap data. All three monitors will | call it, but only the first call per timer tick will do the work. */ linux_read_proc_stat(); }gbooleangkrellm_sys_cpu_init(void) { FILE *f; gchar buf[1024]; if ((f = fopen(PROC_STAT_FILE, "r")) == NULL) return FALSE; while (fgets(buf, sizeof(buf), f)) { if (strncmp(buf, "cpu", 3) != 0) continue; ++n_cpus; } fclose(f); /* If multiple CPUs, the first one will be a composite. Report only real. */ if (n_cpus > 1) --n_cpus; gkrellm_cpu_set_number_of_cpus(n_cpus); return TRUE; }/* ===================================================================== *//* Disk monitor interface */#define PROC_PARTITIONS_FILE "/proc/partitions"#define PROC_DISKSTATS_FILE "/proc/diskstats"#include <linux/major.h>#if ! defined (SCSI_DISK0_MAJOR)#define SCSI_DISK0_MAJOR 8#endif#if ! defined (MD_MAJOR)#define MD_MAJOR 9#endif#if !defined(IDE4_MAJOR)#define IDE4_MAJOR 56#endif#if !defined(IDE5_MAJOR)#define IDE5_MAJOR 57#endif#if !defined(IDE6_MAJOR)#define IDE6_MAJOR 88#endif#if !defined(IDE7_MAJOR)#define IDE7_MAJOR 89#endif#if !defined(IDE8_MAJOR)#define IDE8_MAJOR 90#endif#if !defined(IDE9_MAJOR)#define IDE9_MAJOR 91#endif#if !defined(DAC960_MAJOR)#define DAC960_MAJOR 48#endif#if !defined(COMPAQ_SMART2_MAJOR)#define COMPAQ_SMART2_MAJOR 72#endif#if !defined(COMPAQ_CISS_MAJOR)#define COMPAQ_CISS_MAJOR 104#endif#if !defined(LVM_BLK_MAJOR)#define LVM_BLK_MAJOR 58#endif#if !defined(NBD_MAJOR)#define NBD_MAJOR 43#endifstruct _disk_name_map { gchar *name; gint major; gint minor_mod; gchar suffix_base; }; /* Disk charts will appear in GKrellM in the same order as this table. */static struct _disk_name_map disk_name_map[] = { {"hd", IDE0_MAJOR, 64, 'a' }, /* 3: hda, hdb */ {"hd", IDE1_MAJOR, 64, 'c' }, /* 22: hdc, hdd */ {"hd", IDE2_MAJOR, 64, 'e' }, /* 33: hde, hdf */ {"hd", IDE3_MAJOR, 64, 'g' }, /* 34: hdg, hdh */ {"hd", IDE4_MAJOR, 64, 'i' }, /* 56: hdi, hdj */ {"hd", IDE5_MAJOR, 64, 'k' }, /* 57: hdk, hdl */ {"hd", IDE6_MAJOR, 64, 'm' }, /* 88: hdm, hdn */ {"hd", IDE7_MAJOR, 64, 'o' }, /* 89: hdo, hdp */ {"hd", IDE8_MAJOR, 64, 'q' }, /* 90: hdq, hdr */ {"hd", IDE9_MAJOR, 64, 's' }, /* 91: hds, hdt */ {"sd", SCSI_DISK0_MAJOR, 16, 'a' }, /* 8: sda-sdh */ {"sg", SCSI_GENERIC_MAJOR, 16, '0' }, /* 21: sg0-sg16 */ {"scd", SCSI_CDROM_MAJOR, 16, '0' }, /* 11: scd0-scd16 */ {"sr", SCSI_CDROM_MAJOR, 16, '0' }, /* 11: sr0-sr16 */ {"md", MD_MAJOR, 0, '0' }, /* 9: md0-md3 */ {"c0d", DAC960_MAJOR, 32, '0' }, /* 48: c0d0-c0d31 */ {"c1d", DAC960_MAJOR + 1, 32, '0' }, /* 49: c1d0-c1d31 */ {"c2d", DAC960_MAJOR + 2, 32, '0' }, /* 50: c2d0-c2d31 */ {"c3d", DAC960_MAJOR + 3, 32, '0' }, /* 51: c3d0-c3d31 */ {"c4d", DAC960_MAJOR + 4, 32, '0' }, /* 52: c4d0-c4d31 */ {"c5d", DAC960_MAJOR + 5, 32, '0' }, /* 53: c5d0-c5d31 */ {"c6d", DAC960_MAJOR + 6, 32, '0' }, /* 54: c6d0-c6d31 */ {"c7d", DAC960_MAJOR + 7, 32, '0' }, /* 55: c7d0-c7d31 */ {"cs0d", COMPAQ_SMART2_MAJOR, 16, '0' }, /* 72: c0d0-c0d15 */ {"cs1d", COMPAQ_SMART2_MAJOR + 1, 16, '0' }, /* 73: c1d0-c1d15 */ {"cs2d", COMPAQ_SMART2_MAJOR + 2, 16, '0' }, /* 74: c2d0-c2d15 */ {"cs3d", COMPAQ_SMART2_MAJOR + 3, 16, '0' }, /* 75: c3d0-c3d15 */ {"cs4d", COMPAQ_SMART2_MAJOR + 4, 16, '0' }, /* 76: c4d0-c4d15 */ {"cs5d", COMPAQ_SMART2_MAJOR + 5, 16, '0' }, /* 77: c5d0-c5d15 */ {"cs6d", COMPAQ_SMART2_MAJOR + 6, 16, '0' }, /* 78: c6d0-c6d15 */ {"cs7d", COMPAQ_SMART2_MAJOR + 7, 16, '0' }, /* 79: c7d0-c7d15 */ {"cc0d", COMPAQ_CISS_MAJOR, 16, '0' }, /* 104: c0d0-c0d15 */ {"cc1d", COMPAQ_CISS_MAJOR + 1, 16, '0' }, /* 105: c1d0-c1d15 */ {"cc2d", COMPAQ_CISS_MAJOR + 2, 16, '0' }, /* 106: c2d0-c2d15 */ {"cc3d", COMPAQ_CISS_MAJOR + 3, 16, '0' }, /* 107: c3d0-c3d15 */ {"cc4d", COMPAQ_CISS_MAJOR + 4, 16, '0' }, /* 108: c4d0-c4d15 */ {"cc5d", COMPAQ_CISS_MAJOR + 5, 16, '0' }, /* 109: c5d0-c5d15 */ {"cc6d", COMPAQ_CISS_MAJOR + 6, 16, '0' }, /* 110: c6d0-c6d15 */ {"cc7d", COMPAQ_CISS_MAJOR + 7, 16, '0' }, /* 111: c7d0-c7d15 */ {"fd", FLOPPY_MAJOR, 0, '0' } /* 2: fd0-fd3 */ };static gbooleandisk_major_ok(gint major) { gint i; for (i = 0; i < sizeof(disk_name_map) / sizeof(struct _disk_name_map); ++i) { if (major == disk_name_map[i].major) return TRUE; } return FALSE; }gchar *gkrellm_sys_disk_name_from_device(gint device_number, gint unit_number, gint *order) { struct _disk_name_map *dm = NULL; gint i; gchar suffix; static gchar name[32]; for (i = 0; i < sizeof(disk_name_map) / sizeof(struct _disk_name_map); ++i) { if (device_number != disk_name_map[i].major) continue; dm = &disk_name_map[i]; break; } if (dm) { suffix = dm->suffix_base + unit_number; sprintf(name, "%s%c", dm->name, suffix); } else sprintf(name, "(%d,%d)", device_number, unit_number); *order = i; return name; }gintgkrellm_sys_disk_order_from_name(gchar *name) { struct _disk_name_map *dm, *dm_next; gint i, len, table_size; gchar suffix; table_size = sizeof(disk_name_map) / sizeof(struct _disk_name_map); for (i = 0; i < table_size; ++i) { dm = &disk_name_map[i]; len = strlen(dm->name); if (strncmp(dm->name, name, len)) continue; suffix = name[len]; /* So far looked at only for "hd" series */ if (i < table_size - 1) { dm_next = &disk_name_map[i + 1]; if ( !strcmp(dm_next->name, dm->name) && dm_next->suffix_base <= suffix ) continue; } break; } if (i >= table_size) i = -1; return i; } /* Given a /proc/partitions or /proc/diskstats line disk name in "partition", | make "disk" have the whole disk name (eg hda) and "partition" have the | partition (eg hda1) or NULL if not a partition. For simple names, | "disk" is expected to initially have the whole disk name from the | previous call (or NULL if this is the first call per /proc file parse). */static gbooleandisk_get_device_name(gint major, gint minor, gchar *disk, gchar *partition) { struct _disk_name_map *dm = NULL; gint i, unit = 0; gchar *p, *d; for (p = partition; *p; ++p) if (*p == '/') break; if (!*p) { /* Have a simple name like hda, hda1, sda, ... */ d = disk; p = partition; while (*d && *p && *d++ == *p++) ; if (d == disk || *d || *p < '0' || *p > '9') { strcpy(disk, partition); partition[0] = '\0'; } return TRUE; } /* Have a devfs name like ide/host0/bus0/target0/lun0/part1, so construct | a name based on major, minor numbers and the disk_name_map[]. */ for (i = 0; i < sizeof(disk_name_map) / sizeof(struct _disk_name_map); ++i) { if (major != disk_name_map[i].major) continue; dm = &disk_name_map[i]; if (dm->minor_mod > 0 && minor >= dm->minor_mod) { unit = minor / dm->minor_mod; minor = minor % dm->minor_mod; } sprintf(disk, "%s%c", dm->name, dm->suffix_base + unit); if (minor > 0) sprintf(partition, "%s%d", disk, minor); else partition[0] = '\0'; return TRUE; } return FALSE; } /* Kernels >= 2.5.69 have /proc/diskstats which can be more efficient to | read than getting stats from sysfs. See: | /usr/src/linux/Documentation/iostats.txt | But gkrellm calls this only for 2.6+ kernels since there were some | format incompatible /proc/diskstats patches for 2.4. */static voidlinux_read_proc_diskstats(void)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -