Sysenter/sysexit VS int $0x80
In un sistema Linux x86 (quindi a 32bit) le chiamate di sistema vengono fatte storicamente avvalendosi dell'istruzione INT 80h. Questo interrupt è un interrupt di tipo software e viene "programmato" in fase di inizializzazione del kernel, subito dopo il boot. L'INT 80h non fa altro che portare il processo che esegue la chiamata dallo spazio utente (il processo appunto) allo spazio kernel. Dai processori Pentium2 sono però state introdotte altre due istruzioni per effettuare in modo più ottimizzato questo salto. Queste istruzioni sono Sysenter e Sysexit che rispettivamente permettono di "entrare ed uscire" dal kernel.
Per poter sfruttare queste istruzioni occorre che il processore, il kernel e le glibc le supportino! In particolare si deva avere che:
- Il processore sia un Penium2 o superiore
- Il kernel sia un 2.6.x, ma il supporto fu introdotto dalla versione sperimentale 2.5.53
- La glibc supporti queste istruzioni e, soprattutto, sia stata compilata adeguatamente per introdurre questa feature
Benchmark: quando e perchè usare Sysenter
L'istruzione Sysenter permette di ottimizzare accelerando l'ingresso nel kernel e quindi ottimizzare tutte le chiamate di siatema. Si è notato infatti che le chiamate eseguite in INT 80h sono più lente rispetto alla SYSENTER e un Pentium4 impiega di più di un Pentium3 (e qualche volta di un Pentum2) per eseguire certe operazioni di sistema. In questi casi quindi è bene usare questa chiamata. Ciò che segue prende in riferimento una Slackware 13 con kernel 2.6.32.2 su P4 e glibc 2.9.
Test
Per eseguire un test comparativo, basta un qualsiasi sistema Linux 2.6 e non è necessario che le librerie glibc supportino di già la sysenter, come vedremo tra poco. Il sistema Linux di riferimento è, come detto, una Slackware. La slackware ottimizza le librerie per i486 senza abilitare l'istruzione SYSENTER. Quindi per poter testare la velocità occorre scrivere del codice assembly. Il benchmark proposto imita nella sostanza quello dato all'indirizzo http://kerneltrap.org/node/531, ossia vogliamo eseguire 20 milioni di chiamate getpid(). Il codice proposto è il seguente:
#include <unistd.h> //numero di cicli da fare #define K 1000000 #ifdef NO_DYNAMIC_VDSO #define sys_getpid() __asm__( \ "movl $20, %eax \n" \ "call 0xffffe414 \n" \ "movl %eax, pid \n" \ ); #else #define sys_getpid() __asm__( \ "movl $20, %eax \n" \ "call *%gs:0x10 \n" \ "movl %eax, pid \n" \ ); #endif int pid=0; unsigned long i; int main(int c, char **v) { if(c>1) // se passo un parametro qualsiasi, uso le chiamate di sistema { puts("GetPid()"); asm("xor %ebx, %ebx"); // mi assicuro di azzerare un registro che non viene usato while(i++ < K) { // eseguirò 20 chiamate per un totale di K*20 pid = getpid(); asm("mov %ebx, %gs:0x48;"); //azzero il valore del pid in memoria pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); pid = getpid(); asm("mov %ebx, %gs:0x48;"); } } else // altrimenti senza parametri uso la SYSENTER { puts("__asm__ "); while(i++ < K) { sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); sys_getpid(); } } return pid; }
Questo codice NON va compilato staticamente con l'opzione -DNO_DYNAMIC_VDSO se il VDSO è dinamico, altrimenti sys_getpid() genererà un SEGFAULT. Per compilarlo occorre valutare due condizioni:
- VDSO statico: se eseguendo il comando cat /proc/sys/vm/vdso_enabled il risulatato è 1, il vostro VDSO è dinamico. Lo si può anche vedere eseguento ldd /bin/ls due volte e vedere che la libreria (virtuale) linux-gate.so cambia il proprio indirizzo. In questo caso la compilazione si effettua semplicemente con make misura_getpid.
- VDSO dinamico: se eseguendo il comando cat /proc/sys/vm/vdso_enabled il risulatato è 2, il vostro VDSO è statico. Lo si può anche vedere eseguento ldd /bin/ls due volte e vedere che la libreria (virtuale) linux-gate.so non cambia il proprio indirizzo. In questo caso si può compilare anche con gcc -DNO_DINAMIC_VDSO misura_getpid.c -o misura_getpid.
Attenzione: se cat /proc/sys/vm/vdso_enabled fosse 0, NON potreste usare ne SYSCALL ne SYSENTER. O quantomeno il sistema dovrebbe puntare ad un indirizzo in cui verrebbe eseguito INT 80h, perdendo di fatto ogni beneficio.
Eseguiamo quindi il codice compilato misurando il tempo. Lanciamo da shell l'eseguibile:
time ./misura_getpid c ; time ./misura_getpid
Sul mio sistema, con VDSO forzato statico, il risultato è il seguente:
GetPid() real 0m9.613s user 0m4.400s sys 0m5.192s __asm__ real 0m3.381s user 0m1.228s sys 0m2.132s
Come si nota i tempi di esecuzione sono ridotti circa a 1/3 usando la SYSENTER!! Quindi non c'è motivo di non usarla.
Note sul codice
- Cosa significano "call *%gs:0x10" e "call 0xffffe414"? queste chiamate dicono al processo di saltare all'indirizzo __kernel_vsyscall. Questo indirizzo eseguirà o la SYSENTER se supportata, o l'INT 80h.
- Ma perchè proprio "call *%gs:0x10" e "call 0xffffe414"? questi indirizzi possono variare con la versione del kernel. Per esempio poco tempo fa l'indirizzo __kernel_vsyscall era a 0xfffe400. Nei link sotto riportati viene spiegato come determinare questi indirizzi.
- Perchè asm("xor %ebx, %ebx"); prima delle chiamate di sistema? perchè devo creare un registro azzerato per poter azzerare l'indirizzio di memoria "%gs:0x48". Il registro EBX non viene mai toccato e quindi ho scelto quello.
- E' proprio necessario azzerare "%gs:0x48"? Sì. Gli esempi riportati in rete NON mostrano il codice dei loro benchmark, ma dicono che usano la chiamata getpid(). Questa chiamata però è ingannevole e quanto sto per dire è stato verificato per glibc-2.9. Se provassimo a disassemblare getpid() da un binario compilato staticamente si otterrebbe una cosa come questa:
0x08051a10 <getpid+0>: mov %gs:0x4c,%edx 0x08051a17 <getpid+7>: cmp $0x0,%edx 0x08051a1a <getpid+10>: mov %edx,%eax 0x08051a1c <getpid+12>: jle 0x8051a20 <getpid+16> 0x08051a1e <getpid+14>: repz ret 0x08051a20 <getpid+16>: jne 0x8051a2c <getpid+28> 0x08051a22 <getpid+18>: mov %gs:0x48,%eax 0x08051a28 <getpid+24>: test %eax,%eax 0x08051a2a <getpid+26>: jne 0x8051a1e <getpid+14> 0x08051a2c <getpid+28>: mov $0x14,%eax 0x08051a31 <getpid+33>: int $0x80 0x08051a33 <getpid+35>: test %edx,%edx 0x08051a35 <getpid+37>: mov %eax,%ecx 0x08051a37 <getpid+39>: jne 0x8051a1e <getpid+14> 0x08051a39 <getpid+41>: mov %ecx,%gs:0x48 0x08051a40 <getpid+48>: ret
In pratica il processo chiede al kernel qual è il suo PID e lo salva subito in "%gs:0x48" (con l'istruzione "mov %ecx,%gs:0x48"). Quindi alla seconda chiamata, il sistema caricherà il PID in EAX con l'istruzione "mov %gs:0x48,%eax" (quindi non effettua nessuna call) e valuta se è diverso da 0 ("test %eax,%eax"). Se non lo è allora il valore è ritenuto il PID del processo e viene restituito senza effettuare l'INT 80h rendendo tutto estremamente veloce, ma falsando i risultati del test.
SlackWare - Abilitazione delle SYSENTER/SYSEXIT
Come detto Slackware 13 e inferiori NON usano le istruzioni SYSENTER. Ma cosa occorre fre per abilitarle? Semplice: l'uso di SYSENTER/SYSEXIT dipende in ultima analisi principalmente dalle [librerie di sistema glibc! Basta semplicemente ricompilarle, ma con le debite migliorie. Di seguito è riportato lo slackbuild utilizzato per fare ciò. Questo è lo slackbuild che si trova nei repository SlackWare 13 opportunamente modificato per i686:
#!/bin/sh # Copyright 2006, 2008, 2009 Patrick J. Volkerding, Sebeka, MN, USA # All rights reserved. ## build glibc-$VERSION for Slackware VERSION=${VERSION:-2.9} CHECKOUT=${CHECKOUT:--20090316} BUILD=${BUILD:-calzo} # $ARCH may be preset, otherwise i486 compatibility with i686 binary # structuring is the Slackware default, since this is what gcc-3.2+ # requires for binary compatibility with previous releases. ARCH=i686 CVSVER=${VERSION}${CHECKOUT} # NOTE!!! glibc needs to be built against the sanitized kernel headers, # which will be installed under /usr/include by the kernel-headers package. # Be sure the correct version of the headers package is installed BEFORE # building glibc! CWD=$(pwd) # Temporary build location. This should not be a directory # path a non-root user could create later... TMP=${TMP:-/glibc-tmp-$(mcookie)} mkdir -p $TMP # Sanity check on the version number in the install scripts: if ! grep -vq libutil-$VERSION}.so $CWD/doinst.sh-glibc ; then echo "FATAL: doinst.sh scripts have wrong version numbers." exit 1 fi case $ARCH in i686) echo echo ====================== i686 ====================== echo sleep 2; TARGET=pentium4 ;; x86_64) TARGET=${TARGET:-x86_64} ;; i486) # This should be i486 for all 32-bit x86 arch: TARGET=${TARGET:-i486} ;; esac # This function fixes a doinst.sh file for x86_64. # With thanks to Fred Emmott. fix_doinst() { if [ "x$LIBDIRSUFFIX" = "x" ]; then return; fi; # Fix "( cd usr/lib ;" occurrences sed -i "s#lib ;#lib${LIBDIRSUFFIX} ;#" install/doinst.sh # Fix "lib/" occurrences sed -i "s#lib/#lib${LIBDIRSUFFIX}/#g" install/doinst.sh # Fix "( cd lib" occurrences sed -i "s#( cd lib\$#( cd lib${LIBDIRSUFFIX}#" install/doinst.sh if [ "$ARCH" = "x86_64" ]; then sed -i 's#ld-linux.so.2#ld-linux-x86-64.so.2#' install/doinst.sh fi } # This is a patch function to put all glibc patches in the build script # up near the top. apply_patches() { # Use old-style locale directories rather than a single (and strangely # formatted) /usr/lib/locale/locale-archive file: zcat $CWD/glibc.locale.no-archive.diff.gz | patch -p1 --verbose || exit 1 # The is_IS locale is causing a strange error about the "echn" command # not existing. This patch reverts is_IS to the version shipped in # glibc-2.5: zcat $CWD/is_IS.diff.gz | patch -p1 --verbose || exit 1 # Fix NIS netgroups: zcat $CWD/glibc.nis-netgroups.diff.gz | patch -p1 --verbose || exit 1 # Support ru_RU.CP1251 locale: zcat $CWD/glibc.ru_RU.CP1251.diff.gz | patch -p1 --verbose || exit 1 # Fix missing MAX macro in getcwd.c: zcat $CWD/glibc.getcwd.max.macro.diff.gz | patch -p1 --verbose || exit 1 # Fix resolver problem with glibc-2.9: zcat $CWD/glibc-2.10-dns-no-gethostbyname4.diff.gz | patch -p0 --verbose || exit 1 # This reverts a patch that was made to glibc to fix "namespace leakage", # which seems to cause some build failures (e.g. with conntrack): zcat $CWD/glibc.revert.to.fix.build.breakages.diff.gz | patch -p1 --verbose || exit 1 # Update the timezone information. ( cd timezone tar xzf $CWD/tzdata2009d.tar.gz chown root:root * mv yearistype.sh yearistype chmod 644 * chmod 755 yearistype mkdir tzcode cd tzcode tar xzf $CWD/tzcode2009d.tar.gz chown -R root:root . chmod 644 * cp -a *.c *.h .. ) } # I'll break this out as an option for fun :-) case $ARCH in i386) OPTIMIZ="-O3 -march=i386 -mcpu=i686" LIBDIRSUFFIX="" ;; i486) OPTIMIZ="-O3 -march=i486 -mtune=i686" LIBDIRSUFFIX="" ;; i586) OPTIMIZ="-O3 -march=i586" LIBDIRSUFFIX="" ;; i686) echo ====================== ADD Flags ====================== sleep 2; OPTIMIZ="-O3 -march=i686 -mtune=i686" LIBDIRSUFFIX="" ;; athlon) OPTIMIZ="-O3 -march=athlon" LIBDIRSUFFIX="" ;; s390) OPTIMIZ="-O3" LIBDIRSUFFIX="" ;; x86_64) OPTIMIZ="-O3 -fPIC" LIBDIRSUFFIX="64" ;; *) OPTIMIZ="-O3" LIBDIRSUFFIX="" ;; esac # This is going to be the initial $DESTDIR: export PKG=$TMP/package-glibc-incoming-tree PGLIBC=$TMP/package-glibc PSOLIBS=$TMP/package-glibc-solibs PZONE=$TMP/package-glibc-zoneinfo PI18N=$TMP/package-glibc-i18n PPROFILE=$TMP/package-glibc-profile PDEBUG=$TMP/package-glibc-debug # Empty these locations first: for dir in $PKG $PGLIBC $PSOLIBS $PZONE $PI18N $PPROFILE $PDEBUG ; do if [ -d $dir ]; then rm -rf $dir fi mkdir -p $dir done if [ -d $TMP/glibc-$VERSION ]; then rm -rf $TMP/glibc-$VERSION fi # Create an incoming directory structure for glibc to be built into: mkdir -p $PKG/lib${LIBDIRSUFFIX} mkdir -p $PKG/sbin mkdir -p $PKG/usr/bin mkdir -p $PKG/usr/lib${LIBDIRSUFFIX} mkdir -p $PKG/usr/sbin mkdir -p $PKG/usr/include mkdir -p $PKG/usr/doc mkdir -p $PKG/usr/man mkdir -p $PKG/usr/share mkdir -p $PKG/var/db/nscd mkdir -p $PKG/var/run/nscd # Begin extract/compile: cd $TMP rm -rf glibc-$CVSVER tar xjf $CWD/glibc-$CVSVER.tar.bz2 cd glibc-$CVSVER chown -R root:root . find . -perm 666 -exec chmod 644 {} \; find . -perm 664 -exec chmod 644 {} \; find . -perm 600 -exec chmod 644 {} \; find . -perm 444 -exec chmod 644 {} \; find . -perm 400 -exec chmod 644 {} \; find . -perm 440 -exec chmod 644 {} \; find . -perm 777 -exec chmod 755 {} \; find . -perm 775 -exec chmod 755 {} \; find . -perm 511 -exec chmod 755 {} \; find . -perm 711 -exec chmod 755 {} \; find . -perm 555 -exec chmod 755 {} \; # Clean up leftover CVS directories: find . -type d -name CVS -exec rm -r {} \; 2> /dev/null # Apply patches; exit if any fail. apply_patches if [ ! $? = 0 ]; then exit 1 fi # Make build directory: mkdir build-glibc-$VERSION cd build-glibc-$VERSION echo "BUILDING DAS NPTL GLIBC" CFLAGS="$OPTIMIZ" \ ../configure \ --prefix=/usr \ --libdir=/usr/lib${LIBDIRSUFFIX} \ --enable-kernel=2.6.18 \ --with-headers=/usr/include \ --enable-add-ons=nptl,libidn \ --enable-profile \ --infodir=/usr/info \ --mandir=/usr/man \ --with-tls \ --with-__thread \ --without-cvs \ --without-gd \ --enable-bind-now \ --enable-shared \ $TARGET-slackware-linux make -j4 > /dev/null || exit 1 make install install_root=$PKG || exit 1 make localedata/install-locales install_root=$PKG || exit 1 # The prevailing standard seems to be putting unstripped libraries in # /usr/lib/debug/ and stripping the debugging symbols from all the other # libraries. mkdir -p $PKG/usr/lib${LIBDIRSUFFIX}/debug cp -a $PKG/lib${LIBDIRSUFFIX}/l*.so* $PKG/usr/lib${LIBDIRSUFFIX}/debug cp -a $PKG/usr/lib${LIBDIRSUFFIX}/*.a $PKG/usr/lib${LIBDIRSUFFIX}/debug # Don't need debug+profile: ( cd $PKG/usr/lib${LIBDIRSUFFIX}/debug ; rm -f *_p.* ) # NOTE: Is there really a reason for the glibc-debug package? # If you're debugging glibc, you can also compile it, right? ## COMMENTED OUT: There's no reason for profile libs to include -g information. ## Put back unstripped profiling libraries: #mv $PKG/usr/lib${LIBDIRSUFFIX}/debug/*_p.a $PKG/usr/lib${LIBDIRSUFFIX} # It might be best to put the unstripped and profiling libraries in glibc-debug and glibc-profile. # I don't think "strip -g" causes the pthread problems. It's --strip-unneeded that does. strip -g $PKG/lib${LIBDIRSUFFIX}/l*.so* strip -g $PKG/usr/lib${LIBDIRSUFFIX}/l*.so* strip -g $PKG/usr/lib${LIBDIRSUFFIX}/lib*.a # Back to the sources dir to add some files/docs: cd $TMP/glibc-$CVSVER # We'll automatically install the config file for the Name Server Cache Daemon. # Perhaps this should also have some commented-out startup code in rc.inet2... mkdir -p $PKG/etc cat nscd/nscd.conf > $PKG/etc/nscd.conf.new # Install some scripts to help select a timezone: mkdir -p $PKG/var/log/setup cp -a $CWD/timezone-scripts/setup.timeconfig $PKG/var/log/setup chown root:root $PKG/var/log/setup/setup.timeconfig chmod 755 $PKG/var/log/setup/setup.timeconfig mkdir -p $PKG/usr/sbin cp -a $CWD/timezone-scripts/timeconfig $PKG/usr/sbin chown root:root $PKG/usr/sbin/timeconfig chmod 755 $PKG/usr/sbin/timeconfig ## Install docs: ( mkdir -p $PKG/usr/doc/glibc-$VERSION cp -a \ BUGS CONFORMANCE COPYING COPYING.LIB FAQ INSTALL LICENSES NAMESPACE \ NEWS NOTES PROJECTS README README.libm \ $PKG/usr/doc/glibc-$VERSION ) # Don't forget to add the /usr/share/zoneinfo/localtime -> /etc/localtime symlink! :) if [ ! -r $PKG/usr/share/zoneinfo/localtime ]; then ( cd $PKG/usr/share/zoneinfo ; ln -sf /etc/localtime . ) fi # OK, there are some very old Linux standards that say that any binaries in a /bin or # /sbin directory (and the directories themselves) should be group bin rather than # group root, unless a specific group is really needed for some reason. # # I can't find any mention of this in more recent standards docs, and always thought # that it was pretty cosmetic anyway (hey, if there's a reason -- fill me in!), so # it's possible that this ownership change won't be followed in the near future # (it's a PITA, and causes many bug reports when the perms change is occasionally # forgotten). # # But, it's hard to get me to break old habits, so we'll continue the tradition here: # # No, no we won't. You know how we love to break traditions. # Strip most binaries: ( cd $PKG find . | xargs file | grep "executable" | grep ELF | cut -f 1 -d : | xargs strip --strip-debug 2> /dev/null find . | xargs file | grep "shared object" | grep ELF | cut -f 1 -d : | xargs strip -g 2> /dev/null ) # Fix info dir: rm $PKG/usr/info/dir gzip -9 $PKG/usr/info/* # This is junk rm $PKG/etc/ld.so.cache ( cd $PKG find . -name "*.orig" -exec rm {} \; ) ################################## # OK, time to make some packages # ################################## # glibc-zoneinfo. We will start with an easy one to avoid breaking a sweat. ;-) cd $PZONE # Install some scripts to help select a timezone: mkdir -p $PZONE/var/log/setup cp -a $CWD/timezone-scripts/setup.timeconfig $PZONE/var/log/setup chown root:root $PZONE/var/log/setup/setup.timeconfig chmod 755 $PZONE/var/log/setup/setup.timeconfig mkdir -p $PZONE/usr/sbin cp -a $CWD/timezone-scripts/timeconfig $PZONE/usr/sbin chown root:root $PZONE/usr/sbin/timeconfig chmod 755 $PZONE/usr/sbin/timeconfig mkdir $PZONE/install cat $CWD/doinst.sh-glibc-zoneinfo > $PZONE/install/doinst.sh cat $CWD/slack-desc.glibc-zoneinfo > $PZONE/install/slack-desc mkdir -p $PZONE/usr/share cd $PZONE/usr/share cp -a --verbose $PKG/usr/share/zoneinfo . cd $PZONE mkdir -p $PZONE/etc # This is already hard-coded into doinst.sh (like it'll be there anyway ;-): rm -f etc/localtime # Wrap it up: makepkg -l y -c n $TMP/glibc-zoneinfo-$VERSION-noarch-$BUILD.txz # glibc-profile: cd $PPROFILE mkdir -p usr/lib${LIBDIRSUFFIX} # Might as well just grab these with 'mv' to simplify things later: mv $PKG/usr/lib${LIBDIRSUFFIX}/lib*_p.a usr/lib${LIBDIRSUFFIX} # Profile libs should be stripped. Use the debug libs to debug... ( cd usr/lib${LIBDIRSUFFIX} ; strip -g *.a ) mkdir install cp -a $CWD/slack-desc.glibc-profile install/slack-desc makepkg -l y -c n $TMP/glibc-profile-$VERSION-$ARCH-$BUILD.txz # THIS IS NO LONGER PACKAGED (or is it? might be better to let it be made, and then ship it or not...) # glibc-debug: cd $PDEBUG mkdir -p usr/lib${LIBDIRSUFFIX} # Might as well just grab these with 'mv' to simplify things later: mv $PKG/usr/lib${LIBDIRSUFFIX}/debug usr/lib${LIBDIRSUFFIX} mkdir install cp -a $CWD/slack-desc.glibc-debug install/slack-desc makepkg -l y -c n $TMP/glibc-debug-$VERSION-$ARCH-$BUILD.txz ## INSTEAD, NUKE THESE LIBS #rm -rf $PKG/usr/lib${LIBDIRSUFFIX}/debug # glibc-i18n: cd $PI18N mkdir -p usr/lib${LIBDIRSUFFIX} rm -rf usr/lib${LIBDIRSUFFIX}/locale cp -a $PKG/usr/lib${LIBDIRSUFFIX}/locale usr/lib${LIBDIRSUFFIX} mkdir -p usr/share cp -a $PKG/usr/share/i18n usr/share cp -a $PKG/usr/share/locale usr/share mkdir install cp -a $CWD/slack-desc.glibc-i18n install/slack-desc makepkg -l y -c n $TMP/glibc-i18n-$VERSION-$ARCH-$BUILD.txz # glibc-solibs: cd $PSOLIBS mkdir -p etc/profile.d cp -a $CWD/profile.d/* etc/profile.d chown -R root:root etc chmod 755 etc/profile.d/* mkdir -p lib${LIBDIRSUFFIX} cp -a $PKG/lib${LIBDIRSUFFIX}/* lib${LIBDIRSUFFIX} ( cd lib${LIBDIRSUFFIX} mkdir incoming mv *so* incoming mv incoming/libSegFault.so . ) mkdir -p usr cp -a $PKG/usr/bin usr mv usr/bin/ldd . rm usr/bin/* mv ldd usr/bin mkdir -p usr/lib${LIBDIRSUFFIX} # The gconv directory has a lot of stuff, but including it here will save some problems. # Seems standard elsewhere. cp -a $PKG/usr/lib${LIBDIRSUFFIX}/gconv usr/lib${LIBDIRSUFFIX} # Another manpage abandoned by GNU... #mkdir -p usr/man/man1 #cp -a $PKG/usr/man/man1/ldd.1.gz usr/man/man1 mkdir -p usr/libexec cp -a $PKG/usr/libexec/pt_chown usr/libexec # Same usr.bin deal: cp -a $PKG/sbin . mv sbin/ldconfig . rm sbin/* mv ldconfig sbin mkdir install cp -a $CWD/slack-desc.glibc-solibs install/slack-desc cp -a $CWD/doinst.sh-glibc-solibs install/doinst.sh fix_doinst # Ditch links: find . -type l -exec rm {} \; # Build the package: makepkg -l y -c n $TMP/glibc-solibs-$VERSION-$ARCH-$BUILD.txz # And finally, the complete "all-in-one" glibc package is created # from whatever was leftover: cd $PGLIBC mv $PKG/* . mkdir -p etc/profile.d cp -a $CWD/profile.d/* etc/profile.d chown -R root:root etc chmod 755 etc/profile.d/* # Ditch links (these are in doinst.sh-glibc): find . -type l -exec rm {} \; mkdir install cp -a $CWD/slack-desc.glibc install/slack-desc cp -a $CWD/doinst.sh-glibc install/doinst.sh fix_doinst ( cd lib${LIBDIRSUFFIX} mkdir incoming mv *so* incoming mv incoming/libSegFault.so . ) # Build the package: makepkg -l y -c n $TMP/glibc-$VERSION-$ARCH-$BUILD.txz # Done! echo echo "glibc packages built in $TMP!"
Le cose più importanti da aggiungere sono --enable-bind-now e --enable-shared. Infati mettere solo i686 non serve ad abilitare le SYSENTER/SYSEXIT.
Domande
- Ma se in SlackWare non è abilitata la Sysenter un motivo ci sara? A mio dire NO. Si potrebbe ipotizzare che per mantenere la compatibilità con sistemi antecedenti al Pentium2 si sia optato per mantenere l'INT 80h nelle librerie glibc. Ma ciò è insensato perchè la compatibilità è garantita dal kernel. Supponiamo infatti di abilitare le sysenter in SlackWare ricompilando e installando i pacchetti .txz. Se voglio installare poi il sistema su un Pentium1 posso farlo semplicemente caricando il kernel con il parametro nosep. Così facendo, l'indirizzo __kernel_vsyscall viene spostato in modo da eseguire l'istruzione INT 80h. Per capire questo basta estrarre il VDSO da un vostro processo e disassemblarlo totalmente (con objdump -D file.dso), come spiegato in molti articoli in internet.
- Cambia qualche cosa se ottimizzo per Pentium4, piuttosto che per i686? Dai test effettuati da noi non cambia assolutamente nulla. Questo soprattuttoperchè non vengono ustate librerie matematiche nelle glibc e nel kernel e neppure le istruzioni multimediali come MMX, SSE, SSE2, ecc. I nostri test però non tengono conto di opzioni come --enable-fp (nel ./configure) che, unito agli adeguati parametri del compilatore (come -mmmx, -msse, ecc) potrebbero forse dare dei benefici sulle librerie matemetiche, le quali però non sono state testate.
- Insisto: voglio ottimizzare per Pentium4!: basta sostituire -march=i686 con -march=pentium4, ma ripeto: non cambia nulla in questa versione delle glibc.
- Esistono distribizioni che usano già questa ottimizzazione? Sì, per esempio ARCH Linux, che è anche abbastanza simile a SlackWare, ma in teoria tutte le distribuzioni ottimizzate per i686 dovrebbero implementare queste istruzioni.
- Slackware 13 a 64 bit cosa fa? Fa le cose come si deve e implementa l'istruzine SYSCALL. Lo si verifica semplicemente disassemblando un qualsiasi programma compilato staticamente. Questa istruzione, in linea teorica, permette di ridurre molte istruzioni e quindi di ottimizzare meglio il codice.
CONTROINDICAZIONI
L'unico problema rilevato fino ad ora nell'aggiornare le glibc è che il kernel di default della slackware (2.6.29.6 nel caso di Slack13) dovrebbe essere ricompilato se si volesse ricompilare il diver proprietario di NVIDIA. Il problema non sussiste se siete abituati a ricompilarvi il kernel e ne avete uno più aggiornato.
E per sistemi a 64bit?
Per sistemi x86_64, AMD (e non Intell) ha introdotto le istruzioni SYSCALL e SYSRET adottate subito dopo anche da Intell per l'estensione a 64 bit dei suoi processori (NON mi riferisco all'architettura IA64). Queste istruzioni sono gestite in modo più performante in quanto evitano la call all'indirizzo __kernel_vsyscall, ma vengono chiamate subito nel processo al posto della INT 80h. Sfortunatamente non sono stati ancora fatti (da noi) test di performance su questa istruzione.
Link Utili e documentazione
- Descrizione ed esempio della sysenter/sysezit: http://manugarg.appspot.com/systemcallinlinux2_6.html
- The Linux kernel - Andries Brouwer: http://www.win.tue.nl/~aeb/linux/lk/lk-4.html
- What is linux-gate.so.1? - Johan Petersson: http://www.trilithium.com/johan/2005/08/linux-gate/
- Understanding The Kernel Linux 3° Edizione
Info&Credits
Tutti i test di compilazione e performance sono stati effettuati alla sede del LUGMan sull'hardware messo a disposizione dall'associazione e dai soci in generale. Per qualsiasi informazione, chiarimento o curiosità contattateci!