Это вполне рабочая утилита, нагружающая дисковую и файловую подсистему "на отказ". Писалась и использовалась для тестирования рабочего сервера. Здесь размещаю just for fun.
/*********************************************************
FILE: crashtest.c
DESCRIPTION: multithreaded crashtest for disk-subsustem
AUTHOR: Artamonov Dmitry, screw_cms@mail.ru
HISTORY: 08-09/07/2003, Screw: initial version (stable)
11/07/2003, Wizard: parsing command line by getopt
16/07/2003, Screw: optional make (argv/getopt arguments)
COMMENT: It's my first trip in Linux programming, but i'm prefer Win32 platform
THANKS: Evseev Alexander (Wizard) for help in foreign OS
MAKE: g++ -L/usr/lib -lpthread -o disktest disktest.cpp
!!! FIX_ME: open_dir is NO REEINTRANT function!!!
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.
*********************************************************/
#define O_GETOPT_PARSING // comment it for argv[] parsing case
// basically console IO
#include <stdio.h>
#include <stdlib.h>
// strings
#include <string.h>
// directory scanning
#include "dirent.h"
// for directory STATus
#include "sys/stat.h"
#include "sys/types.h"
#include "unistd.h"
// threads
#include "pthread.h"
#define MAX_PATH_LENGTH 32768
// variety selecting directory in current hierarchy, in percent(%)
#define SEL_DIR_FREQ 30
#define C_COMMANDS_COUNT 6
#define C_MAKE_DIR 0
#define C_REM_DIR 1
#define C_MAKE_FILE 2
#define C_REM_FILE 3
#define C_RAND_READ 4
#define C_RAND_WRITE 5
// file sizes in Kb
#define C_FILE_MIN_SIZE 200
#define C_FILE_MAX_SIZE 20000
#define C_THREAD_DEF 20
#define C_TIME_DEF 1
int file_min_size;
int file_max_size;
#define C_FILE_BUFFER_SIZE 65536
int my_rand()
{
static unsigned int numb = 0;
if (!numb) numb = rand();
return numb++;
}
//===========================================================================================
// Selecting random directory into 'root_dir' directories tree.
// if selecting is done routine returns TRUE and copies founded path into root_dir string
//===========================================================================================
bool select_directory(char* root_dir)
{
char path[MAX_PATH_LENGTH];
int dir_count, dir_num, level = 0;
DIR *dp;
struct dirent *ep;
if (!strcmp( root_dir, "/dev") || !strcmp( root_dir, "/proc")) return false;
for (;;)
{
dp = opendir(root_dir);
if (!dp) return false;// open dir fails
dir_count = 0;
while ( ep = readdir (dp) )
{
if (strcmp(ep->d_name,"..") && strcmp(ep->d_name,".") )
{
strcpy( path, root_dir );
if (strlen( path) > 1)
strcat( path, "/");
strcat( path, ep->d_name );
struct stat stat_buff;
stat( path, &stat_buff );
if ( S_ISDIR( stat_buff.st_mode ) /*&& !S_ISLNK( stat_buff.st_mode )*/ )
dir_count++;
}
}
closedir( dp );
if (0==dir_count) break;
// select random directory
dir_num = my_rand()%dir_count;
dp = opendir(root_dir);
if (!dp) return false;// open dir fails
dir_count = 0;
while ( ep = readdir (dp) )
{
if (strcmp(ep->d_name,"..") && strcmp(ep->d_name,".") )
{
strcpy( path, root_dir );
if (strlen( path) > 1)
strcat( path, "/");
strcat( path, ep->d_name );
struct stat stat_buff;
stat( path, &stat_buff );
if ( S_ISDIR( stat_buff.st_mode ) /*&& !S_ISLNK( stat_buff.st_mode )*/ )
if ( dir_num == dir_count)
break;
else
dir_count++;
}
}
closedir( dp );
// check that directory was founded
if (dir_count != dir_num)
{
dir_count = 0;
break;
}
strcpy( root_dir, path ); // returning current dir
if (my_rand()%100 < SEL_DIR_FREQ)
return true;
level++;
}
// return's current directory if search fails
if (0 == dir_count)
{
// no directories we're founded, but level is childest - then return TRUE!
return (level>0);
}
}
//===========================================================================================
// Selecting random file in 'root_dir' directory
// Sipmle analog with 'select_directory' routine
//===========================================================================================
bool select_file(char *root_dir)
{
char path[MAX_PATH_LENGTH];
int file_count, file_num, level = 0;
DIR *dp;
struct dirent *ep;
if (!strcmp( root_dir, "/dev") || !strcmp( root_dir, "/proc")) return false;
dp = opendir(root_dir);
if (!dp) return false;// open dir fails
file_count = 0;
while ( ep = readdir (dp) )
{
if (strcmp(ep->d_name,"..") && strcmp(ep->d_name,".") )
{
strcpy( path, root_dir );
if (strlen( path) > 1)
strcat( path, "/");
strcat( path, ep->d_name );
struct stat stat_buff;
stat( path, &stat_buff );
if ( !S_ISDIR( stat_buff.st_mode ) /*&& !S_ISLNK( stat_buff.st_mode )*/ )
file_count++;
}
}
closedir( dp );
if (0==file_count) return false;
// select random file
file_num = my_rand()%file_count;
dp = opendir(root_dir);
if (!dp) return false;// open dir fails
file_count = 0;
while ( ep = readdir (dp) )
{
if (strcmp(ep->d_name,"..") && strcmp(ep->d_name,".") )
{
strcpy( path, root_dir );
if (strlen( path) > 1)
strcat( path, "/");
strcat( path, ep->d_name );
struct stat stat_buff;
stat( path, &stat_buff );
if ( !S_ISDIR( stat_buff.st_mode ) /*&& !S_ISLNK( stat_buff.st_mode )*/ )
if ( file_num == file_count)
break;
else
file_count++;
}
}
closedir( dp );
// check that file was founded
if (file_count != file_num)
return false;
strcpy( root_dir, path ); // returning current file
return true;
}
//===========================================================================================
// Deleting sub-tree recursivelly with all content
// returns deleted files count
// 'dirs' parameter is increased by deleted folders count
//===========================================================================================
int del_tree(char* root_dir, int &dirs)
{
char path[MAX_PATH_LENGTH];
DIR *dp;
struct dirent *ep;
int count = 0;
if (!strcmp( root_dir, "/dev") || !strcmp( root_dir, "/proc"))
return 0;
dp = opendir (root_dir);
if (dp != NULL)
{
while ( ep = readdir (dp) )
{
if (strcmp(ep->d_name,"..") && strcmp(ep->d_name,".") )
{
strcpy( path, root_dir );
if (strlen( path) > 1)
strcat( path, "/");
strcat( path, ep->d_name );
struct stat stat_buff;
stat( path, &stat_buff );
if ( S_ISDIR( stat_buff.st_mode ) /*&& !S_ISLNK( stat_buff.st_mode )*/ )
{
count += del_tree( path, dirs );
dirs++;
}
else
count++;
remove( path );
}
}
if (!remove( root_dir ))
dirs++;
}
return count;
}
//===========================================================================================
// POSIX thread block with program-logic implementation
//===========================================================================================
#define PTH_INIT 0
#define PTH_RUNNING 1
#define PTH_STOP_PENDING 2
#define PTH_EXITED 3
// work statistic
typedef struct {
unsigned int f_created;
unsigned int f_erased;
unsigned int d_created;
unsigned int d_erased;
unsigned int reading;
unsigned int writing;
bool is_FS_full;
} statistic, *statistic_ptr;
// thread arguments
typedef struct {
int th_num;
pthread_t THREAD;
int state;
char *root_dir;
void *buffer;
statistic_ptr p_st;
} thread_arg, *thread_arg_ptr;
// MAIN thread working routine
void* thread_func( void* arg )
{
void *retval;
thread_arg_ptr p_arg = (thread_arg_ptr)arg;
char path[MAX_PATH_LENGTH];
char name[MAX_PATH_LENGTH];
int cmd, rnd, dirs, res;
unsigned int size, bl_pos, bl_size;
// printf("Thread %i started!\n", p_arg->th_num);
p_arg->state = PTH_RUNNING;
while ( PTH_STOP_PENDING != p_arg->state )
{
strcpy( path, p_arg->root_dir );
if (!select_directory( path ))
strcpy( path, p_arg->root_dir ); // selection failure, work with root dir
// select command
rnd = rand()%100;
if (p_arg->p_st->is_FS_full)
{
// filesystem is full, enable removing content
if ( rnd < 50 ) cmd = C_MAKE_FILE;
else
if (rnd < 70) cmd = C_MAKE_DIR;
else
if (rnd < 80) cmd = C_REM_FILE;
else
if (rnd < 85)
{
// lock removing root directory
if (strcmp( path, p_arg->root_dir))
cmd = C_REM_DIR;
else
cmd = 0;
}
else
if (rnd<93) cmd = C_RAND_READ;
else cmd = C_RAND_WRITE;
} else {
// filesystem while is empty - filling one
if ( rnd < 65 ) cmd = C_MAKE_FILE;
else
if (rnd < 85) cmd = C_MAKE_DIR;
else
if (rnd<93) cmd = C_RAND_READ;
else cmd = C_RAND_WRITE;
}
// executing command
switch( cmd )
{
case C_MAKE_DIR: {
if (rand()%100 < SEL_DIR_FREQ)
// with SEL_DIR_FREQUENCY we're creating directory in root_dir folder
strcpy( path, p_arg->root_dir );
sprintf( name, "%s/%i.%i", path, my_rand(), p_arg->th_num );
if ( !mkdir( name, 493 ) )
p_arg->p_st->d_created++;
break;
}
case C_REM_DIR: {
dirs = 0;
p_arg->p_st->f_erased += del_tree( path, dirs );
p_arg->p_st->d_erased += dirs;
break;
}
case C_MAKE_FILE: {
sprintf( name, "%s/%i.%i", path, my_rand(), p_arg->th_num );
FILE* f = fopen(name, "wb");
if (f)
{
size = file_min_size + rand()%(file_max_size - file_min_size);
size*=1024;
while (size>0)
{
if (size >= C_FILE_BUFFER_SIZE)
{
res = fwrite( p_arg->buffer, 1, C_FILE_BUFFER_SIZE, f );
if (res != C_FILE_BUFFER_SIZE)
p_arg->p_st->is_FS_full = true;
size-=C_FILE_BUFFER_SIZE;
} else {
res = fwrite( p_arg->buffer, 1, size, f );
if (res != size)
p_arg->p_st->is_FS_full = true;
size=0;
}
}
fclose( f );
p_arg->p_st->f_created++;
break;
}
}
case C_REM_FILE: {
if (select_file(path))
{
if (!remove(path))
p_arg->p_st->f_erased++;
}
break;
}
case C_RAND_READ:
case C_RAND_WRITE: {
if (select_file(path))
{
FILE* f;
if (C_RAND_READ == cmd)
f = fopen( path, "rb" );
else
f = fopen( path, "wb" );
if (f)
{
struct stat stat_buff;
stat( path, &stat_buff );
size = stat_buff.st_size;
if (size>0)
{
// file size is enough
bl_pos = rand()%size;
bl_size = rand()%(size-bl_pos+1)+1;
while (bl_size>0)
{
if ( bl_size >= C_FILE_BUFFER_SIZE )
size = C_FILE_BUFFER_SIZE;
else
size = bl_size;
if (!fseek(f, bl_pos, SEEK_SET) )
{
if ( C_RAND_READ == cmd )
fread(p_arg->buffer, 1, size, f);
else
fwrite(p_arg->buffer, 1, size, f);
}
bl_size -= size;
}
}
fclose(f);
if (C_RAND_READ == cmd)
p_arg->p_st->reading++;
else
p_arg->p_st->writing++;
}
}
break;
}
}
}
p_arg->state = PTH_EXITED;
pthread_exit( retval );
}
void usage(const char * pr_name) {
#ifdef O_GETOPT_PARSING
printf("Disk subsustem crash-test. Was written by (c)2003 Screw\n" \
"USAGE:\n%s dirpath [-c count] [-t mins] [-m min_size] [-M max_size]\n" \
"Where: dirpath - working directory/device\n\n" \
" c - threads counts (%i by default)\n" \
" t - time of test execution (in min, %i by default)\n" \
" m - minimal work-files size (in Kb, %i by default)\n" \
" M - maximal work-files size (in Kb, %i by default)\n\n", pr_name, C_THREAD_DEF, C_TIME_DEF, C_FILE_MIN_SIZE, C_FILE_MAX_SIZE \
);
#else
printf("Disk subsustem crash-test. Was written by (c)2003 Screw\n" \
"USAGE:\n%s dirpath [-c] [-t] [-m] [-M]\n" \
"Where: dirpath - working directory/device\n\n" \
" c - threads counts (%i by default)\n" \
" t - time of test execution (in min, %i by default)\n" \
" m - minimal work-files size (in Kb, %i by default)\n" \
" M - maximal work-files size (in Kb, %i by default)\n\n", pr_name, C_THREAD_DEF, C_TIME_DEF, C_FILE_MIN_SIZE, C_FILE_MAX_SIZE \
);
#endif
return;
}
//===========================================================================================
// MAIN PROGRAMM
//===========================================================================================
int main( int argc, char *argv[])
{
if (argc < 2)
{
usage(argv[0]);
exit(1);
}
char root_dir[MAX_PATH_LENGTH];
int th_count = C_THREAD_DEF;
int testing_time = C_TIME_DEF; // in min
file_min_size = C_FILE_MIN_SIZE;
file_max_size = C_FILE_MAX_SIZE;
time_t start, finish;
int i;
#ifdef O_GETOPT_PARSING
// initialization - parsing command-line arguments
char ch;
char *optstr = "c:t:m:M:";
while( -1 != (ch=getopt(argc,argv,optstr))) {
switch(ch) {
case 'c':
th_count = atoi( optarg );
break;
case 't':
testing_time = atoi( optarg );
break;
case 'm':
file_min_size = atoi( optarg );
break;
case 'M':
file_max_size = atoi( optarg );
break;
default:
usage(argv[0]);
exit (1);
}
}
if ( optind+1 > argc ) {
usage(argv[0]);
exit(1);
} else {
strcpy( root_dir, argv[optind] );
}
#else
strcpy( root_dir, argv[1] );
if (argc>2)
{
th_count = atoi( argv[2] );
if (th_count<=0)
{
printf("Invalid 'th' parameter-value specified! Must be > 0!\n");
return 0;
}
}
if (argc>3)
{
testing_time = atoi( argv[3] );
if (testing_time<=0)
{
printf("Invalid 't' parameter-value specified! Must be > 0!\n");
return 0;
}
}
if (argc>4)
{
file_min_size = atoi( argv[4] );
if (testing_time<0)
{
printf("Invalid 'minsize' parameter-value specified! Must be >= 0!\n");
return 0;
}
}
if (argc>5)
{
file_max_size = atoi( argv[5] );
if (file_max_size<file_min_size)
{
printf("Invalid 'maxsize' parameter-value specified! Must be >= %i!", file_min_size);
return 0;
}
}
#endif
printf("Starting crash test for: %s\n"\
"threads: %i\n" \
"for %i minutes\n"
"work files sizes: %i-%i Kb\n\nStarting threads...", root_dir, th_count, testing_time, file_min_size, file_max_size );
time( &start );
// creating threads arguments array, fill it, and start threads
thread_arg_ptr threads = (thread_arg_ptr)malloc( sizeof(thread_arg) * th_count);
statistic st;
st.f_created = 0;
st.f_erased = 0;
st.d_created = 0;
st.d_erased = 0;
st.reading = 0;
st.writing = 0;
st.is_FS_full = false;
// create & fill memory buffer for file I/O
void *buffer = malloc( C_FILE_BUFFER_SIZE );
for (i=0; i<C_FILE_BUFFER_SIZE; i++)
((char*)buffer)[i] = rand() & 0xFF;
for (i=0; i<th_count; i++)
{
threads[i].th_num = i;
threads[i].p_st = &st;
threads[i].buffer = buffer;
threads[i].state = PTH_INIT;
threads[i].root_dir = root_dir;
// starting working thread
pthread_create( &threads[i].THREAD, 0, &thread_func, &threads[i] );
// pthread_t THREAD;
// int res = pthread_create( &THREAD, NULL, &thread_func, NULL );
// if (res)
// printf("Couldn't start child thread!\n");
}
// printf("waiting for a key\n");
// getchar();
// printf("key was pressed\n");
printf("OK!\n");
do {
time( &finish );
char ctm[26];
strcpy( ctm, ctime(&finish) );
ctm[19] = 0x00;
printf("Directories (M/E): %i/%i, Files: %i/%i, Data (R/W): %i/%i, %s\r",
st.d_created, st.d_erased,
st.f_created, st.f_erased,
st.reading, st.writing, &ctm[11] );
sleep(1);
} while ( difftime( finish, start )/60.0 < testing_time );
// stopping all threads.
printf("Exiting...\n");
for (i=0; i<th_count; i++)
{
printf("Stopping thread: %i...", i);
threads[i].state = PTH_STOP_PENDING;
while (threads[i].state != PTH_EXITED);
printf("OK!\n");
}
free(buffer);
free(threads);
return 0;
}