fork.c (5163B)
1// SPDX-License-Identifier: GPL-2.0+ 2 3/* 4 * Context switch microbenchmark. 5 * 6 * Copyright 2018, Anton Blanchard, IBM Corp. 7 */ 8 9#define _GNU_SOURCE 10#include <assert.h> 11#include <errno.h> 12#include <getopt.h> 13#include <limits.h> 14#include <linux/futex.h> 15#include <pthread.h> 16#include <sched.h> 17#include <signal.h> 18#include <stdio.h> 19#include <stdlib.h> 20#include <string.h> 21#include <sys/shm.h> 22#include <sys/syscall.h> 23#include <sys/time.h> 24#include <sys/types.h> 25#include <sys/wait.h> 26#include <unistd.h> 27 28static unsigned int timeout = 30; 29 30static void set_cpu(int cpu) 31{ 32 cpu_set_t cpuset; 33 34 if (cpu == -1) 35 return; 36 37 CPU_ZERO(&cpuset); 38 CPU_SET(cpu, &cpuset); 39 40 if (sched_setaffinity(0, sizeof(cpuset), &cpuset)) { 41 perror("sched_setaffinity"); 42 exit(1); 43 } 44} 45 46static void start_process_on(void *(*fn)(void *), void *arg, int cpu) 47{ 48 int pid; 49 50 pid = fork(); 51 if (pid == -1) { 52 perror("fork"); 53 exit(1); 54 } 55 56 if (pid) 57 return; 58 59 set_cpu(cpu); 60 61 fn(arg); 62 63 exit(0); 64} 65 66static int cpu; 67static int do_fork = 0; 68static int do_vfork = 0; 69static int do_exec = 0; 70static char *exec_file; 71static int exec_target = 0; 72static unsigned long iterations; 73static unsigned long iterations_prev; 74 75static void run_exec(void) 76{ 77 char *const argv[] = { "./exec_target", NULL }; 78 79 if (execve("./exec_target", argv, NULL) == -1) { 80 perror("execve"); 81 exit(1); 82 } 83} 84 85static void bench_fork(void) 86{ 87 while (1) { 88 pid_t pid = fork(); 89 if (pid == -1) { 90 perror("fork"); 91 exit(1); 92 } 93 if (pid == 0) { 94 if (do_exec) 95 run_exec(); 96 _exit(0); 97 } 98 pid = waitpid(pid, NULL, 0); 99 if (pid == -1) { 100 perror("waitpid"); 101 exit(1); 102 } 103 iterations++; 104 } 105} 106 107static void bench_vfork(void) 108{ 109 while (1) { 110 pid_t pid = vfork(); 111 if (pid == -1) { 112 perror("fork"); 113 exit(1); 114 } 115 if (pid == 0) { 116 if (do_exec) 117 run_exec(); 118 _exit(0); 119 } 120 pid = waitpid(pid, NULL, 0); 121 if (pid == -1) { 122 perror("waitpid"); 123 exit(1); 124 } 125 iterations++; 126 } 127} 128 129static void *null_fn(void *arg) 130{ 131 pthread_exit(NULL); 132} 133 134static void bench_thread(void) 135{ 136 pthread_t tid; 137 cpu_set_t cpuset; 138 pthread_attr_t attr; 139 int rc; 140 141 rc = pthread_attr_init(&attr); 142 if (rc) { 143 errno = rc; 144 perror("pthread_attr_init"); 145 exit(1); 146 } 147 148 if (cpu != -1) { 149 CPU_ZERO(&cpuset); 150 CPU_SET(cpu, &cpuset); 151 152 rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset); 153 if (rc) { 154 errno = rc; 155 perror("pthread_attr_setaffinity_np"); 156 exit(1); 157 } 158 } 159 160 while (1) { 161 rc = pthread_create(&tid, &attr, null_fn, NULL); 162 if (rc) { 163 errno = rc; 164 perror("pthread_create"); 165 exit(1); 166 } 167 rc = pthread_join(tid, NULL); 168 if (rc) { 169 errno = rc; 170 perror("pthread_join"); 171 exit(1); 172 } 173 iterations++; 174 } 175} 176 177static void sigalrm_handler(int junk) 178{ 179 unsigned long i = iterations; 180 181 printf("%ld\n", i - iterations_prev); 182 iterations_prev = i; 183 184 if (--timeout == 0) 185 kill(0, SIGUSR1); 186 187 alarm(1); 188} 189 190static void sigusr1_handler(int junk) 191{ 192 exit(0); 193} 194 195static void *bench_proc(void *arg) 196{ 197 signal(SIGALRM, sigalrm_handler); 198 alarm(1); 199 200 if (do_fork) 201 bench_fork(); 202 else if (do_vfork) 203 bench_vfork(); 204 else 205 bench_thread(); 206 207 return NULL; 208} 209 210static struct option options[] = { 211 { "fork", no_argument, &do_fork, 1 }, 212 { "vfork", no_argument, &do_vfork, 1 }, 213 { "exec", no_argument, &do_exec, 1 }, 214 { "timeout", required_argument, 0, 's' }, 215 { "exec-target", no_argument, &exec_target, 1 }, 216 { NULL }, 217}; 218 219static void usage(void) 220{ 221 fprintf(stderr, "Usage: fork <options> CPU\n\n"); 222 fprintf(stderr, "\t\t--fork\tUse fork() (default threads)\n"); 223 fprintf(stderr, "\t\t--vfork\tUse vfork() (default threads)\n"); 224 fprintf(stderr, "\t\t--exec\tAlso exec() (default no exec)\n"); 225 fprintf(stderr, "\t\t--timeout=X\tDuration in seconds to run (default 30)\n"); 226 fprintf(stderr, "\t\t--exec-target\tInternal option for exec workload\n"); 227} 228 229int main(int argc, char *argv[]) 230{ 231 signed char c; 232 233 while (1) { 234 int option_index = 0; 235 236 c = getopt_long(argc, argv, "", options, &option_index); 237 238 if (c == -1) 239 break; 240 241 switch (c) { 242 case 0: 243 if (options[option_index].flag != 0) 244 break; 245 246 usage(); 247 exit(1); 248 break; 249 250 case 's': 251 timeout = atoi(optarg); 252 break; 253 254 default: 255 usage(); 256 exit(1); 257 } 258 } 259 260 if (do_fork && do_vfork) { 261 usage(); 262 exit(1); 263 } 264 if (do_exec && !do_fork && !do_vfork) { 265 usage(); 266 exit(1); 267 } 268 269 if (do_exec) { 270 char *dirname = strdup(argv[0]); 271 int i; 272 i = strlen(dirname) - 1; 273 while (i) { 274 if (dirname[i] == '/') { 275 dirname[i] = '\0'; 276 if (chdir(dirname) == -1) { 277 perror("chdir"); 278 exit(1); 279 } 280 break; 281 } 282 i--; 283 } 284 } 285 286 if (exec_target) { 287 exit(0); 288 } 289 290 if (((argc - optind) != 1)) { 291 cpu = -1; 292 } else { 293 cpu = atoi(argv[optind++]); 294 } 295 296 if (do_exec) 297 exec_file = argv[0]; 298 299 set_cpu(cpu); 300 301 printf("Using "); 302 if (do_fork) 303 printf("fork"); 304 else if (do_vfork) 305 printf("vfork"); 306 else 307 printf("clone"); 308 309 if (do_exec) 310 printf(" + exec"); 311 312 printf(" on cpu %d\n", cpu); 313 314 /* Create a new process group so we can signal everyone for exit */ 315 setpgid(getpid(), getpid()); 316 317 signal(SIGUSR1, sigusr1_handler); 318 319 start_process_on(bench_proc, NULL, cpu); 320 321 while (1) 322 sleep(3600); 323 324 return 0; 325}