blob: 35263704b3a4aab7809f9a23ff49b47d4b3c1a14 [file] [log] [blame]
/*
* Copyright (C) Bull S.A. 1996
* Copyright (c) International Business Machines Corp., 2001
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*---------------------------------------------------------------------+
| shmem_test_03 |
| ==================================================================== |
| |
| Description: Verify shared memory mapping of /dev/zero with |
| exclusive writes and shared reads using semaphores |
| as the synchronization method. |
| |
| Algorithm: o Obtain three shared memory segments by mapping |
| /dev/zero, one for random data created by the |
| parent, another for the childs checksums, and the |
| last for the read count (number of child processes |
| reading the data). |
| o Spawn N child processes |
| o Parent: |
| - obtain write lock (exclusive) on data |
| - fill shared memory segment with data |
| - compute data checksum |
| - release lock |
| o Child: |
| - obtain read lock (shared) on data |
| - compute data checksum |
| - release lock |
| - store checksum in other shared memory segment |
| o Parent: |
| - wait for child proceeses to complete |
| - compare child's checksum (from shared memory) |
| with that of the parent |
| |
| System calls: The following system calls are tested: |
| |
| mmap () - Maps a file-system object into memory |
| semget () - Gets a set of semaphores |
| semctl () - Controls semaphore operations |
| semop () - Preforms semaphore operations |
| |
| Usage: shmem_test_03 [-c num_children] [-s shmem_size] |
| |
| To compile: cc -o shmem_test_03 shmem_test_03.c |
| |
| Last update: Ver. 1.2, 2/8/94 00:08:45 |
| |
| Change Activity |
| |
| Version Date Name Reason |
| 0.1 012093 DJK Initial version for AIX 4.1 |
| 1.2 020794 DJK Moved to "prod" directory |
| |
+---------------------------------------------------------------------*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <sys/file.h>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/sem.h>
#include <sys/signal.h>
#include <sys/types.h>
#include <sys/wait.h>
/* Defines
*
* USAGE: usage statement
*/
#define MAX_CHILDREN 400
#define DEFAULT_NUM_CHILDREN 2
#define DEFAULT_SHMEM_SIZE 100000
#define USAGE "\nUsage: %s [-c num_children] [-s shmem_size]\n\n" \
"\t-c num_children number of child processes to spawn\n" \
"\t-s shmem_size size of shared memory segment (bytes)\n" \
"\t (must be less than 256MB!)\n\n"
/*
* Function prototypes
*
* create_semaphores (): Create semaphores for synchorizing memory accesses
* delete_semaphores (): Delete the semaphores
* lock_resource (): Obtains the resource (shared memory segment)
* unlock_resource (): Releases the resource (shared memory segment)
* parse_args (): Parse command line arguments
* setup_signal_handlers (): Setup the signal catching function
* handler (): Signal catching function
* child (): Child process
* sys_error (): System error message function
* error (): Error message function
* cleanup (): Releases semaphores & kills child processes
*/
static void create_semaphores();
static void delete_semaphores();
static void lock_resource(int);
static void unlock_resource(int);
static void parse_args(int, char **);
static void setup_signal_handlers();
static void handler(int, int, struct sigcontext *);
static void child(int, unsigned char *);
static void sys_error(const char *, int);
static void error(const char *, int);
static void cleanup();
/*
* Global Variables:
*
* checksum: Array of checksums computed by child processes
* parent_pid: Process id of the parent
* pid: Array of child process id's
* semid: System wide unique shared memory identifier
* num_children: number of child processes to spawn
* buffer_size: size of "scratch" shared memory segment
* read_count: number of child processes reading data
*/
enum { READ_COUNT, WRITE }; /* semaphore constants */
int *read_count;
unsigned long *checksum; /* shared memory segment address */
pid_t parent_pid; /* process id of parent */
pid_t pid[MAX_CHILDREN]; /* child processes process id's */
int semid; /* semaphore id */
int num_children = DEFAULT_NUM_CHILDREN;
int buffer_size = DEFAULT_SHMEM_SIZE;
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;
/*---------------------------------------------------------------------+
| main |
| ==================================================================== |
| |
| Function: Main program (see prolog for more details) |
| |
| Returns: (0) Successful completion |
| (-1) Error occurred |
| |
+---------------------------------------------------------------------*/
int main(int argc, char **argv)
{
int fd; /* Misc file descriptor */
int i; /* Misc loop index */
int shmem_size; /* Size (in bytes) of shared memory segment */
int status; /* Child processes exit status */
unsigned char *ptr; /* Misc pointer */
unsigned char data = 0; /* Value written into shared memory segment */
unsigned char *shmptr; /* Shared memory segment address */
unsigned long cksum; /* Shared memory segment checksum */
/*
* Parse command line arguments and print out program header
*/
parse_args(argc, argv);
printf("%s: IPC Shared Memory TestSuite program\n", *argv);
/*
* Setup the signal handlers (in case user aborts program).
*
* Create the semaphores to insure exclusive writes to the
* shared memory segment.
*
* Save the parent process id and initialize the array of child
* process ids.
*/
setup_signal_handlers();
create_semaphores();
parent_pid = getpid();
for (i = 0; i < num_children; i++)
pid[i] = (pid_t) 0;
/*
* Create a shared memory segment for storing the read count
* (number of child processes reading shared data)
* After creating the shared memory segment, initialize it.
*/
if ((fd = open("/dev/zero", O_RDWR)) < 0)
sys_error("open failed", __LINE__);
if ((read_count = (int *)
mmap(0, sizeof(int), PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0)) < 0)
sys_error("mmap failed", __LINE__);
close(fd);
*read_count = 0;
/*
* Create a shared memory segment for storing the child
* processes checksums by memory mapping /dev/zero.
* After creating the shared memory segment, initialize it.
*/
if ((fd = open("/dev/zero", O_RDWR)) < 0)
sys_error("open failed", __LINE__);
shmem_size = sizeof(unsigned long) * num_children;
if ((checksum = (unsigned long *)
mmap(0, shmem_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0)) < 0)
sys_error("mmap failed", __LINE__);
close(fd);
for (i = 0; i < num_children; i++)
*(checksum + (sizeof(unsigned long) * i)) = 0;
/*
* Create the "scratch" shared memory segment for storing
* a series of values by memory mapping /dev/zero.
*/
if ((fd = open("/dev/zero", O_RDWR)) < 0)
sys_error("open failed", __LINE__);
printf("\n\tGet shared memory segment (%d bytes)\n", buffer_size);
if ((shmptr = mmap(0, buffer_size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0)) < 0)
sys_error("mmap failed", __LINE__);
close(fd);
/*
* Obtain an exclusive "write" lock on the shared memory data
* segment -- get lock now to insure the parent process gets
* first access to the segment.
*/
lock_resource(WRITE);
/*
* Spawn all N child processes. Each child process will compute
* the checksum of the shared memory segment and will store
* the results in the other shared memory segment accessible
* by the parent.
*/
printf("\n\tSpawning %d child processes ... \n", num_children);
for (i = 0; i < num_children; i++) {
if ((pid[i] = fork()) == (pid_t) 0) {
child(i, shmptr);
exit(0);
} else if (pid[i] < (pid_t) 0)
sys_error("fork failed", __LINE__);
}
/*
* Fill the "scratch" shared memory segment up with data and
* compute the segments checksum. Release "write" lock after
* completing so that the child processes may begin to read the
* data.
*/
printf("\n\tParent: calculate shared memory segment checksum\n");
cksum = data = 0;
for (ptr = shmptr; ptr < (shmptr + buffer_size); ptr++) {
*ptr = (data++) % (UCHAR_MAX + 1);
cksum += *ptr;
}
printf("\t shared memory checksum %08lx\n", cksum);
unlock_resource(WRITE);
/*
* Wait for the child processes to compute the checksums and complete.
* After the processes complete, check their exit status to insure
* that they ran to completion and then verify the corresponding
* checksum.
*/
for (i = 0; i < num_children; i++) {
waitpid(pid[i], &status, 0);
if (!WIFEXITED(status))
sys_error("child process terminated abnormally",
__LINE__);
if (cksum != *(checksum + (sizeof(unsigned long) * i))) {
printf("checksum [%d] = %08lx\n", i, checksum[i]);
error("checksums do not match", __LINE__);
}
}
printf("\n\tParent: children calculated segment successfully\n");
/*
* Program completed successfully, cleanup semaphores and exit.
*/
delete_semaphores();
printf("\nsuccessful!\n");
return (0);
}
/*---------------------------------------------------------------------+
| child () |
| ==================================================================== |
| |
| Function: Waits for read access to the shared memory segment, |
| computes the shared memory segments checksum and releases |
| the read lock. Then stores the checksum. |
| |
| Updates: checksum - shared memory segment containing checksums |
| computed by child processes |
| |
+---------------------------------------------------------------------*/
static void child(int num, unsigned char *shmptr)
{
unsigned long cksum = 0; /* Shared memory regions checksum */
int i; /* Misc index */
/*
* Wait for a READ_COUNT lock on the shared memory segment, then
* compute the checksum and release the READ_COUNT lock.
*/
lock_resource(READ_COUNT);
(*read_count)++;
if (*read_count == 1)
lock_resource(WRITE);
unlock_resource(READ_COUNT);
for (i = 0; i < buffer_size; i++)
cksum += *shmptr++;
lock_resource(READ_COUNT);
(*read_count)--;
if (*read_count == 0)
unlock_resource(WRITE);
unlock_resource(READ_COUNT);
/*
* Store the resulting checksum and print out a message
*/
checksum[num] = cksum;
*(checksum + (sizeof(unsigned long) * num)) = cksum;
printf("\t\tchild (%02d): checksum %08lx\n", num,
*(checksum + (sizeof(unsigned long) * num)));
}
/*---------------------------------------------------------------------+
| create_semaphores () |
| ==================================================================== |
| |
| Function: Creates two semaphores: |
| |
| READ_COUNT: shared read semaphore |
| WRITE: exclusive write semaphore |
| |
| Updates: semid - system wide semaphore identifier |
| |
+---------------------------------------------------------------------*/
static void create_semaphores()
{
int nsems = 2; /* Number of semaphores to create */
/*
* Create two system unique semaphores.
*/
if ((semid = semget(IPC_PRIVATE, nsems, IPC_CREAT | 0666)) < 0)
sys_error("semget failed", __LINE__);
arg.val = 1;
if (semctl(semid, WRITE, SETVAL, arg) < 0)
sys_error("semctl (SETVAL) failed", __LINE__);
if (semctl(semid, READ_COUNT, SETVAL, arg) < 0)
sys_error("semctl (SETVAL) failed", __LINE__);
}
/*---------------------------------------------------------------------+
| delete_semaphores () |
| ==================================================================== |
| |
| Function: Deletes the two READ/WRITE semaphores. |
| |
+---------------------------------------------------------------------*/
static void delete_semaphores()
{
int nsems = 2;
/*
* Delete both READ_COUNT and WRITE semaphores.
*/
arg.val = 0;
if (semctl(semid, nsems, IPC_RMID, arg) < 0)
sys_error("semctl (IPC_RMID) failed", __LINE__);
}
/*---------------------------------------------------------------------+
| lock_resource () |
| ==================================================================== |
| |
| Function: Obtains a READ/WRITE semaphore lock. |
| |
+---------------------------------------------------------------------*/
static void lock_resource(int semaphore)
{
struct sembuf buf;
buf.sem_op = -1; /* Obtain resource */
buf.sem_num = semaphore;
buf.sem_flg = 0;
if (semop(semid, &buf, 1) < 0)
sys_error("semop (LOCK) failed", __LINE__);
}
/*---------------------------------------------------------------------+
| unlock_resource () |
| ==================================================================== |
| |
| Function: Releases a READ/WRITE semaphore lock. |
| |
+---------------------------------------------------------------------*/
static void unlock_resource(int semaphore)
{
struct sembuf buf;
buf.sem_op = 1; /* Release resource */
buf.sem_num = semaphore;
buf.sem_flg = 0;
if (semop(semid, &buf, 1) < 0)
sys_error("semop (UNLOCK) failed", __LINE__);
}
/*---------------------------------------------------------------------+
| parse_args () |
| ==================================================================== |
| |
| Function: Parse the command line arguments & initialize global |
| variables. |
| |
| Updates: (command line options) |
| |
| [-s] size: shared memory segment size |
| |
| [-c] num_children: number of child processes |
| |
+---------------------------------------------------------------------*/
void parse_args(int argc, char **argv)
{
int i;
int errflag = 0;
char *program_name = *argv;
extern char *optarg; /* Command line option */
while ((i = getopt(argc, argv, "c:s:?")) != EOF) {
switch (i) {
case 'c':
num_children = atoi(optarg);
break;
case 's':
buffer_size = atoi(optarg);
break;
case '?':
errflag++;
break;
}
}
if (num_children >= MAX_CHILDREN) {
errflag++;
fprintf(stderr, "ERROR: num_children must be less than %d\n",
MAX_CHILDREN);
}
if (errflag) {
fprintf(stderr, USAGE, program_name);
exit(2);
}
}
/*---------------------------------------------------------------------+
| setup_handler () |
| ==================================================================== |
| |
| Function: Setup the signal handler for SIGINT. |
| |
+---------------------------------------------------------------------*/
void setup_signal_handlers()
{
struct sigaction invec;
invec.sa_handler = (void (*)(int))handler;
sigemptyset(&invec.sa_mask);
invec.sa_flags = 0;
if (sigaction(SIGINT, &invec, (struct sigaction *)NULL) < 0)
sys_error("sigaction failed", __LINE__);
}
/*---------------------------------------------------------------------+
| handler () |
| ==================================================================== |
| |
| Function: Signal catching function for SIGINT signal |
| |
| o SIGINT: Parent process calls cleanup, child processes |
| simply exit |
| |
| o Other: Print message and abort program... |
| |
+---------------------------------------------------------------------*/
void handler(int sig, int code, struct sigcontext *scp)
{
char msg[100]; /* Buffer for error message */
if (sig == SIGINT) {
if (getpid() == parent_pid) {
fprintf(stderr, "Received SIGINT -- cleaning up...\n");
fflush(stderr);
cleanup();
} else
exit(-1);
} else {
sprintf(msg, "Received an unexpected signal (%d)", sig);
error(msg, __LINE__);
}
}
/*---------------------------------------------------------------------+
| cleanup () |
| ==================================================================== |
| |
| Function: Closes all of the pipes, kills all of the child |
| processes and exits the program... |
| |
+---------------------------------------------------------------------*/
void cleanup()
{
int i;
if (getpid() == parent_pid) {
delete_semaphores();
for (i = 0; i < num_children; i++) {
if (pid[i] > (pid_t) 0 && kill(pid[i], SIGKILL) < 0)
sys_error("signal failed", __LINE__);
}
}
exit(-1);
}
/*---------------------------------------------------------------------+
| sys_error () |
| ==================================================================== |
| |
| Function: Creates system error message and calls error () |
| |
+---------------------------------------------------------------------*/
void sys_error(const char *msg, int line)
{
char syserr_msg[256];
sprintf(syserr_msg, "%s: %s\n", msg, strerror(errno));
error(syserr_msg, line);
}
/*---------------------------------------------------------------------+
| error () |
| ==================================================================== |
| |
| Function: Prints out message and exits... |
| |
+---------------------------------------------------------------------*/
void error(const char *msg, int line)
{
fprintf(stderr, "ERROR [line: %d] %s\n", line, msg);
cleanup();
}