up cpulimit 0.3.2

This commit is contained in:
SirPdboy 2022-10-12 12:57:37 +08:00 committed by GitHub
parent 6fd4d1c241
commit 91276a6f01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 158 additions and 139 deletions

View File

@ -1,9 +1,8 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=cpulimit PKG_NAME:=cpulimit
PKG_VERSION:=0.2 PKG_VERSION:=0.3.2
PKG_RELEASE:=1 PKG_RELEASE:=$(AUTORELEASE)
#PKG_REV:=cabeb9947ccddd9a6e6ba14503e2a33063ac1b21
#PKG_SOURCE_PROTO:=git #PKG_SOURCE_PROTO:=git
#PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 #PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
@ -16,8 +15,8 @@ include $(INCLUDE_DIR)/package.mk
define Package/cpulimit define Package/cpulimit
SECTION:=utils SECTION:=utils
CATEGORY:=Utilities CATEGORY:=Utilities
TITLE:=cpulimit TITLE:=CPU usage limiter
URL:=https://github.com/opsengine/cpulimit.git URL:=https://github.com/denji/cpulimit
endef endef
define Build/Prepare define Build/Prepare

View File

@ -1,9 +1,10 @@
BINDIR ?= $(PREFIX)/bin BINDIR ?= $(PREFIX)/bin
BIN_TARGET = cpulimit BIN_TARGET = cpulimit
CC ?= gcc CC ?= gcc
CFLAGS ?= -O2 -Wall -g -D_GNU_SOURCE CFLAGS ?= -Os -Wall -g -D_GNU_SOURCE
HELP2MAN := $(shell which help2man) HELP2MAN := $(shell which help2man)
LIBS = list.o process_iterator.o process_group.o LIBS = list.o process_iterator.o process_group.o
MANDIR ?= $(PREFIX)/share/man/man1
MAN_NAME := "CPU Utilization Limiter" MAN_NAME := "CPU Utilization Limiter"
MAN_SEC := 1 MAN_SEC := 1
MAN_SRC := $(shell git remote get-url origin) MAN_SRC := $(shell git remote get-url origin)
@ -20,14 +21,21 @@ endif
all:: $(TARGETS) $(LIBS) all:: $(TARGETS) $(LIBS)
static: static: $(TARGETS) $(LIBS)
$(MAKE) all CFLAGS="$(CFLAGS) --static" $(MAKE) all CFLAGS="$(CFLAGS) --static" CC="musl-gcc"
touch static
strip: $(BIN_TARGET) strip: $(BIN_TARGET)
$(STRIP) $< $(STRIP) $<
touch strip
install: $(TARGETS) install: $(TARGETS)
$(SUDO) install $^ $(BINDIR) $(SUDO) install -D $(BIN_TARGET) $(BINDIR)/$(BIN_TARGET)
$(SUDO) install -D $(MAN_TARGET) $(MANDIR)/$(MAN_TARGET)
touch install
static_install: static strip install
touch static_install
$(BIN_TARGET): cpulimit.c $(LIBS) $(BIN_TARGET): cpulimit.c $(LIBS)
$(CC) -o cpulimit cpulimit.c $(LIBS) $(CFLAGS) $(CC) -o cpulimit cpulimit.c $(LIBS) $(CFLAGS)
@ -45,5 +53,5 @@ process_group.o: process_group.c process_group.h process_iterator.o list.o
$(CC) -c process_group.c $(CFLAGS) $(CC) -c process_group.c $(CFLAGS)
clean: clean:
rm -f *~ *.o $(TARGETS) rm -f *~ *.o static strip install static_install $(TARGETS)

View File

