File: [Development] / projects / ltp / tests / kill10.c (download)
Revision 1.1, Tue Apr 17 18:54:56 2001 UTC (16 years, 6 months ago) by nstraz
Branch: MAIN
CVS Tags: HEAD
Adding a new test program that tests signals with lots of processes. This is
what can happen when you get carried away for a week. :) There may be some
race conditions left that will stick MP machines when -g and -n are large.
|
/*
* Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write the Free Software Foundation, Inc., 59
* Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan/
*
*/
/* $Id: kill10.c,v 1.1 2001/04/17 18:54:56 nstraz Exp $ */
/**********************************************************
*
* OS Test - Silicon Graphics, Inc.
*
* TEST IDENTIFIER : kill10
*
* EXECUTED BY : anyone
*
* TEST TITLE : signal flooding test
*
* TEST CASE TOTAL : 1
*
* WALL CLOCK TIME :
*
* CPU TYPES : ALL
*
* AUTHOR : Nate Straz
*
* DATE STARTED : 04/09/2001
*
* INITIAL RELEASE : Linux 2.4.x
*
* TEST CASES
*
* 1.) Create a large number of processes and signal between them.
*
* INPUT SPECIFICATIONS
* The standard options for system call tests are accepted.
* (See the parse_opts(3) man page).
*
* OUTPUT SPECIFICATIONS
*
* DURATION
* Terminates - with frequency and infinite modes.
*
* SIGNALS
* Uses SIGUSR1 to pause before test if option set.
* (See the parse_opts(3) man page).
*
* RESOURCES
* None
*
* ENVIRONMENTAL NEEDS
* No run-time environmental needs.
*
* SPECIAL PROCEDURAL REQUIREMENTS
* None
*
* INTERCASE DEPENDENCIES
* None
*
* DETAILED DESCRIPTION
* This test creates -g groups of -n processes each and prepares them to send
* large numbers of signals. All process fall into three levels.
* * Level 1 - Master
* This is the parent of all processes. It handles test looping
* and making sure that all level 2 Managers report in.
* SIGUSR1 -> ack Manager is ready
* SIGUSR2 -> ack Manager is done and sends reset
* * Level 2 - Managers
* There are -g (default 2) of these processes. They handle
* forking off -n procs and setting up their signal handling.
* Managers are in a pgid with their Children.
* SIGALRM -> Process making your children
* SIGUSR1 ->
* SIGUSR2 -> Reply to Child to stop
* SIGHUP -> Reset child signal counter
* SIGQUIT -> Exit gracefully
* * Level 3 - Child
* There are -n (default 10) of these process per Manager. Their
* only job is to send signals to their Managers when told to by
* the Master.
* SIGUSR1 -> Start signaling Manager
* SIGUSR2 -> Stop signaling Manager
* SIGHUP -> IGNORE
* SIGQUIT -> Exit gracefully
*
* During each test loop, Master sends SIGUSR1 to the pgid of each Manager.
* This tells the Children to start signalling their manager. They do this
* until the manager signals them to stop. Once the manager finds that all
* children have been signaled (by checking them off in the checklist), the
* Manager signals the Master. Once the Master acknowledges that all Managers
* have talked to all their Children, the test iteration is over.
*
* Setup:
* Pause for SIGUSR1 if option specified.
* Fork -g Managers
* Set up signal handling for Children
* Fork -n Children for each manager
* Set up signal handling for Managers
* Set up signal handling for Master
*
* Test:
* Loop if the proper options are given.
* Send SIGUSR1 to all Managers and their Children
* Wait for Managers to send SIGUSR2
*
* Cleanup:
* Send SIGQUIT to all Manager process groups and wait for Manager to quit.
* Print errno log and/or timing stats if options given
*
* Debugging:
* 0 - normal operations
* 1 - Master setup
* 2 - Master processing
* 3 - Master - Manager interaction
* 4 - Manager setup
* 5 - Manager processing
* 6 - Manager - Child interaction
* 7 - Child setup
* 8 - Child processing
*
*
*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#**/
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include "test.h"
#include "usctest.h"
void setup();
void help();
void cleanup();
void fork_pgrps(int pgrps_left);
void manager(int num_procs);
void fork_procs(int procs_left);
/* signal handlers */
void ack_ready(int sig, siginfo_t *si, void *data);
void ack_done(int sig, siginfo_t *si, void *data);
void set_create_procs(int sig);
void graceful_exit(int sig);
void set_signal_parents(int sig);
void clear_signal_parents(int sig);
void set_confirmed_ready(int sig);
void reset_counter(int sig);
void reply_to_child(int sig, siginfo_t *si, void *data);
void wakeup(int sig);
/* pid checklist management */
struct pid_list_item {
pid_t pid;
short flag;
} *child_checklist = NULL;
int child_checklist_total = 0;
int checklist_cmp(const void *a, const void *b);
void checklist_reset(int bit);
inline int k_sigaction(int sig, struct sigaction *sa, struct sigaction *osa);
char *TCID="kill10"; /* Test program identifier. */
int TST_TOTAL=1; /* Total number of test cases. */
extern int Tst_count; /* Test Case counter for tst_* routines */
extern int Tst_nobuf;
int exp_enos[]={0, 0};
int num_procs = 10;
int num_pgrps = 2;
int pgrps_ready = 0;
int child_signal_counter = 0;
int create_procs_flag = 0;
int signal_parents_flag = 0;
int confirmed_ready_flag = 0;
int debug_flag = 0;
pid_t mypid = 0;
char *narg, *garg, *darg;
int nflag=0, gflag=0, dflag=0;
/* for test specific parse_opts options */
option_t options[] = {
{ "n:", &nflag, &narg }, /* -n #procs */
{ "g:", &gflag, &garg }, /* -g #pgrps */
{ "d:", &dflag, &darg }, /* -d <debug level> */
{ NULL, NULL, NULL }
};
/***********************************************************************
* Main
***********************************************************************/
int
main(int ac, char **av)
{
int lc; /* loop counter */
char *msg; /* message returned from parse_opts */
int cnt;
Tst_nobuf=1;
/***************************************************************
* parse standard options
***************************************************************/
/* start off by parsing the command line options. We provide a function
* that understands many common options to control looping. If you are not
* adding any new options, pass NULL in place of options and &help.
*/
if ((msg = parse_opts(ac, av, options, &help))) {
tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
tst_exit();
}
if (nflag) {
if (sscanf(narg, "%i", &num_procs) != 1 ) {
tst_brkm(TBROK, NULL, "-n option arg is not a number");
tst_exit();
}
}
if (gflag) {
if (sscanf(garg, "%i", &num_pgrps) != 1 ) {
tst_brkm(TBROK, NULL, "-g option arg is not a number");
tst_exit();
}
}
if (dflag) {
if (sscanf(darg, "%i", &debug_flag) != 1 ) {
tst_brkm(TBROK, NULL, "-d option arg is not a number");
tst_exit();
}
}
/***************************************************************
* perform global setup for test
***************************************************************/
/* Next you should run a setup routine to make sure your environment is
* sane.
*/
setup();
/* set the expected errnos... */
TEST_EXP_ENOS(exp_enos);
/***************************************************************
* check looping state
***************************************************************/
/* TEST_LOOPING() is a macro that will make sure the test continues
* looping according to the standard command line args.
*/
for (lc=0; TEST_LOOPING(lc); lc++) {
/* reset Tst_count in case we are looping. */
Tst_count=0;
child_signal_counter = 0;
pgrps_ready = 0;
checklist_reset(0x03);
/* send SIGUSR1 to each pgroup */
for (cnt = 0; cnt < child_checklist_total; ++cnt) {
if (debug_flag >= 2)
printf("%d: test_loop, SIGUSR1 -> %d\n",
mypid, -child_checklist[cnt].pid);
kill(-child_checklist[cnt].pid, SIGUSR1);
}
/* wait for the managers to signal they are done */
while (child_signal_counter < num_pgrps) {
alarm(1);
if (debug_flag >= 2)
printf("%d: Master pausing for done (%d/%d)\n",
mypid, child_signal_counter, num_pgrps);
pause();
}
tst_resm(TPASS, "All %d pgrps received their signals", child_signal_counter);
/* Here we clean up after the test case so we can do another iteration.
*/
} /* End for TEST_LOOPING */
/***************************************************************
* cleanup and exit
***************************************************************/
cleanup();
return 0;
} /* End main */
/***************************************************************
* help
***************************************************************/
/* The custom help() function is really simple. Just write your help message to
* standard out. Your help function will be called after the standard options
* have been printed
*/
void
help()
{
printf(" -g n Create n process groups (default: %d)\n", num_pgrps);
printf(" -n n Create n children in each process group (default: %d)\n", num_procs);
printf(" -d n Set debug level to n (default: %d)\n", debug_flag);
}
/***************************************************************
* setup() - performs all ONE TIME setup for this test.
***************************************************************/
void
setup()
{
struct sigaction sa;
int i;
/* You will want to enable some signal handling so you can capture
* unexpected signals like SIGSEGV.
*/
tst_sig(FORK, DEF_HANDLER, cleanup);
/* Pause if that option was specified */
/* One cavet that hasn't been fixed yet. TEST_PAUSE contains the code to
* fork the test with the -c option. You want to make sure you do this
* before you create your temporary directory.
*/
TEST_PAUSE;
mypid = getpid();
sa.sa_handler = SIG_DFL;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (debug_flag >= 1)
printf("%d: setting SIGTRAP -> SIG_DFL\n", mypid);
k_sigaction(SIGTRAP, &sa, 0);
if (debug_flag >= 1)
printf("%d: setting SIGCONT -> SIG_DFL\n", mypid);
k_sigaction(SIGCONT, &sa, 0);
sa.sa_handler = set_create_procs;
if (debug_flag >= 4)
printf("%d: setting SIGALRM -> set_create_procs\n", mypid);
k_sigaction(SIGALRM, &sa, 0);
sa.sa_handler = NULL;
sa.sa_sigaction = ack_ready;
sa.sa_flags = SA_SIGINFO;
if (debug_flag >= 1)
printf("%d: setting SIGUSR1 -> ack_ready\n", mypid);
k_sigaction(SIGUSR1, &sa, 0);
fork_pgrps(num_pgrps);
/* wait for all pgrps to report in */
printf("Master: %d\n", mypid);
while (pgrps_ready < num_pgrps) {
if (debug_flag >= 3)
printf("%d: Master pausing for Managers to check in (%d/%d)\n",
mypid, pgrps_ready, num_pgrps);
pause();
}
checklist_reset(0x03);
printf("Managers: \n");
for (i = 0; i < num_pgrps; i++) { printf("%d ", child_checklist[i].pid); }
printf("\n");
/* set up my signal processing */
/* continue on ALRM */
sa.sa_handler = wakeup;
if (debug_flag >= 4)
printf("%d: setting SIGALRM -> wakeup\n", mypid);
k_sigaction(SIGALRM, &sa, 0);
/* reply to child on USR2 */
sa.sa_handler = NULL;
sa.sa_sigaction = ack_done;
sa.sa_flags = SA_SIGINFO;
if (debug_flag >= 1)
printf("%d: setting SIGUSR2 -> ack_done\n", mypid);
k_sigaction(SIGUSR2, &sa, 0);
}
void
ack_ready(int sig, siginfo_t *si, void *data)
{
struct pid_list_item findit, *result;
findit.pid = si->si_pid;
result = bsearch(&findit, child_checklist, child_checklist_total,
sizeof(*child_checklist), checklist_cmp);
if (result) {
if (!(result->flag & 0x01)) {
if (debug_flag >= 3)
printf("%d: ack_ready, SIGUSR1 -> %d\n", mypid, si->si_pid);
kill(si->si_pid, SIGUSR1);
result->flag = result->flag | 0x01;
++pgrps_ready;
} else {
if (debug_flag >= 3)
printf("%d: ack_ready, already acked %d\n", mypid, si->si_pid);
}
} else {
tst_brkm(TBROK, cleanup,
"received unexpected signal %d from %d", sig, si->si_pid);
}
}
void
ack_done(int sig, siginfo_t *si, void *data)
{
struct pid_list_item findit, *result;
findit.pid = si->si_pid;
result = bsearch(&findit, child_checklist, child_checklist_total,
sizeof(*child_checklist), checklist_cmp);
if (result) {
if (!(result->flag & 0x02)) {
if (debug_flag >= 3)
printf("%d: ack_done, SIGHUP -> %d\n", mypid, si->si_pid);
kill(si->si_pid, SIGHUP);
++child_signal_counter;
result->flag = result->flag | 0x02;
} else {
if (debug_flag >= 3)
printf("%d: ack_done, already told %d\n", mypid, si->si_pid);
}
} else {
tst_brkm(TBROK, cleanup,
"received unexpected signal from %d", si->si_pid);
}
}
/***************************************************************
* cleanup() - performs all ONE TIME cleanup for this test at
* completion or premature exit.
***************************************************************/
void
cleanup()
{
int i;
/* send SIGHUP to all pgroups */
for (i = 0; i < num_pgrps; ++i) {
/* try to do this as nicely as possible */
kill(-child_checklist[i].pid, SIGQUIT);
waitpid(child_checklist[i].pid, NULL, 0);
}
/*
* print timing stats if that option was specified.
* print errno log if that option was specified.
*/
TEST_CLEANUP;
/* exit with return code appropriate for results */
tst_exit();
}
/*********************************************************************
* fork_pgrps() forks off a child, changes it's pgrp, then continues
********************************************************************/
void
fork_pgrps(int pgrps_left)
{
pid_t child;
if (!(child_checklist = calloc(pgrps_left, sizeof(*child_checklist)))) {
tst_brkm(TBROK, cleanup,
"%d: couldn't calloc child_checklist, errno=%d : %s",
mypid, errno, strerror(errno));
}
child_checklist_total = 0;
while (pgrps_left) {
if (debug_flag >= 1)
printf("%d: forking new Manager\n", mypid);
switch (child = fork()) {
case -1:
tst_brkm(TBROK, cleanup,
"fork() failed in fork_pgrps(%d), errno=%d : %s",
pgrps_left, errno, strerror(errno));
break;
case 0: mypid = getpid();
free(child_checklist);
child_checklist = NULL;
manager(num_procs);
break;
default:
child_checklist[child_checklist_total++].pid = child;
setpgid(child, child);
if (debug_flag >= 3)
printf("%d: fork_pgrps, SIGALRM -> %d\n", mypid, child);
kill(child, SIGALRM);
}
--pgrps_left;
}
qsort(child_checklist, child_checklist_total, sizeof(*child_checklist), checklist_cmp);
}
void
set_create_procs(int sig)
{
if (debug_flag >= 3)
printf("%d: Manager cleared to fork\n", getpid());
create_procs_flag++;
return;
}
/*********************************************************************
* new_pgrg() - handle the creation of the pgrp managers and their
* children
********************************************************************/
void
manager(int num_procs)
{
struct sigaction sa;
/* Wait for the parent to change our pgid before we start forking */
while (!create_procs_flag) {
alarm(1);
if (debug_flag >= 3)
printf("%d: Manager pausing, not cleared to fork\n", mypid);
pause();
}
/* set up the signal handling the children will use */
/* ignore ALRM and HUP */
sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
if (debug_flag >= 4)
printf("%d: setting SIGALRM -> SIG_IGN\n", mypid);
k_sigaction(SIGALRM, &sa, 0);
if (debug_flag >= 4)
printf("%d: setting SIGHUP -> SIG_IGN\n", mypid);
k_sigaction(SIGHUP, &sa, 0);
/* exit on QUIT */
sa.sa_handler = graceful_exit;
if (debug_flag >= 4)
printf("%d: setting SIGQUIT -> graceful_exit\n", mypid);
k_sigaction(SIGQUIT, &sa, 0);
/* start signaling on USR1 */
sa.sa_handler = set_signal_parents;
sigfillset(&sa.sa_mask);
if (debug_flag >= 7)
printf("%d: setting SIGUSR1 -> set_signal_parents\n", mypid);
k_sigaction(SIGUSR1, &sa, 0);
/* stop signaling on USR2 */
sa.sa_handler = clear_signal_parents;
if (debug_flag >= 7)
printf("%d: setting SIGUSR2 -> clear_signal_parents\n", mypid);
k_sigaction(SIGUSR2, &sa, 0);
fork_procs(num_procs);
sleep(1); /* wait a sec to let all the children pause */
/* now set up my signal handling */
/* continue on ALRM */
sa.sa_handler = wakeup;
if (debug_flag >= 4)
printf("%d: setting SIGALRM -> wakeup\n", mypid);
k_sigaction(SIGALRM, &sa, 0);
/* mark ready confirmation on USR1 */
sa.sa_handler = set_confirmed_ready;
if (debug_flag >= 4)
printf("%d: setting SIGUSR1 -> set_confirmed_ready\n", mypid);
k_sigaction(SIGUSR1, &sa, 0);
/* reset our counter on HUP */
sa.sa_handler = reset_counter;
if (debug_flag >= 4)
printf("%d: setting SIGHUP -> reset_counter\n", mypid);
k_sigaction(SIGHUP, &sa, 0);
/* reply to child on USR2 */
sa.sa_handler = NULL;
sa.sa_sigaction = reply_to_child;
sa.sa_flags = SA_SIGINFO;
if (debug_flag >= 4)
printf("%d: setting SIGUSR2 -> reply_to_child\n", mypid);
k_sigaction(SIGUSR2, &sa, 0);
/* tell our parent that we are ready to rock */
while (!confirmed_ready_flag) {
if (debug_flag >= 3)
printf("%d: Manager, SIGUSR1 -> %d\n", mypid, getppid());
if (kill(getppid(), SIGUSR1) == -1) {
tst_resm(TWARN, "%d: Couldn't signal master (%d) that we're ready. %d: %s",
mypid, getppid(), errno, strerror(errno));
exit(errno);
}
usleep(100);
}
/* handle pgroup management while the tests are running */
while (1) {
alarm(1);
if (debug_flag >= 5)
printf("%d: Manager pausing (%d/%d)\n",
mypid, child_signal_counter, num_procs);
pause();
if (child_signal_counter >= num_procs) {
confirmed_ready_flag = 0;
tst_resm(TINFO, "%d: All %d children reported in", mypid, child_signal_counter);
while (child_signal_counter) {
if (debug_flag >= 3)
printf("%d: Manager, SIGUSR2 -> %d\n", mypid, getppid());
if (kill(getppid(), SIGUSR2) == -1) {
tst_resm(TINFO, "%d: Couldn't signal master (%d) that we're ready. %d: %s",
mypid, getppid(), errno, strerror(errno));
exit(errno);
}
usleep(100);
}
}
}
}
/* some simple signal handlers for the kids */
void
graceful_exit(int sig)
{
exit(0);
}
void
set_signal_parents(int sig)
{
if (debug_flag >= 8)
printf("%d: Child start signalling\n", mypid);
signal_parents_flag = 1;
}
void
clear_signal_parents(int sig)
{
if (debug_flag >= 8)
printf("%d: Child stop signalling\n", mypid);
signal_parents_flag = 0;
}
void
set_confirmed_ready(int sig)
{
if (debug_flag >= 3)
printf("%d: Manager confirmed ready\n", mypid);
confirmed_ready_flag = 1;
}
void
reset_counter(int sig)
{
checklist_reset(0xFF);
child_signal_counter = 0;
if (debug_flag >= 3)
printf("%d: reset_counter\n", mypid);
}
void
reply_to_child(int sig, siginfo_t *si, void *data)
{
struct pid_list_item findit, *result;
findit.pid = si->si_pid;
result = bsearch(&findit, child_checklist, child_checklist_total,
sizeof(*child_checklist), checklist_cmp);
if (result) {
if (!result->flag) {
if (debug_flag >= 6)
printf("%d: reply_to_child, SIGUSR1 -> %d\n", mypid, si->si_pid);
kill(si->si_pid, SIGUSR2);
++child_signal_counter;
result->flag = 1;
} else {
if (debug_flag >= 6)
printf("%d: reply_to_child, already told %d\n", mypid, si->si_pid);
}
} else {
tst_brkm(TBROK, cleanup,
"received unexpected signal from %d", si->si_pid);
}
}
void wakeup(int sig) { return; }
/*************************************************
* fork_procs() - create all the children
************************************************/
void
fork_procs(int procs_left)
{
pid_t child;
if (!(child_checklist = calloc(procs_left, sizeof(*child_checklist)))) {
tst_brkm(TBROK, cleanup,
"%d: couldn't calloc child_checklist, errno=%d : %s",
mypid, errno, strerror(errno));
}
child_checklist_total = 0;
while (procs_left) {
if (debug_flag >= 4)
printf("%d: forking new Child\n", mypid);
switch (child = fork()) {
case -1: tst_brkm(TBROK, cleanup,
"fork() failed in fork_procs(%d), errno=%d : %s",
procs_left, errno, strerror(errno));
break;
case 0: mypid = getpid();
while (1) {
/* wait to start */
if (debug_flag >= 8)
printf("%d: Child pausing\n", mypid);
signal_parents_flag = 0;
pause();
/* if we started, call mama */
while (signal_parents_flag) {
if (debug_flag >= 6)
printf("%d: child, SIGUSR2 -> %d\n",
mypid, getppid());
if (kill(getppid(), SIGUSR2) == -1) {
/* something went wrong */
tst_resm(TINFO, "%d: kill(ppid:%d, SIGUSR2) failed. %d: %s",
mypid, getppid(), errno, strerror(errno));
exit(errno);
}
usleep(100);
}
}
break;
default:
child_checklist[child_checklist_total++].pid = child;
}
--procs_left;
}
qsort(child_checklist, child_checklist_total, sizeof(*child_checklist), checklist_cmp);
}
int
checklist_cmp(const void *a, const void *b)
{
const struct pid_list_item *pa = (const struct pid_list_item *)a;
const struct pid_list_item *pb = (const struct pid_list_item *)b;
return (pa->pid > pb->pid) - (pa->pid < pb->pid);
}
void
checklist_reset(int bit)
{
int i;
for (i = 0; i < child_checklist_total; i++) {
child_checklist[i].flag = child_checklist[i].flag & (~bit);
}
}
inline int
k_sigaction(int sig, struct sigaction *sa, struct sigaction *osa)
{
int ret;
if ((ret = sigaction(sig, sa, osa)) == -1) {
tst_brkm(TBROK, cleanup,
"sigaction(%d, ...) failed, errno %d: %s",
sig, errno, strerror(errno));
}
return ret;
}