This is the mail archive of the guile@cygnus.com mailing list for the guile project.
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |
I've just completed a fascinating exercise with guile-unexec, and I'd like to share my experiences so that other users may be able to use guile in a similar way. I have a program that loads ~4K lines of scheme code spread over ~15 modules on startup. The program often takes over a minute to load, because of the large number of NFS stat calls, etc., involved in loading this many scheme files. So I decided to take a look at guile-unexec, and see if I could integrate it into my program. After a couple of days of effort, I've gotten everything working, and the program now starts up in a flash! Although most of the problems I ran into were easy to resolve thanks to the excellent notes that come with guile-unexec, there were a couple of extra gotchas. I will try to summarize all the relevant information here. 1. The unexelf.c that comes with guile-unexec does not build on Solaris without some minor code changes. See the end of this mail for the 'diff -c' patch; I called the patched version of the file unexelf.cc. The function 'brk_addr()' is not actually needed, but I left the code in there for some reason. (Perhaps this part of Emacs should be a separately maintained library?) 2. You can use a global variable to determine whether you are starting up from a dump. When creating a dump, set the global variable to 1. 3. libguile must be linked statically for unexec to work. If you try to link libguile dynamically, the dumped executable will also load libguile.so at runtime, and you'll lose the effects of loading scheme code on startup. 4. The ports that get returned from (current-input-port) (current-output-port) and (current-error-port) need to be re-initialized after starting up from a dump. I used code like this to do the re-initialization (stolen directly from init.c): // fix up the input and output ports scm_def_inp = scm_standard_stream_to_port(stdin, (isatty (fileno (stdin)) ? "r0" : "r"), "standard input"); scm_def_outp = scm_standard_stream_to_port(stdout, "w", "standard output"); scm_def_errp = scm_standard_stream_to_port(stderr, "w", "standard error"); 5. In general, you have to be careful to not capture any references to external resources that won't survive the dump. This can imply significant changes in the startup sequence of your program. 6. The extra arguments in the call to unexec don't seem to be of any use. Perhaps these arguments are important on non-ELF platforms (?), but on ELF they must not be of any use. The signature of the function is: void unexec (char *new_name, char *old_name, unsigned data_start, // not used anywhere unsigned bss_start, // not used anywhere unsigned entry_address); // not used anywhere 7. Linking on Solaris is apparently much easier than on Linux. The guile-unexec Makefile has a line like this: $(CC) $(LDFLAGS) -o $@ -nostartfiles pre-crt0.o \ /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtbegin.o \ $(TGUILE_OBJS) unexelf.o gmalloc.o $(GUILE_LIBS) -lm -ldl \ /usr/lib/crtend.o /usr/lib/crtn.o I didn't need any of the /usr/lib/crt* business to get things working on Solaris. I don't know what these files are for specifically, and a quick run through the man pages on my Linux system was not very helpful. That's all I can think of for now. Hopefully, I'll get the itch to produce a version of guile-scsh that does the unexec dance, so that it can actually be used for writing small scripts. I guess I would need to have an implementation of unexec for more platforms than just ELF. I think that the possibilities for this technology in relation to guile are fantastic, and it's probably worth some additional effort to make unexec friendlier to use, and supported on more platforms. -russ *** unexelf.cc Tue Jan 26 10:49:54 1999 --- /home/mcmanr/compile/guile-unexec/unexelf.c Tue Jan 26 14:28:51 1999 *************** *** 425,435 **** #include <sys/mman.h> #ifndef emacs - extern "C" { - extern int exit(int); - void unexec (char *new_name, char *old_name, - unsigned data_start, unsigned bss_start, unsigned entry_address); - } #define fatal(a, b, c) fprintf (stderr, a, b, c), exit (1) #else #include <config.h> --- 425,430 ---- *************** *** 485,491 **** /* Round X up to a multiple of Y. */ int ! round_up (int x, int y) { int rem = x % y; if (rem == 0) --- 480,487 ---- /* Round X up to a multiple of Y. */ int ! round_up (x, y) ! int x, y; { int rem = x % y; if (rem == 0) *************** *** 503,540 **** * */ ! #include <sys/types.h> ! #include <sys/signal.h> ! #include <sys/fault.h> ! #include <sys/syscall.h> ! #include <sys/procfs.h> ! ! unsigned int ! brk_addr() ! { ! static struct prstatus p; ! static int here_before; ! ! if (!here_before) { ! char filename[256]; ! int fd, retval; ! here_before++; ! sprintf(filename, "/proc/%d", (long)getpid()); ! fd = open(filename, O_RDONLY); ! if (fd < 0) { ! fatal ("Can't open %s for reading: errno %d\n", filename, errno); ! } ! retval = ioctl(fd, PIOCSTATUS, (void*)&p); ! if (retval < 0) { ! fatal ("Can't ioctl %s: errno %d\n", filename, errno); ! } ! } ! return((unsigned int)p.pr_brkbase); ! } void ! unexec (char *new_name, char *old_name, ! unsigned data_start, unsigned bss_start, unsigned entry_address) { int new_file, old_file, new_file_size; void *orig_brk; --- 499,512 ---- * */ ! #if 0 ! extern void *___brk_addr; ! #endif void ! unexec (new_name, old_name, data_start, bss_start, entry_address) ! char *new_name, *old_name; ! unsigned data_start, bss_start, entry_address; { int new_file, old_file, new_file_size; void *orig_brk; *************** *** 763,771 **** --- 735,750 ---- NEW_SECTION_H (nn).sh_addralign = OLD_SECTION_H (n).sh_addralign; /* Now copy over what we have in the memory now. */ + #if 0 + orig_brk = ___brk_addr; + #endif + /* ___brk_addr = 0; */ memcpy (NEW_SECTION_H (nn).sh_offset + new_base, (caddr_t) OLD_SECTION_H (n).sh_addr, new_data2_size); + #if 0 + ___brk_addr = orig_brk; + #endif nn++; } -- "Nobody goes to that restaurant anymore: it's too crowded." --Yogi Berra