In Keil demos, I find some definitions such as "#ifndef __C51__", what's the meaning for double '_'.
They have no particular meaning whatsoever. They're part of the symbol name, i.e. the symbol really is "__C51__", not "C51". The C standard reserves all identifies starting with __ to the compiler maker, so they can have a "protected" set of names to themselves, which no user-code is allowed to trample over.
The symbols are documented in the manual They are Keil-specific, but work in exactly the same way as the standard 'C' __DATE__, __TIME__, etc