fork.c 5.0 KB

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