瀏覽代碼

implement forkall

this is a "nonstandard" function that was "rejected" by POSIX, but
nonetheless had its behavior documented in the POSIX rationale for
fork. it's present on solaris and possibly some other systems, and
duplicates the whole calling process, not just a single thread. glibc
does not have this function. it should not be used in programs
intending to be portable, but may be useful for testing,
checkpointing, etc. and it's an interesting (and quite small) example
of the usefulness of the __synccall framework originally written to
work around deficiencies in linux's setuid syscall.
Rich Felker 13 年之前
父節點
當前提交
4054a135fc
共有 2 個文件被更改,包括 67 次插入0 次删除
  1. 1 0
      include/unistd.h
  2. 66 0
      src/thread/forkall.c

+ 1 - 0
include/unistd.h

@@ -147,6 +147,7 @@ int getdtablesize(void);
 #ifdef _GNU_SOURCE
 int brk(void *);
 void *sbrk(intptr_t);
+pid_t forkall(void);
 pid_t vfork(void);
 int vhangup(void);
 int chroot(const char *);

+ 66 - 0
src/thread/forkall.c

@@ -0,0 +1,66 @@
+#include "pthread_impl.h"
+#include <setjmp.h>
+
+struct thread {
+	struct thread *next;
+	pthread_t td;
+	jmp_buf jb;
+	void *tmp, *stack;
+};
+
+struct ctx {
+	struct thread *list;
+	pthread_t caller;
+	pid_t pid;
+	size_t cnt;
+	pthread_barrier_t barrier;
+};
+
+static void restart_thread(pthread_t self)
+{
+	struct thread *t = self->start_arg;
+	self->start_arg = t->tmp;
+	self->pid = getpid();
+	longjmp(t->jb, 1);
+}
+
+static void do_forkall(void *p)
+{
+	struct ctx *c = p, *volatile cv = c;
+	char tmpstack[2048];
+	struct thread *tp, t = {
+		.td = __pthread_self(),
+		.next = c->list,
+		.stack = tmpstack+1024
+	};
+
+	if (t.td != c->caller) {
+		c->cnt++;
+		t.tmp = t.td->start_arg;
+		t.td->start_arg = &t;
+		if (setjmp(t.jb)) {
+			c = cv;
+			if (c->pid) return;
+			pthread_barrier_wait(&c->barrier);
+			return;
+		}
+		c->list = &t;
+		__synccall_wait();
+		return;
+	}
+	c->pid = syscall(SYS_fork);
+	if (c->pid) return;
+
+	pthread_barrier_init(&c->barrier, 0, c->cnt);
+	for (tp=c->list; tp; tp=tp->next)
+		if (__uniclone(tp->stack, restart_thread, tp->td) < 0)
+			_exit(127);
+	pthread_barrier_wait(&c->barrier);
+}
+
+pid_t forkall()
+{
+	struct ctx c = { .caller = pthread_self() };
+	__synccall(do_forkall, &c);
+	return c.pid;
+}