⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 kupdate.c

📁 linux 系统控制硬盘旋转和停止的程序(spin down/up)
💻 C
字号:
/* * <kupdate.c> * * Handle interaction with kupdate(d)/pdflush kernel daemon. * * Copyright (C) 2000-2003 Daniel Kobras *           (C) 2003 Michael Buesch (parts of pdflush support) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *  * $Id: kupdate.c,v 1.21 2004/08/08 15:01:57 nold Exp $ * */#ifdef HAVE_CONFIG_H#include <config.h>#endif#include "kupdate.h"#include "bug.h"#include "util.h"#include "noflushd.h"#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <fcntl.h>#include <dirent.h>#include <string.h>#include <ctype.h>#include <signal.h>#include <errno.h>#include <sys/param.h>		/* for HZ. (Yeah, I know...) */#include <sys/types.h>/* * Here's a simple yet effective fallback in case everything goes haywire * and kupdate(d) is left messed up. In fact, this is bdflush's/update's * fallback behaviour as well. */static void last_resort(long interval){	if (interval <= 0)		interval = 1;	ERR("Kernel background updates messed up");	ERR("Falling back to brute-force emergency syncing every %ld secs",	    interval); 	while (1==1) {		sync();		sleep(interval);	}}	/* * Ugly fumbling through /proc to determine the PID of the kupdate kernel * thread. My, do I hate writing parsers! I wonder how you do it without * /proc or /proc mounted on a different node. Humm... [peeking through * pidof source] looks like you're simply screwed. Weird. But then thank * god the days of messing with /dev/kmem have gone. */static int get_kupdate_pid(kupdate_t k){	DIR	*proc, *pid_dir;	FILE	*f=NULL;	char	*line, *s_pid, *name;	char	namebuf[267] = "/proc/";	/* d_name[] length of 256 bytes						   is hardcoded in <direntry.h>.						   We need to store at max 						   /proc/<d_name[]>/stat. */	int	pid, kpid=-1;	struct dirent	*procent;	proc = opendir("/proc");	if (!proc) {		ERR("Unable to open /proc");		return 0;	}	/* More space efficient than using scandir()... */ 	while ((procent = readdir(proc))) {		if (!isdigit((int)(procent->d_name[0])))			continue;		strcpy(namebuf+6, procent->d_name);		if (!(pid_dir = opendir(namebuf)))			continue;		closedir(pid_dir);		strcat(namebuf, "/stat");		if (f)			fclose(f);		f = fopen(namebuf, "r");		line=get_line(f);		if (!line)			continue;		name=_get_entry(line, 1);		if (!name) {			release_line(line);			continue;		}		/* An ordinary user might have started a process named kupdate		 * but it will never get a lower pid than the kupdate kernel		 * thread. Uh, versions later than 2.4.2 seem to call it		 * kupdated. Alan's valentine? Lovely...		 */		if (!strcmp(name, "(kupdate)") || !strcmp(name, "(kupdated)")) {			s_pid=_get_entry(line, 0);			if (!s_pid)				BUG("No pid info on kupdate");			pid=atoi(s_pid);			if((pid < kpid) || (kpid == -1)) {				kpid=pid;				if (k->stat)					fclose(k->stat);				k->stat=f;				f=NULL;			}			DEBUG("Detected process %s at pid %d", name, pid);		}		release_line(line);			}	closedir(proc);	if (kpid == -1) {		ERR("Could not determine PID of kupdate");		return 0;	}	k->pid=(pid_t) kpid;	return 1;}/* Check if kupdate is really stopped. */static int kupdate_check_stop(kupdate_t k){	char *line;	char *state;	int ret;		fflush(k->stat);	rewind(k->stat);	line=get_line(k->stat);	if (!line)		BUG("Could not read kupdate stat");		state=_get_entry(line, 2);	if (!state)		BUG("Could not read kupdate state");		ret=(state[0]=='T');		release_line(line);		return ret;}/* * get/set_interval are simple helper functions to wrap [gs]etting the * basic flush interval of kupdate. */static int _kupdate_get_interval(kupdate_t k, long *interval){	long args[9];	int i;	rewind(k->f);	for (i=0; i < 9; i++) {		if (fscanf(k->f, "%ld", &args[i]) != 1) {			ERR("Error reading kupdate(d) control file");			return 0;		}	}	*interval = args[4];	if (k->interval <= 0) {		INFO("Bogus kupdate interval %ld, setting to %d",		       *interval, 5*HZ);		*interval = 5*HZ;	}	return 1;}static int _kupdate_set_interval(kupdate_t k, long interval){	long args[9];	int i;		rewind(k->f);	for (i=0; i < 9; i++) {		if (fscanf(k->f, "%ld", &args[i]) != 1) {			ERR("Error reading kupdate(d) control file");			return 0;		}	}		args[4]=interval;		rewind(k->f);	for (i=0; i < 9; i++) {		if (fprintf(k->f, "%ld", args[i]) <= 0) {			ERR("Error writing to kupdate(d) control file");			return 0;		}	}	fprintf(k->f, "\n");	fflush(k->f);	return 1;}static long kupdate_fixup_interval(long interval){	return (interval+HZ-1)/HZ;}/* * Handle background flushing of dirty buffers to disk via starting or  * stopping the kupdate kernel thread. kupdate is either (re)started or * stopped, depending on current setting in struct *k. * There's one ugly thing: kupdate expects its params scaled by HZ. Which * is quite funny as HZ is a kernel thingie and nowhere exported to userspace * (and rightfully so).  */static void __kupdate_stop(kupdate_t k){	long interval, left;	if (!k)		return;	_kupdate_get_interval(k, &interval);	if (!_kupdate_set_interval(k, 0))		ERR("kupdate interval still set. Expect frequent spinups");	kill(k->pid, SIGSTOP);	/* The signal itself is ignored, but it will				 * cause kupdate to wakeup now and detect 				 * interval=0. */	/* Make sure kupdate is really asleep. We poll 5 times per second and	 * wait at most 10 kupdate wakeup cycles. (During heavy system load,	 * kupdate wakeup might be delayed.) */	left = (10*k->interval*5+HZ-1)/HZ;	while (!kupdate_check_stop(k) && left > 0) {		usleep(200000);		left--;	}	if (!left) {		ERR("Could not stop kupdate. Expect lousy spindown times");		return;	}			k->stopped=1;	DEBUG("kupdate stopped");}	static void __kupdate_start(kupdate_t k){	if (!k)		return;	if (!_kupdate_set_interval(k, k->interval) ||	    (-1 == kill(k->pid, SIGCONT))) {		ERR("Could not restart kupdate");		ERR("Falling back to sync()");		last_resort(k->interval);	}	k->stopped=0;		DEBUG("kupdate restarted with interval %ld.", k->interval);}/*  * Set up kupdate struct. Determines pid of kupdate kernel daemon. */static int _kupdate_init(kupdate_t k){	k->start=__kupdate_start;	k->stop =__kupdate_stop;	k->fixup_interval=kupdate_fixup_interval;	k->stat=NULL;	if (!get_kupdate_pid(k)) {		DEBUG("No kupdate(d) found");		return 0;	}	k->f=fopen("/proc/sys/vm/bdflush","r+");	if (!k->f) {		ERR("Unable to open kupdate(d) control file");		fclose(k->stat);		return 0;	}	if (!_kupdate_get_interval(k, &k->interval)) {		ERR("Could not get valid kupdate interval");		fclose(k->f);		fclose(k->stat);		return 0;	}	return 1;}/* * Linux kernels 2.5 and up handle background flushing via the pdflush * daemon. */static int pdflush_get_interval(kupdate_t k, long *interval){	char *buf;	rewind(k->f);	if (!(buf = get_line(k->f))) {		ERR("Failed to get pdflush update interval");		return 0;	}	*interval = strtol(_get_entry(buf, 0), NULL, 0);	if (*interval < 0) {		ERR("Bogus pdflush interval");		return 0;	}	release_line(buf);	return 1;}static int pdflush_set_interval(kupdate_t k, long interval){	rewind(k->f);	if (fprintf(k->f, "%ld\n", interval) < 0) {		ERR("Failed to set net update interval");		return 0;	}	fflush(k->f);	return 1;}	static int check_pdflush(kupdate_t k){	long interval;	if (!pdflush_get_interval(k, &k->interval)) {		DEBUG("Failed to get pdflush interval");		return 0;	}		if (k->interval <= 100) {		INFO("pdflush wakeup interval %ld suspiciously low, "		     "overriding to 5 seconds\n", k->interval);		k->interval = 500;	}	if (!pdflush_set_interval(k, 0)) {		DEBUG("Failed to set pdflush interval");		return 0;	}	if (!pdflush_get_interval(k, &interval)) {		DEBUG("Failed to get 2nd pdflush interval");		return 0;	}	if (interval != 0) {		DEBUG("pdflush interval inconsistent - giving up");		return 0;	}		if (!pdflush_set_interval(k, k->interval)) {		DEBUG("Failed to set 2nd pdflush interval");		return 0;	}	return 1;}static void pdflush_start(kupdate_t k){	if (!k)		return;	if (!pdflush_set_interval(k, k->interval)) {		ERR("Failed to set pdflush interval");		last_resort(k->interval);	}		k->stopped = 0;	DEBUG("pdflush restarted with interval %ld.", k->interval);}static void pdflush_stop(kupdate_t k){	long interval;		if (!k)		return;	if (!pdflush_get_interval(k, &k->interval)) {		ERR("Failed to get pdflush interval");		return;	}		if (!pdflush_set_interval(k, 0))		ERR("Failed to stop pdflush");	else {		/* Need to wait until all pending pdflush timers have		 * expired. Unfortunately there's no way to kick them. */		sleep(k->fixup_interval(k->interval));				DEBUG("pdflush stopped");		k->stopped = 1;	}}static long pdflush_fixup_interval(long interval){	return (interval+100-1)/100;}static int pdflush_init(kupdate_t k){	FILE *f = NULL;	f = fopen("/proc/sys/vm/dirty_writeback_centisecs", "w+");	if (!f) {		DEBUG("Could not open pdflush control interface");		return 0;	}	k->f = f;	/* On success, check_pdflush() initializes k->interval as a	 * side-effect.	 */	if (!check_pdflush(k)) {		ERR("Cannot control pdflush writeback");		return 0;	}		k->start = pdflush_start;	k->stop = pdflush_stop;	k->fixup_interval = pdflush_fixup_interval;		return 1;}/* The kupdate kernel interface scales with HZ whereas the pdflush interface * exports centisecs. Need to take care when converting into secs. */long kupdate_get_interval(kupdate_t k){	return k->fixup_interval(k->interval);}/* This is the main init function for both kupdate(d) and pdflush support. * The kupdate prefix for all generic methods is both unfortunate and * historic. Tough... */kupdate_t kupdate_init(void){	kupdate_t k;	k=malloc(sizeof(struct kupdate_s));	if (!k)		return NULL;	if (pdflush_init(k))		DEBUG("Controlling pdflush daemon");	else if (_kupdate_init(k))		DEBUG("Controlling kupdate(d) daemon");	else {		ERR("No kernel support for flush control found");		free(k);		return NULL;	}	k->stopped=k->interval ? 0 : 1;	return k;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -