📄 cpud.c
字号:
/* * Copyright (c) 2004 Alf Schlichting a.schlichting@lemarit.com * Copyright (c) 2004 Laurence Tratt * http://tratt.net/laurie/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */#include <sys/types.h>#include <sys/param.h>#include <sys/stat.h>#include <sys/un.h>#include <fcntl.h>#include <sys/sysctl.h>#include <sys/dkstat.h>#include <sys/socket.h>#include <string.h>#include <poll.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <math.h>#include <err.h>#include <errno.h>#include <time.h>#include <syslog.h>#include <stdarg.h>#include <sys/ioctl.h>#include <machine/apmvar.h>#include "common.h"#include "pathnames.h"#include "util.h"/* This value is the threshold CPU utilization has to reach to force cpud to increase system performance; if CPU utilization falls below this value then system performance is decreased. */#define ACTIVITY_THRESHOLD 80/* This is the value that hw.setperf is incremented when necessary. */#define PERF_INCREMENT 15/* This is the value that hw.setperf is decremented when necessary. */#define PERF_DECREMENT 15/* Sleep interval is in microseconds. */#define SLEEP_INTERVAL 50000/* setperf can sometimes return an incorrect figure (e.g. after a machine has woken from hibernation, the hardware may put the processor in high power mode, but setperf may still be set to 0). Every n cycles we update setperf regardless of what value it claims to be. */#define FORCE_SET_PERF_UPDATE 20/* APM check is only exceuted every n cycles. */#define APM_CHECK_INTERVAL 25int debug = 0;extern char *__progname;void usage(void);static int create_socket(char *);voidusage(void){ fprintf(stderr, "usage: %s [-d]\n", __progname); exit(1);}static intcreate_socket(char *path){ int s; struct sockaddr_un sun; if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) err(1, "socket()"); memset(&sun, 0, sizeof (sun)); sun.sun_family = AF_UNIX; if (strlcpy(sun.sun_path, path, sizeof (sun.sun_path)) >= sizeof (sun.sun_path)) errx(1, "Path too long: %s", path); if (bind(s, (struct sockaddr*) &sun, sizeof(sun)) == -1) err(1, "bind()"); if (chmod(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == -1) err(1, "chmod()"); if (listen(s, 5) == -1) err(1, "listen()"); return s;}intmain(int argc, char *argv[]){ struct perf_command *pc_new, *pc_cur; struct apm_power_info apm_info; char line[SOCKET_LINE_LEN]; /* socket read buf */ char *sockpath; static int cp_time_mib[] = {CTL_KERN, KERN_CPTIME}; static int hw_perf_mib[] = {CTL_HW, HW_SETPERF}; long cp_time[CPUSTATES]; long cp_idletime_old, diff; size_t size; uid_t uid; gid_t gid; int max_perf, new_perf, perf, next_apm_check; int active; int fcn_val; /* fcntl value */ int next_forced_setperf; int scheme_isset; /* do we have a scheme*/ int treshhold; /* When to change cpu_rate */ int stepup, stepdown; /* how much change */ int sleeptime; /* loop sleep value */ int is_said; /* did we tell syslog*/ int ch, fd, fds, s, r, plen; /* Initialize pc_cur in case we are asked for our current scheme when * we haven't loaded one */ sockpath = PATH_CPUD_UNIX_SOCKET; pc_cur = calloc(1, sizeof(struct perf_command)); pc_new = NULL; plen = sizeof(struct perf_command); /* Force APM's state to be read on the first iteration */ next_apm_check = 0; sleeptime = SLEEP_INTERVAL; treshhold = ACTIVITY_THRESHOLD; stepup = PERF_INCREMENT; stepdown = PERF_DECREMENT; max_perf = 100; is_said = 0; scheme_isset = 0; next_forced_setperf = FORCE_SET_PERF_UPDATE; while ((ch = getopt(argc, argv, "d")) != -1) { switch(ch) { case 'd': debug = 1; break; case '?': default: usage(); } } size = sizeof(cp_time); if (sysctl(cp_time_mib, 2, &cp_time, &size, NULL, 0) < 0) err(1, "sysctl kern.cp_time failed"); cp_idletime_old = cp_time[CP_IDLE]; if ((fd = open(PATH_APM_NORMAL, O_RDONLY)) == -1) err(1, "opening /dev/apm failed"); s = create_socket(sockpath); if ((fcn_val = fcntl(s, F_GETFL, O_NONBLOCK)) == -1) err(1, NULL); if (fcntl(s, F_SETFL, fcn_val | O_NONBLOCK) == -1) err(1, "Couldn't set socket to NONBLOCK mode"); if (!debug) { daemon(0, 1); openlog("cpud", LOG_PID, LOG_DAEMON); syslog(LOG_INFO, "starting..."); } for (;;) { usleep(sleeptime); if ((fds = accept(s, NULL, NULL)) == -1) { if (errno != EAGAIN) { syslog(LOG_ERR, "accept failed: %m"); exit(1); } } else { if ((r = read_sock(fds, line, plen)) != plen) { syslog(LOG_ERR, "read failed: %m, exiting"); exit(1); } if (debug) fprintf(stdout, "cpud: read %d bytes\n", r); else { getpeereid(fds, &uid, &gid); syslog(LOG_INFO, "Connection from UID %d: " "GID %d",\ uid, gid); } pc_new = (struct perf_command *)line; if (pc_new->command == SET_SCHEME) { memcpy(pc_cur, pc_new, plen); scheme_isset = 1; stepup = pc_cur->s.stepup; stepdown = pc_cur->s.stepdown; sleeptime = pc_cur->s.sleep; treshhold = pc_cur->s.treshhold; syslog(LOG_INFO, "Loaded scheme %s", \ pc_cur->s.name); } else { if ((r = write_sock(fds, (char *)pc_cur, \ plen)) != plen) { if (debug) warn("write"); syslog(LOG_ERR, \ "write failed: %m, exiting"); exit(1); } } } /* Look if we have a scheme else do nothing */ if (!scheme_isset) continue; /* Checking the APM state is an expensive operation which we * only do every APM_CHECK_INTERVAL cycles. */ if (next_apm_check == 0) { if (ioctl(fd, APM_IOC_GETPOWER, &apm_info) == -1) { syslog(LOG_WARNING, "ioctl apm failed"); continue; } next_apm_check = APM_CHECK_INTERVAL; } else { next_apm_check -= 1; } if (apm_info.ac_state == APM_AC_ON) { if (apm_info.battery_state == APM_BATT_CHARGING) new_perf = pc_cur->s.ac_con_charging.\ cpu_rate_full_power; else new_perf = pc_cur->s.ac_con.cpu_rate_full_power; } else { if (apm_info.battery_life >= pc_cur-> \ s.ac_discon.borderline) { max_perf = pc_cur-> \ s.ac_discon.cpu_rate_full_power; is_said = 0; } else { max_perf = pc_cur-> \ s.ac_discon.cpu_rate_low_power; if (!is_said) { syslog(LOG_INFO, "Reached borderline " "at %d%%: Switching to %d", \ pc_cur->s.ac_discon.borderline, \ max_perf); is_said = 1; } } } size = sizeof(int); if (sysctl(hw_perf_mib, 2, &perf, &size, NULL, 0) < 0) { syslog(LOG_WARNING, "sysctl hw.setperf failed"); continue; } new_perf = perf; /* Read the processor stats. */ size = sizeof(cp_time); if (sysctl(cp_time_mib, 2, &cp_time, &size, NULL, 0) < 0) { syslog(LOG_WARNING, "sysctl kern.cp_time failed"); continue; } /* active is set to the percentage of processor time spent perfoming * any kind of activity since the last time we polled. */ diff = (cp_time[CP_IDLE] - cp_idletime_old); if (diff == 0) active = 100; else active = 100 - (((diff * 1000 + (diff / 2l)) / \ diff) / 10); cp_idletime_old = cp_time[CP_IDLE]; if (active > treshhold) new_perf = perf + stepup; else if (active < treshhold) new_perf = perf - stepdown; /* Enforce the upper and lower bounds. */ if (new_perf < 0) new_perf = 0; else if (new_perf > max_perf) new_perf = max_perf; /* Because we can't always trust the value given to us for setperf, * every FORCE_SET_PERF_UPDATE cycles we forcibly update its value, * even if we don't think it's changed. */ next_forced_setperf--; if (new_perf == perf && next_forced_setperf > 0) continue; else if (next_forced_setperf == 0) next_forced_setperf = FORCE_SET_PERF_UPDATE; if (debug) fprintf(stderr, "setting perf to %d from %d [max %d]\n", new_perf, perf, max_perf); size = sizeof(int); if (sysctl(hw_perf_mib, 2, &perf, &size, &new_perf, \ sizeof(int)) < 0) { syslog(LOG_WARNING, "sysctl hw.setperf failed"); continue; } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -