/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements . See the NOTICE file distributed with
* this work for additional information regarding copyright ownership .
* The ASF licenses this file to You under the Apache License , Version 2 . 0
* ( the " License " ) ; you may not use this file except in compliance with
* the License . You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*/
/*
time - sem . c has the basics of the semaphores we use in http_main . c . It ' s
intended for timing differences between various methods on an
architecture . In practice we ' ve found many things affect which semaphore
to be used :
- NFS filesystems absolutely suck for fcntl ( ) and flock ( )
- uslock absolutely sucks on single - processor IRIX boxes , but
absolutely rocks on multi - processor boxes . The converse
is true for fcntl . sysvsem seems a moderate balance .
- Under Solaris you can ' t have too many processes use SEM_UNDO , there
might be a tuneable somewhere that increases the limit from 29 .
We ' re not sure what the tunable is , so there ' s a define
NO_SEM_UNDO which can be used to simulate us trapping / blocking
signals to be able to properly release the semaphore on a clean
child death . You ' ll also need to define NEED_UNION_SEMUN
under solaris .
You ' ll need to define USE_SHMGET_SCOREBOARD if anonymous shared mmap ( )
doesn ' t work on your system ( i . e . linux ) .
argv [ 1 ] is the # children , argv [ 2 ] is the # iterations per child
You should run each over many different # children inputs , and choose
# iter such that the program runs for at least a second or so . . . or even
longer depending on your patience .
compile with :
gcc - o time - FCNTL - Wall - O time - sem . c - DUSE_FCNTL_SERIALIZED_ACCEPT
gcc - o time - FLOCK - Wall - O time - sem . c - DUSE_FLOCK_SERIALIZED_ACCEPT
gcc - o time - SYSVSEM - Wall - O time - sem . c - DUSE_SYSVSEM_SERIALIZED_ACCEPT
gcc - o time - SYSVSEM2 - Wall - O time - sem . c - DUSE_SYSVSEM_SERIALIZED_ACCEPT - DNO_SEM_UNDO
gcc - o time - PTHREAD - Wall - O time - sem . c - DUSE_PTHREAD_SERIALIZED_ACCEPT - lpthread
gcc - o time - USLOCK - Wall - O time - sem . c - DUSE_USLOCK_SERIALIZED_ACCEPT
not all versions work on all systems .
*/
#include <errno.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <signal.h>
#if defined (USE_FCNTL_SERIALIZED_ACCEPT)
static struct flock lock_it;
static struct flock unlock_it;
static int fcntl_fd=-1 ;
#define accept_mutex_child_init()
#define accept_mutex_cleanup()
/*
* Initialize mutex lock .
* Must be safe to call this on a restart .
*/
void
accept_mutex_init(void )
{
lock_it.l_whence = SEEK_SET; /* from current point */
lock_it.l_start = 0 ; /* -"- */
lock_it.l_len = 0 ; /* until end of file */
lock_it.l_type = F_WRLCK; /* set exclusive/write lock */
lock_it.l_pid = 0 ; /* pid not actually interesting */
unlock_it.l_whence = SEEK_SET; /* from current point */
unlock_it.l_start = 0 ; /* -"- */
unlock_it.l_len = 0 ; /* until end of file */
unlock_it.l_type = F_UNLCK; /* set exclusive/write lock */
unlock_it.l_pid = 0 ; /* pid not actually interesting */
printf("opening test-lock-thing in current directory\n" );
fcntl_fd = open("test-lock-thing" , O_CREAT | O_WRONLY | O_EXCL, 0644 );
if (fcntl_fd == -1 )
{
perror ("open" );
fprintf (stderr, "Cannot open lock file: %s\n" , "test-lock-thing" );
exit (1 );
}
unlink("test-lock-thing" );
}
void accept_mutex_on(void )
{
int ret;
while ((ret = fcntl(fcntl_fd, F_SETLKW, &lock_it)) < 0 && errno == EINTR)
continue ;
if (ret < 0 ) {
perror ("fcntl lock_it" );
exit (1 );
}
}
void accept_mutex_off(void )
{
if (fcntl (fcntl_fd, F_SETLKW, &unlock_it) < 0 )
{
perror ("fcntl unlock_it" );
exit (1 );
}
}
#elif defined (USE_FLOCK_SERIALIZED_ACCEPT)
#include <sys/file.h>
static int flock_fd=-1 ;
#define FNAME "test-lock-thing"
/*
* Initialize mutex lock .
* Must be safe to call this on a restart .
*/
void accept_mutex_init(void )
{
printf("opening " FNAME " in current directory\n" );
flock_fd = open(FNAME, O_CREAT | O_WRONLY | O_EXCL, 0644 );
if (flock_fd == -1 )
{
perror ("open" );
fprintf (stderr, "Cannot open lock file: %s\n" , "test-lock-thing" );
exit (1 );
}
}
void accept_mutex_child_init(void )
{
flock_fd = open(FNAME, O_WRONLY, 0600 );
if (flock_fd == -1 ) {
perror("open" );
exit (1 );
}
}
void accept_mutex_cleanup(void )
{
unlink(FNAME);
}
void accept_mutex_on(void )
{
int ret;
while ((ret = flock(flock_fd, LOCK_EX)) < 0 && errno == EINTR)
continue ;
if (ret < 0 ) {
perror ("flock(LOCK_EX)" );
exit (1 );
}
}
void accept_mutex_off(void )
{
if (flock (flock_fd, LOCK_UN) < 0 )
{
perror ("flock(LOCK_UN)" );
exit (1 );
}
}
#elif defined (USE_SYSVSEM_SERIALIZED_ACCEPT)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
static int sem_id = -1 ;
#ifdef NO_SEM_UNDO
static sigset_t accept_block_mask;
static sigset_t accept_previous_mask;
#endif
#define accept_mutex_child_init()
#define accept_mutex_cleanup()
void accept_mutex_init(void )
{
#ifdef NEED_UNION_SEMUN
/* believe it or not, you need to define this under solaris */
union semun {
int val;
struct semid_ds *buf;
ushort *array;
};
#endif
union semun ick;
sem_id = semget(999 , 1 , IPC_CREAT | 0666 );
if (sem_id < 0 ) {
perror ("semget" );
exit (1 );
}
ick.val = 1 ;
if (semctl(sem_id, 0 , SETVAL, ick) < 0 ) {
perror ("semctl" );
exit (1 );
}
#ifdef NO_SEM_UNDO
sigfillset(&accept_block_mask);
sigdelset(&accept_block_mask, SIGHUP);
sigdelset(&accept_block_mask, SIGTERM);
sigdelset(&accept_block_mask, SIGUSR1);
#endif
}
void accept_mutex_on()
{
struct sembuf op;
#ifdef NO_SEM_UNDO
if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
perror("sigprocmask(SIG_BLOCK)" );
exit (1 );
}
op.sem_flg = 0 ;
#else
op.sem_flg = SEM_UNDO;
#endif
op.sem_num = 0 ;
op.sem_op = -1 ;
if (semop(sem_id, &op, 1 ) < 0 ) {
perror ("accept_mutex_on" );
exit (1 );
}
}
void accept_mutex_off()
{
struct sembuf op;
op.sem_num = 0 ;
op.sem_op = 1 ;
#ifdef NO_SEM_UNDO
op.sem_flg = 0 ;
#else
op.sem_flg = SEM_UNDO;
#endif
if (semop(sem_id, &op, 1 ) < 0 ) {
perror ("accept_mutex_off" );
exit (1 );
}
#ifdef NO_SEM_UNDO
if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
perror("sigprocmask(SIG_SETMASK)" );
exit (1 );
}
#endif
}
#elif defined (USE_PTHREAD_SERIALIZED_ACCEPT)
/* note: pthread mutexes aren't released on child death, hence the
* signal goop . . . in a real implementation we ' d do special things
* during hup , term , usr1 .
*/
#include <pthread.h>
static pthread_mutex_t *mutex;
static sigset_t accept_block_mask;
static sigset_t accept_previous_mask;
#define accept_mutex_child_init()
#define accept_mutex_cleanup()
void accept_mutex_init(void )
{
pthread_mutexattr_t mattr;
int fd;
fd = open ("/dev/zero" , O_RDWR);
if (fd == -1 ) {
perror ("open(/dev/zero)" );
exit (1 );
}
mutex = (pthread_mutex_t *)mmap ((caddr_t)0 , sizeof (*mutex),
PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 );
if (mutex == (void *)(caddr_t)-1 ) {
perror ("mmap" );
exit (1 );
}
close (fd);
if (pthread_mutexattr_init(&mattr)) {
perror ("pthread_mutexattr_init" );
exit (1 );
}
if (pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED)) {
perror ("pthread_mutexattr_setpshared" );
exit (1 );
}
if (pthread_mutex_init(mutex, &mattr)) {
perror ("pthread_mutex_init" );
exit (1 );
}
sigfillset(&accept_block_mask);
sigdelset(&accept_block_mask, SIGHUP);
sigdelset(&accept_block_mask, SIGTERM);
sigdelset(&accept_block_mask, SIGUSR1);
}
void accept_mutex_on()
{
if (sigprocmask(SIG_BLOCK, &accept_block_mask, &accept_previous_mask)) {
perror("sigprocmask(SIG_BLOCK)" );
exit (1 );
}
if (pthread_mutex_lock (mutex)) {
perror ("pthread_mutex_lock" );
exit (1 );
}
}
void accept_mutex_off()
{
if (pthread_mutex_unlock (mutex)) {
perror ("pthread_mutex_unlock" );
exit (1 );
}
if (sigprocmask(SIG_SETMASK, &accept_previous_mask, NULL)) {
perror("sigprocmask(SIG_SETMASK)" );
exit (1 );
}
}
#elif defined (USE_USLOCK_SERIALIZED_ACCEPT)
#include <ulocks.h>
static usptr_t *us = NULL;
static ulock_t uslock = NULL;
#define accept_mutex_child_init()
#define accept_mutex_cleanup()
void accept_mutex_init(void )
{
ptrdiff_t old;
/* default is 8 */
#define CONF_INITUSERS_MAX 15
if ((old = usconfig(CONF_INITUSERS, CONF_INITUSERS_MAX)) == -1 ) {
perror("usconfig" );
exit (-1 );
}
if ((old = usconfig(CONF_LOCKTYPE, US_NODEBUG)) == -1 ) {
perror("usconfig" );
exit (-1 );
}
if ((old = usconfig(CONF_ARENATYPE, US_SHAREDONLY)) == -1 ) {
perror("usconfig" );
exit (-1 );
}
if ((us = usinit("/dev/zero" )) == NULL) {
perror("usinit" );
exit (-1 );
}
if ((uslock = usnewlock(us)) == NULL) {
perror("usnewlock" );
exit (-1 );
}
}
void accept_mutex_on()
{
switch (ussetlock(uslock)) {
case 1 :
/* got lock */
break ;
case 0 :
fprintf(stderr, "didn't get lock\n" );
exit (-1 );
case -1 :
perror("ussetlock" );
exit (-1 );
}
}
void accept_mutex_off()
{
if (usunsetlock(uslock) == -1 ) {
perror("usunsetlock" );
exit (-1 );
}
}
#endif
#ifndef USE_SHMGET_SCOREBOARD
static void *get_shared_mem(apr_size_t size)
{
void *result;
/* allocate shared memory for the shared_counter */
result = (unsigned long *)mmap ((caddr_t)0 , size,
PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1 , 0 );
if (result == (void *)(caddr_t)-1 ) {
perror ("mmap" );
exit (1 );
}
return result;
}
#else
#include <sys/types.h>
#include <sys/ipc.h>
#ifdef HAVE_SYS_MUTEX_H
#include <sys/mutex.h>
#endif
#include <sys/shm.h>
static void *get_shared_mem(apr_size_t size)
{
key_t shmkey = IPC_PRIVATE;
int shmid = -1 ;
void *result;
#ifdef MOVEBREAK
char *obrk;
#endif
if ((shmid = shmget(shmkey, size, IPC_CREAT | SHM_R | SHM_W)) == -1 ) {
perror("shmget" );
exit (1 );
}
#ifdef MOVEBREAK
/*
* Some SysV systems place the shared segment WAY too close
* to the dynamic memory break point ( sbrk ( 0 ) ) . This severely
* limits the use of malloc / sbrk in the program since sbrk will
* refuse to move past that point .
*
* To get around this , we move the break point " way up there " ,
* attach the segment and then move break back down . Ugly
*/
if ((obrk = sbrk(MOVEBREAK)) == (char *) -1 ) {
perror("sbrk" );
}
#endif
#define BADSHMAT ((void *)(-1 ))
if ((result = shmat(shmid, 0 , 0 )) == BADSHMAT) {
perror("shmat" );
}
/*
* We must avoid leaving segments in the kernel ' s
* ( small ) tables .
*/
if (shmctl(shmid, IPC_RMID, NULL) != 0 ) {
perror("shmctl(IPC_RMID)" );
}
if (result == BADSHMAT) /* now bailout */
exit (1 );
#ifdef MOVEBREAK
if (obrk == (char *) -1 )
return ; /* nothing else to do */
if (sbrk(-(MOVEBREAK)) == (char *) -1 ) {
perror("sbrk 2" );
}
#endif
return result;
}
#endif
#ifdef _POSIX_PRIORITY_SCHEDULING
/* don't ask */
#define _P __P
#include <sched.h>
#define YIELD sched_yield()
#else
#define YIELD do { struct timeval zero; zero.tv_sec = zero.tv_usec = 0 ; select(0 ,0 ,0 ,0 ,&zero); } while (0 )
#endif
void main (int argc, char **argv)
{
int num_iter;
int num_child;
int i;
struct timeval first;
struct timeval last;
long ms;
int pid;
unsigned long *shared_counter;
if (argc != 3 ) {
fprintf (stderr, "Usage: time-sem num-child num iter\n" );
exit (1 );
}
num_child = atoi (argv[1 ]);
num_iter = atoi (argv[2 ]);
/* allocate shared memory for the shared_counter */
shared_counter = get_shared_mem(sizeof (*shared_counter));
/* initialize counter to 0 */
*shared_counter = 0 ;
accept_mutex_init ();
/* parent grabs mutex until done spawning children */
accept_mutex_on ();
for (i = 0 ; i < num_child; ++i) {
pid = fork();
if (pid == 0 ) {
/* child, do our thing */
accept_mutex_child_init();
for (i = 0 ; i < num_iter; ++i) {
unsigned long tmp;
accept_mutex_on ();
tmp = *shared_counter;
YIELD;
*shared_counter = tmp + 1 ;
accept_mutex_off ();
}
exit (0 );
} else if (pid == -1 ) {
perror ("fork" );
accept_mutex_off ();
exit (1 );
}
}
/* a quick test to see that nothing is screwed up */
if (*shared_counter != 0 ) {
puts ("WTF! shared_counter != 0 before the children have been started!" );
accept_mutex_off ();
exit (1 );
}
gettimeofday (&first, NULL);
/* launch children into action */
accept_mutex_off ();
for (i = 0 ; i < num_child; ++i) {
if (wait(NULL) == -1 ) {
perror ("wait" );
}
}
gettimeofday (&last, NULL);
if (*shared_counter != num_child * num_iter) {
printf ("WTF! shared_counter != num_child * num_iter!\n"
"shared_counter = %lu\nnum_child = %d\nnum_iter=%d\n" ,
*shared_counter,
num_child, num_iter);
}
last.tv_sec -= first.tv_sec;
ms = last.tv_usec - first.tv_usec;
if (ms < 0 ) {
--last.tv_sec;
ms += 1000000 ;
}
last.tv_usec = ms;
printf ("%8lu.%06lu\n" , last.tv_sec, last.tv_usec);
accept_mutex_cleanup();
exit (0 );
}
Messung V0.5 in Prozent C=95 H=93 G=93
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-06-09)
¤
*© Formatika GbR, Deutschland