@ -23,7 +23,7 @@
* This is a simple program to limit the cpu usage of a process * This is a simple program to limit the cpu usage of a process
* If you modify this code, send me a copy please * If you modify this code, send me a copy please
* *
* Get the latest version at: http://github.com/opsengine/cpulimit * Get the latest version at: http://github.com/denji/cpulimit
* *
*/ */
@ -38,8 +38,11 @@
#include <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
/* This breaks under musl and isn't necessary under glibc
#ifndef __sun__ #ifndef __sun__
#include <sys/sysctl.h>
#endif #endif
*/
#include <sys/resource.h> #include <sys/resource.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
@ -67,14 +70,9 @@
#define MAX(a,b) (((a)>(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b))
#endif #endif
//control time slot in microseconds
//each slot is splitted in a working slice and a sleeping slice
//TODO: make it adaptive, based on the actual system load
#define TIME_SLOT 100000
#define MAX_PRIORITY -10 #define MAX_PRIORITY -10
#define VERSION "0.3" #define VERSION "0.3.2"
/* GLOBAL VARIABLES */ /* GLOBAL VARIABLES */
@ -121,6 +119,7 @@ static void print_usage(FILE *stream, int exit_code) {
fprintf(stream, "Usage: %s [OPTIONS...] TARGET\n", program_name); fprintf(stream, "Usage: %s [OPTIONS...] TARGET\n", program_name);
fprintf(stream, " OPTIONS\n"); fprintf(stream, " OPTIONS\n");
fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to %d (required)\n", 100*NCPU); fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to %d (required)\n", 100*NCPU);
fprintf(stream, " -c, --chunk-size=N enforce CPU limit in N microsecond chunks\n");
fprintf(stream, " -v, --verbose show control statistics\n"); fprintf(stream, " -v, --verbose show control statistics\n");
fprintf(stream, " -V, --version show program version number\n"); fprintf(stream, " -V, --version show program version number\n");
fprintf(stream, " -z, --lazy exit if there is no target process, or if it dies\n"); fprintf(stream, " -z, --lazy exit if there is no target process, or if it dies\n");
@ -193,7 +192,7 @@ int get_pid_max() {
#endif #endif
} }
void limit_process(pid_t pid, double limit, int include_children, float minimum_cpu_usage) { void limit_process(pid_t pid, double limit, int include_children, float minimum_cpu_usage, int chunk_size) {
//slice of the slot in which the process is allowed to run //slice of the slot in which the process is allowed to run
struct timespec twork; struct timespec twork;
//slice of the slot in which the process is stopped //slice of the slot in which the process is stopped
@ -258,13 +257,13 @@ void limit_process(pid_t pid, double limit, int include_children, float minimum_
//it's the 1st cycle, initialize workingrate //it's the 1st cycle, initialize workingrate
pcpu = limit; pcpu = limit;
workingrate = limit; workingrate = limit;
twork.tv_nsec = TIME_SLOT * limit * 1000; twork.tv_nsec = chunk_size * limit * 1000;
} else { } else {
//adjust workingrate //adjust workingrate
workingrate = MIN(workingrate / pcpu * limit, 1); workingrate = MIN(workingrate / pcpu * limit, 1);
twork.tv_nsec = TIME_SLOT * 1000 * workingrate; twork.tv_nsec = chunk_size * 1000 * workingrate;
} }
tsleep.tv_nsec = TIME_SLOT * 1000 - twork.tv_nsec; tsleep.tv_nsec = chunk_size * 1000 - twork.tv_nsec;
if (verbose) { if (verbose) {
if (c%200==0) { if (c%200==0) {
@ -339,6 +338,7 @@ int main(int argc, char **argv) {
pid_t pid = 0; pid_t pid = 0;
float minimum_cpu_usage=0; float minimum_cpu_usage=0;
int include_children = 0; int include_children = 0;
int chunk_size = 100000;
//get program name //get program name
#ifdef __sun__ #ifdef __sun__
@ -356,14 +356,15 @@ int main(int argc, char **argv) {
int next_option; int next_option;
int option_index = 0; int option_index = 0;
//A string listing valid short options letters //A string listing valid short options letters
const char *short_options = "+p:e:l:vVzim:h"; const char *short_options = "+p:e:l:c:vVzim:h";
//An array describing valid long options //An array describing valid long options
const struct option long_options[] = { const struct option long_options[] = {
{ "pid", required_argument, NULL, 'p' }, { "pid", required_argument, NULL, 'p' },
{ "exe", required_argument, NULL, 'e' }, { "exe", required_argument, NULL, 'e' },
{ "limit", required_argument, NULL, 'l' }, { "limit", required_argument, NULL, 'l' },
{ "chunk-size", required_argument, NULL, 'c' },
{ "verbose", no_argument, NULL, 'v' }, { "verbose", no_argument, NULL, 'v' },
{ "version", no_argument, NULL, 'V' }, { "version", no_argument, NULL, 'V' },
{ "lazy", no_argument, NULL, 'z' }, { "lazy", no_argument, NULL, 'z' },
{ "include-children", no_argument, NULL, 'i' }, { "include-children", no_argument, NULL, 'i' },
{ "minimum-limited-cpu", no_argument, NULL, 'm' }, { "minimum-limited-cpu", no_argument, NULL, 'm' },
@ -386,12 +387,15 @@ int main(int argc, char **argv) {
perclimit = atoi(optarg); perclimit = atoi(optarg);
limit_ok = 1; limit_ok = 1;
break; break;
case 'c':
chunk_size = atoi(optarg);
break;
case 'v': case 'v':
verbose = 1; verbose = 1;
break; break;
case 'V': case 'V':
print_version(stdout, 0); print_version(stdout, 0);
break; break;
case 'z': case 'z':
lazy = 1; lazy = 1;
break; break;
@ -435,6 +439,12 @@ int main(int argc, char **argv) {
exit(1); exit(1);
} }
if (0 > chunk_size || chunk_size > 1000000) {
fprintf(stderr,"Error: chunk size must be in the range 0-1000000\n");
print_usage(stderr, 1);
exit(1);
}
int command_mode = optind < argc; int command_mode = optind < argc;
if (exe_ok + pid_ok + command_mode == 0) { if (exe_ok + pid_ok + command_mode == 0) {
fprintf(stderr,"Error: You must specify one target process, either by name, pid, or command line\n"); fprintf(stderr,"Error: You must specify one target process, either by name, pid, or command line\n");
@ -513,7 +523,7 @@ int main(int argc, char **argv) {
if (verbose) { if (verbose) {
printf("Limiting process %d\n",child); printf("Limiting process %d\n",child);
} }
limit_process(child, limit, include_children, minimum_cpu_usage); limit_process(child, limit, include_children, minimum_cpu_usage, chunk_size);
exit(0); exit(0);
} }
} }
@ -548,7 +558,7 @@ int main(int argc, char **argv) {
} }
printf("Process %d found\n", pid); printf("Process %d found\n", pid);
//control //control
limit_process(pid, limit, include_children, minimum_cpu_usage); limit_process(pid, limit, include_children, minimum_cpu_usage, chunk_size);
} }
if (lazy) { if (lazy) {
break; break;

View File

@ -32,7 +32,7 @@
#include <assert.h> #include <assert.h>
#ifdef __sun__ #ifdef __sun__
#include <libgen.h> #include <libgen.h>
#endif #endif
#include "process_iterator.h" #include "process_iterator.h"

View File

@ -23,9 +23,10 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#ifdef __sun__ #ifdef __sun__
#include <procfs.h> #include <procfs.h>
#elif !defined __APPLE__ #elif !defined __APPLE__
#include <sys/procfs.h> #include <unistd.h>
#include <sys/procfs.h>
#endif #endif
#include <time.h> #include <time.h>
#include "process_iterator.h" #include "process_iterator.h"
@ -46,7 +47,7 @@
#elif defined __sun__ #elif defined __sun__
#include "process_iterator_solaris.c" #include "process_iterator_solaris.c"
#else #else

View File

@ -4,7 +4,7 @@
* Copyright (C) 2016 by Jim Mason <jmason at ibinx dot com> * Copyright (C) 2016 by Jim Mason <jmason at ibinx dot com>
* *
* Adapted from process_iterator_linux.c * Adapted from process_iterator_linux.c
* Copyright (C) 2005-2012, by: Angelo Marletta <angelo dot marletta at gmail dot com> * Copyright (C) 2005-2012, by: Angelo Marletta <angelo dot marletta at gmail dot com>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -22,97 +22,114 @@
*/ */
int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { int init_process_iterator(struct process_iterator *it, struct process_filter *filter) {
// open a directory stream to /proc directory // open a directory stream to /proc directory
if (!(it->dip = opendir("/proc"))) { if (!(it->dip = opendir("/proc"))) {
perror("opendir"); perror("opendir");
return -1; return -1;
} }
it->filter = filter; it->filter = filter;
return 0; return 0;
} }
static int read_process_info(pid_t pid, struct process *p) { static int read_process_info(pid_t pid, struct process *p) {
psinfo_t psinfo; psinfo_t psinfo;
char statfile[32]; char statfile[32];
p->pid = pid; p->pid = pid;
sprintf(statfile, "/proc/%ld/psinfo", (long)pid); sprintf(statfile, "/proc/%ld/psinfo", (long)pid);
FILE *fd = fopen(statfile, "r"); FILE *fd = fopen(statfile, "r");
if (!fd) return -1; if (!fd) {
if (!fread(&psinfo, sizeof(psinfo), 1, fd)) { return -1;
fclose(fd); }
return -1; if (!fread(&psinfo, sizeof(psinfo), 1, fd)) {
} fclose(fd);
fclose(fd); return -1;
}
fclose(fd);
p->ppid = psinfo.pr_ppid; p->ppid = psinfo.pr_ppid;
p->cputime = psinfo.pr_time.tv_sec * 1.0e03 + psinfo.pr_time.tv_nsec / 1.0e06; p->cputime = psinfo.pr_time.tv_sec * 1.0e03 + psinfo.pr_time.tv_nsec / 1.0e06;
p->starttime = psinfo.pr_start.tv_sec * 1.0e03 + psinfo.pr_start.tv_nsec / 1.0e06; p->starttime = psinfo.pr_start.tv_sec * 1.0e03 + psinfo.pr_start.tv_nsec / 1.0e06;
strcpy(p->command, psinfo.pr_psargs); strcpy(p->command, psinfo.pr_psargs);
return 0; /**
* per proc(4):
*
* If the process is a zombie, pr_nlwp, pr_nzomb, and
* pr_lwp.pr_lwpid are zero and the other fields of pr_lwp
* are undefined
*
* In this case, we'll return -1 to the caller
*/
return psinfo.pr_nlwp?0:-1;
} }
static pid_t getppid_of(pid_t pid) { static pid_t getppid_of(pid_t pid) {
psinfo_t psinfo; psinfo_t psinfo;
char statfile[32]; char statfile[32];
sprintf(statfile, "/proc/%ld/psinfo", (long)pid); sprintf(statfile, "/proc/%ld/psinfo", (long)pid);
FILE *fd = fopen(statfile, "r"); FILE *fd = fopen(statfile, "r");
if (!fd) return -1; if (!fd) {
if (!fread(&psinfo, sizeof(psinfo), 1, fd)) { return -1;
fclose(fd); }
return -1; if (!fread(&psinfo, sizeof(psinfo), 1, fd)) {
} fclose(fd);
fclose(fd); return -1;
}
fclose(fd);
return psinfo.pr_ppid; return psinfo.pr_ppid;
} }
static int is_child_of(pid_t child_pid, pid_t parent_pid) { static int is_child_of(pid_t child_pid, pid_t parent_pid) {
int ppid = child_pid; int ppid = child_pid;
while(ppid > 1 && ppid != parent_pid) while (ppid > 1 && ppid != parent_pid) {
ppid = getppid_of(ppid); ppid = getppid_of(ppid);
return ppid == parent_pid; }
return ppid == parent_pid;
} }
int get_next_process(struct process_iterator *it, struct process *p) { int get_next_process(struct process_iterator *it, struct process *p) {
if (!it->dip) { if (!it->dip) {
// end of processes // end of processes
return -1; return -1;
} }
if (it->filter->pid != 0 && !it->filter->include_children) { if (it->filter->pid != 0 && !it->filter->include_children) {
int ret = read_process_info(it->filter->pid, p); int ret = read_process_info(it->filter->pid, p);
closedir(it->dip); closedir(it->dip);
it->dip = NULL; it->dip = NULL;
return ret; return ret;
} }
// read in from /proc and seek for process dirs // read in from /proc and seek for process dirs
struct dirent *dit; struct dirent *dit;
while ((dit = readdir(it->dip))) { while ((dit = readdir(it->dip))) {
p->pid = atoi(dit->d_name); p->pid = atoi(dit->d_name);
if (it->filter->pid != 0 && it->filter->pid != p->pid && !is_child_of(p->pid, it->filter->pid)) continue; if (it->filter->pid != 0 && it->filter->pid != p->pid && !is_child_of(p->pid, it->filter->pid)) {
read_process_info(p->pid, p); continue;
break; }
} if (!read_process_info(p->pid, p)) {
break;
}
}
if (!dit) { if (!dit) {
// end of processes // end of processes
closedir(it->dip); closedir(it->dip);
it->dip = NULL; it->dip = NULL;
return -1; return -1;
} }
return 0; return 0;
} }
int close_process_iterator(struct process_iterator *it) { int close_process_iterator(struct process_iterator *it) {
if (it->dip && closedir(it->dip) == -1) { if (it->dip && closedir(it->dip) == -1) {
perror("closedir"); perror("closedir");
return 1; return 1;
} }
it->dip = NULL; it->dip = NULL;
return 0; return 0;
} }

View File

@ -5,42 +5,11 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-cpulimit LUCI_TITLE:=cpulimit configuration module
PKG_VERSION=1.0 LUCI_DEPENDS:=+cpulimit
PKG_RELEASE:=2
include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME) include $(TOPDIR)/feeds/luci/luci.mk
SECTION:=luci
CATEGORY:=LuCI
SUBMENU:=3. Applications
DEPENDS:=+cpulimit
TITLE:=LuCI support for cpulimit
PKGARCH:=all
endef
define Build/Compile
endef
define Package/$(PKG_NAME)/postinst # call BuildPackage - OpenWrt buildroot signature
#!/bin/sh
rm -f /tmp/luci-indexcache /tmp/luci-modulecache
endef
define Package/$(PKG_NAME)/conffiles
/etc/config/cpulimit
endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/etc/init.d $(1)/etc/config $(1)/usr/lib/lua/luci
$(CP) ./luasrc/* $(1)/usr/lib/lua/luci
$(INSTALL_CONF) ./root/etc/config/cpulimit $(1)/etc/config
$(INSTALL_BIN) ./root/etc/init.d/cpulimit $(1)/etc/init.d
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) ./root/usr/bin/cpulimit.sh $(1)/usr/bin
$(INSTALL_DIR) $(1)/usr/lib/lua/luci/i18n
po2lmo ./po/zh-cn/cpulimit.po $(1)/usr/lib/lua/luci/i18n/cpulimit.zh-cn.lmo
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View File

@ -18,7 +18,7 @@ end
limit = s:option(Value, "limit", translate("limit")) limit = s:option(Value, "limit", translate("limit"))
limit.optional = false limit.optional = false
limit.rmempty = false limit.rmempty = false
limit.default="50" limit.default="30"
limit:value("100","100%") limit:value("100","100%")
limit:value("90","90%") limit:value("90","90%")
limit:value("80","80%") limit:value("80","80%")

View File

@ -0,0 +1,13 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete ucitrack.@cpulimit[-1]
add ucitrack cpulimit
set ucitrack.@cpulimit[-1].init=cpulimit
commit ucitrack
EOF
/etc/init.d/cpulimit enable
rm -f /tmp/luci-indexcache
exit 0

View File

@ -20,6 +20,8 @@ killall -9 cpulimit
# do # do
# killall cpulimit>/dev/null 2>&1 # killall cpulimit>/dev/null 2>&1
# done # done
# pgrep -f cpulimit | xargs kill -9 >/dev/null 2>&1
} }
case "$1" in case "$1" in