对话#25:Getting to the Point

本文围绕C++中指针使用展开,作者起初使用auto_ptr遇诸多限制,后考虑改用vector。Guru推荐使用Boost库的灵巧指针,介绍了scoped_ptr、scoped_array、shared_ptr、shared_array和weak_ptr的特点、使用场景及与auto_ptr的区别,还提及了使用时的注意事项。

   对话#25:Getting to the Point(WQ译)


“啊!”

       这一个小时内我第七次受挫后的大叫。 温迪明显不胜其扰了。”瞧瞧,朋友,安静一下,好吧?”她的声音飘过隔间。我觉得她的反应比以前好多了。

我以前就和auto_ptrs搏斗过了。在为Guru干活的最初几天内,我已经领教过auto_ptrs的价值了-以及它那不显眼的陷阱[1]. 我已经被auto_ptr那奇怪的所有权问题扎过一次了。auto_ptr使用伪装为拷贝操作的移交操作。想像一下这种情况: 你在操作一个复印机,放上你的表格,然后按 “拷贝”按钮。过了一会儿,复印机给你副本-然后将原件送入碎纸器。这就是auto_ptr的拷贝和赋值操作干的好事。

       无论如何,我现在已经更聪明和更富有经验了,可仍然正陷入在auto_ptr的形形色色的限制之中。比如说,auto_ptr不能用来包容指向数组的指针。我玩弄了一个Hacking的技巧来绕开它,通过使用显式的数组delete

auto_ptr<T> autoArray(new T[10]);

//...

delete [] autoArray.release;()

       但立刻就后悔了。我可以预见到这个 “面目可憎的东西”(Guru会也会这么称呼它) 的许多危险。最重要的是,嗯,auto_ptr的卖点就是拥有和销毁内存;手工这么做完全打破了使用它的本意。

因此,我决定改用一个vector来拥有数组。它比我所需要的还更强劲一些,因为我不需要动态更改vector的大小,而它提供了此功能。 我实际上想要是一个能自动处理指针和内存管理的灵巧指针,并且可以用来处理数组。

“使用Boost, 天行者,”我听到身后Guru的声音。

“是不是使用大场,天行者?” 我侧肩答道。 “而且,不要叫我天行者。”

Boost是能量的源泉,”Guru继续于她的电影台词之中。

我今天没心情,于是我打断了她。”嗯,我们能今天不用演戏吗?鲍伯在伦敦的办公室里,也没有新员神情反常。”

她来到附近,我看见了她的微笑和耸肩。”哦,我仅仅感到无聊,”她叹了口气。”你看过Boost库中的灵巧指针了吗?”

“嗯,”我吞吞吐吐的,”我还没空仔细去看Boost库。它怎么实现灵巧指针的?”

“共有五种灵巧指针,”Guru一边坐下一边说着。”两个处理指针的单所有权,两个处理共享所有权。”

“共享所有权?哦,类似于带引用计数的类?”

“完全正确。灵巧指针是成对的,一个处理指向单一物体的指针,另一个处理指向数组的指针。”在说的时候,她在白板上写下:scoped_ptrscoped_arrayshared_ptr shared_array

“只有四个,你前面说有五个?”我问。

“剩下的一个叫weak_ptr。它是shared_ptr的非拥有关系的观察者。我随后讲它。 scoped_*形灵巧指针在它们离开生存范围时,自动析构所指向的对象。一个提议用途是实现Pimpl惯用法-指向实现的指针,”在我发问前,她急忙加了一句。

“如此说来...他们象auto_ptr,对吧?”

“不十分象。scoped_ptrscoped_array不可拷贝。”

“不可拷贝?那么,如果我将一个指针作为类的成员-”我边说边在白板上潦草地写着:

class T

{

scoped_ptr<TImpl> p;

...

“-我如何实现拷贝和赋值?”

“和auto_ptr一样,”Guru边答边写:

T( const T& other )

: p(new TImpl(*other.p)

{

}

T& operator=( const T& other )

{

scoped_ptr<TImpl> tmp(new TImpl(*other.p));

p.swap;(tmp)

}

Ooookay,”我懒洋洋地说着,装作我已经懂了。“ 于是,当我使用scoped_ptr的时候, 我必须同样完成使用auto_ptr时所必须做的所有工作,对吧?那么为什么不就使用auto_ptr?”

“因为使用auto_ptr时能编译,但是做错事, 因为自动生成的函数。 使用 scoped_ptr 使得极难忽略拷贝语义,因为如果你使用编辑器自动生成的版本时,编辑器将会拒绝编译这个类。同样,scoped_ptr不能够在一个不完全的类型上被使用。”我给予了Guru我擅长的车前灯前的鹿的目光。 她叹了口气。“考虑这种情况,”她边说边在白板上写:

class Simple;

Simple * CreateSimple();

void f()

{

auto_ptr<Simple>autoS(CreateSimple());

scoped_ptr<Simple>scopedS(CreateSimple());

//...

}

       “在函数体中,Simple是一个不完全的类型。如果它的析构函数是有行为的,那么通过auto_ptrscoped_ptr销毁它,其结果为未定义。一些编译器在你实例化auto_ptr时将会警告你这一点,但不是所有编译器都会这么做。相对的,scoped_ptr为确保语义正确而做了些小动作以造成编译错误,如果是在实例化一个不完全类型的话。”

“哦,是的,当然,” 我来劲了,以显示我理解了。 “因此我们应该始终使用scoped_ptr来代替auto_ptr,对吧?”

“错,抱歉,” 她失望地说道。“auto_ptr仍然有它的用处。不像auto_ptrscoped_ptr 没有release()成员-它不能够放弃指针的所有权。 因为这个,以及因为scoped_ptr不能够被复制,当scoped_ptr离开生存范围时,它所管理的指针总是被delete。这使得scoped_ptr不适用于需要传递所有权的地方,比如厂。”

“使用scoped_ptr向其他程序员表明你的意图。它告诉其他人,这个指针不应该在当前范围外被复制。正相反,auto_ptr允许从产生指针的代码空间向外传递所有权,但是它维持对指针的控制除非所有权的传递是完全地。 这当然对写异常安全的代码有重要意义。”

“啊,好,我明白了,”我嘟囔着。“ 你提到的另一种灵巧指针是怎么回事?”

scoped_array的行为和scoped_ptr相同,除了它处理是对象数组而不是单个对象。 scoped_array 提供了稍有差别的访问函数。它没有operator*operator->,但它有operator[]。我相信,”她总结道,“你需要是一个 scoped_array。”

“也许吧,”我答道,”但是我认为在作决定之前我应该多了解些shared_*形的灵巧指针。”

“非常明智,我的徒……弟,抱歉,叫习惯了。”Guru歉意地笑了一下。“是的, 熟悉各个选择之后再作决定是个好习惯。”

她指着白板上的shared_ptr shared_array说道:“shared_*形的灵巧指针是非常有用的工具。 他们是引用计数型的指针,能够区分出所有者和观察者。举例来说, 使用上面的class T:”

void sharing()

{

shared_ptr<T> firstShared(new T);

shared_ptr<T> secondShared;(firstShared)

//...

}

“两个shared_ptr对象指向相同的T对象。引用计数现在是2。既然shared_ptr是引用计数的,对象一直不被销毁直到最后一个shared_ptr离开生存范围-此时引用计数降为0shared_*的灵巧指针有相当的柔性,你可以使用它们包容指向不完全类型的指针。”

“我想你说过的,这很糟吧?”

“是很糟,如果你不小心的话。 shared_*的灵巧指针可以用一个指向不完全类型的指针来实例化,但必须要指明一个函数或函数子供销毁被拥有对象时调用。比如说,我们修改了上面的f函数:”

void DestroySimple( Simple* );

void f()

{

shared_ptr<Simple>sharedSFails( CreateSimple() );

shared_ptr<Simple>sharedSSucceeds( CreateSimple(), DestroySimple );

}

“第一个shared_ptr失败了, 因为Simple是一个不完全类型。第二个成功了,因为你明确地告诉了 shared_ptr 该如何销毁这个指针。

“因为 shared_ 指针被设计可被拷贝,它们完全适用于标准容器, 包括associative 容器。并且,在那些必须强制类型转换的特别场合上,可以定义特别的类型转换操作以生成新的shared_*形的灵巧指针。 例如,如果我们有一个类Base以及公有继承而来的Derived,和一个无关类,那么你将遇到:”

void g()

{

shared_ptr<Base>sharedBase( new Derived );

shared_ptr<Derived> sharedDer =

shared_dynamic_cast<Derived>( sharedBase);

shared_ptr<Unrelated> sharedUnrelated =

shared_dynamic_cast<Unrelated>( sharedBase);

try

{

shared_ptr<Unrelated> oops =

shared_polymorphic_cast<Unrelated>( sharedBase);

       }

      catch( bad_cast)

      {

           //...

      }

}

       “同样还存在着一个shared_static_cast,供那些特别场合使用,”Guru总结道。

我对这个函数研究了一会儿。“好吧,让我试试是否能推算出将发生什么。第一个shared_dynamic_cast在灵巧指针上执行了一个dynamic_cast,返回一个新的灵巧指针。 假如dynamic_cast失败了怎么办-在引用计数上将发生什么?”

Guru满意地点点头。“在那种情况下,原始的计数不被影响,而且新的hared_ptr包容的是一个NULL指针。正如你从try/catch语句推测的,shared_polymorphic_cast将试图在被容纳的指针上执行dynamic_cast 如果转换失败,它将抛出bad_cast异常。”

“哇,”我赞叹道,“这个类可真完备。看起来很不错。”

“的确,”Guru同意。 “但还有些事情是必须要知道的。引用计算过于简单而不能检测任何形式的循环引用。同样,shared_ptr也没有实现任何形式的写时拷贝(copy-on-write),因此用它来实现Pimpl惯用法时必须仔细衡量。”

我深思熟虑后确定我喜欢它。“嗨,”我追问道,我还记着呢,“你提到过的weak_ptr是怎么回事?”

“啊,是的。weak_ptrshared_ptrs联合使用。weak_ptr是观察者,不影响共享对象的引用计数。weak_ptr的主要目的是允许shared_ptr参与循环依赖-A引用BB反过来又引用A。我喜欢将它想象成俱乐部中的准会员-它没有投票权,但是能参加俱乐部的会议。”

Hmmm……” 我仔细想了一下。“因为 weak_ptr不影响引用计数,如果俱乐部解散了怎么办-也就是说,最後一个shared_ptr离开了生存范围-而此时 weak_ptr正在使用共享对象?”

“在那情况下,weak_ptr维护的指针被设置为NULL。而对于NULL,不能进行任何dereferencing操作,例如operator*operator->、或operator[],在dereferencing指针前应该进行NULL检查。于是,正如dereferencing内建指针前必须进行NULL检查,你也一定要检查灵巧指针是否为NULL。这个限制适用于Boost库中的所有灵巧指针。”

“天那,要记太多的东西了,”我一边说一边盯着潦草地写在白板上的东西。

“别担心,”Guru微笑着站起离开了。“我会email给你Boost库的URL,以及练习和示范各种不同指针的一个小程序。”她当然说到做到,几分钟内我收到了她的email 我读到这句时立即就闭上了眼睛:

       “我的徒弟,如前所说,这是Boost库中的说明文档: <www.boost.org/libs/smart_ptr/smart_ptr.htm>[2] 阅读并思考附上问题[3]。”

[感谢]

       Thanks to Peter Dimov and Bjorn Karlsson for providing valuable comments and updates.

[注释]

[1] Jim Hyslop and Herb Sutter. “Conversations #1,” C++ Report, April 2000.

[2] The most recent version of the library and documentation can also be obtained via anonymous CVS checkout from the Source Forge project. Detailed instructions can be found at: <http://sourceforge.net/projects/boost/>.

[3] The sample code can be downloaded from the CUJ website: hyslop.zip.

# Configuration file for notebook. c = get_config() #noqa #------------------------------------------------------------------------------ # Application(SingletonConfigurable) configuration #------------------------------------------------------------------------------ ## This is an application. ## The date format used by logging formatters for %(asctime)s # Default: &#39;%Y-%m-%d %H:%M:%S&#39; # c.Application.log_datefmt = &#39;%Y-%m-%d %H:%M:%S&#39; ## The Logging format template # Default: &#39;[%(name)s]%(highlevel)s %(message)s&#39; # c.Application.log_format = &#39;[%(name)s]%(highlevel)s %(message)s&#39; ## Set the log level by value or name. # Choices: any of [0, 10, 20, 30, 40, 50, &#39;DEBUG&#39;, &#39;INFO&#39;, &#39;WARN&#39;, &#39;ERROR&#39;, &#39;CRITICAL&#39;] # Default: 30 # c.Application.log_level = 30 ## Configure additional log handlers. # # The default stderr logs handler is configured by the log_level, log_datefmt # and log_format settings. # # This configuration can be used to configure additional handlers (e.g. to # output the log to a file) or for finer control over the default handlers. # # If provided this should be a logging configuration dictionary, for more # information see: # https://docs.python.org/3/library/logging.config.html#logging-config- # dictschema # # This dictionary is merged with the base logging configuration which defines # the following: # # * A logging formatter intended for interactive use called # ``console``. # * A logging handler that writes to stderr called # ``console`` which uses the formatter ``console``. # * A logger with the name of this application set to ``DEBUG`` # level. # # This example adds a new handler that writes to a file: # # .. code-block:: python # # c.Application.logging_config = { # &#39;handlers&#39;: { # &#39;file&#39;: { # &#39;class&#39;: &#39;logging.FileHandler&#39;, # &#39;level&#39;: &#39;DEBUG&#39;, # &#39;filename&#39;: &#39;<path/to/file>&#39;, # } # }, # &#39;loggers&#39;: { # &#39;<application-name>&#39;: { # &#39;level&#39;: &#39;DEBUG&#39;, # # NOTE: if you don&#39;t list the default "console" # # handler here then it will be disabled # &#39;handlers&#39;: [&#39;console&#39;, &#39;file&#39;], # }, # } # } # Default: {} # c.Application.logging_config = {} ## Instead of starting the Application, dump configuration to stdout # Default: False # c.Application.show_config = False ## Instead of starting the Application, dump configuration to stdout (as JSON) # Default: False # c.Application.show_config_json = False #------------------------------------------------------------------------------ # JupyterApp(Application) configuration #------------------------------------------------------------------------------ ## Base class for Jupyter applications ## Answer yes to any prompts. # Default: False # c.JupyterApp.answer_yes = False ## Full path of a config file. # Default: &#39;&#39; # c.JupyterApp.config_file = &#39;&#39; ## Specify a config file to load. # Default: &#39;&#39; # c.JupyterApp.config_file_name = &#39;&#39; ## Generate default config file. # Default: False # c.JupyterApp.generate_config = False ## The date format used by logging formatters for %(asctime)s # See also: Application.log_datefmt # c.JupyterApp.log_datefmt = &#39;%Y-%m-%d %H:%M:%S&#39; ## The Logging format template # See also: Application.log_format # c.JupyterApp.log_format = &#39;[%(name)s]%(highlevel)s %(message)s&#39; ## Set the log level by value or name. # See also: Application.log_level # c.JupyterApp.log_level = 30 ## # See also: Application.logging_config # c.JupyterApp.logging_config = {} ## Instead of starting the Application, dump configuration to stdout # See also: Application.show_config # c.JupyterApp.show_config = False ## Instead of starting the Application, dump configuration to stdout (as JSON) # See also: Application.show_config_json # c.JupyterApp.show_config_json = False #------------------------------------------------------------------------------ # ExtensionApp(JupyterApp) configuration #------------------------------------------------------------------------------ ## Base class for configurable Jupyter Server Extension Applications. # # ExtensionApp subclasses can be initialized two ways: # # - Extension is listed as a jpserver_extension, and ServerApp calls # its load_jupyter_server_extension classmethod. This is the # classic way of loading a server extension. # # - Extension is launched directly by calling its `launch_instance` # class method. This method can be set as a entry_point in # the extensions setup.py. ## Answer yes to any prompts. # See also: JupyterApp.answer_yes # c.ExtensionApp.answer_yes = False ## Full path of a config file. # See also: JupyterApp.config_file # c.ExtensionApp.config_file = &#39;&#39; ## Specify a config file to load. # See also: JupyterApp.config_file_name # c.ExtensionApp.config_file_name = &#39;&#39; # Default: &#39;&#39; # c.ExtensionApp.default_url = &#39;&#39; ## Generate default config file. # See also: JupyterApp.generate_config # c.ExtensionApp.generate_config = False ## Handlers appended to the server. # Default: [] # c.ExtensionApp.handlers = [] ## The date format used by logging formatters for %(asctime)s # See also: Application.log_datefmt # c.ExtensionApp.log_datefmt = &#39;%Y-%m-%d %H:%M:%S&#39; ## The Logging format template # See also: Application.log_format # c.ExtensionApp.log_format = &#39;[%(name)s]%(highlevel)s %(message)s&#39; ## Set the log level by value or name. # See also: Application.log_level # c.ExtensionApp.log_level = 30 ## # See also: Application.logging_config # c.ExtensionApp.logging_config = {} ## Whether to open in a browser after starting. # The specific browser used is platform dependent and # determined by the python standard library `webbrowser` # module, unless it is overridden using the --browser # (ServerApp.browser) configuration option. # Default: False # c.ExtensionApp.open_browser = False ## Settings that will passed to the server. # Default: {} # c.ExtensionApp.settings = {} ## Instead of starting the Application, dump configuration to stdout # See also: Application.show_config # c.ExtensionApp.show_config = False ## Instead of starting the Application, dump configuration to stdout (as JSON) # See also: Application.show_config_json # c.ExtensionApp.show_config_json = False ## paths to search for serving static files. # # This allows adding javascript/css to be available from the notebook server machine, # or overriding individual files in the IPython # Default: [] # c.ExtensionApp.static_paths = [] ## Url where the static assets for the extension are served. # Default: &#39;&#39; # c.ExtensionApp.static_url_prefix = &#39;&#39; ## Paths to search for serving jinja templates. # # Can be used to override templates from notebook.templates. # Default: [] # c.ExtensionApp.template_paths = [] #------------------------------------------------------------------------------ # LabServerApp(ExtensionApp) configuration #------------------------------------------------------------------------------ ## A Lab Server Application that runs out-of-the-box ## "A list of comma-separated URIs to get the allowed extensions list # # .. versionchanged:: 2.0.0 # `LabServerApp.whitetlist_uris` renamed to `allowed_extensions_uris` # Default: &#39;&#39; # c.LabServerApp.allowed_extensions_uris = &#39;&#39; ## Answer yes to any prompts. # See also: JupyterApp.answer_yes # c.LabServerApp.answer_yes = False ## The application settings directory. # Default: &#39;&#39; # c.LabServerApp.app_settings_dir = &#39;&#39; ## The url path for the application. # Default: &#39;/lab&#39; # c.LabServerApp.app_url = &#39;/lab&#39; ## Deprecated, use `LabServerApp.blocked_extensions_uris` # Default: &#39;&#39; # c.LabServerApp.blacklist_uris = &#39;&#39; ## A list of comma-separated URIs to get the blocked extensions list # # .. versionchanged:: 2.0.0 # `LabServerApp.blacklist_uris` renamed to `blocked_extensions_uris` # Default: &#39;&#39; # c.LabServerApp.blocked_extensions_uris = &#39;&#39; ## Whether to cache files on the server. This should be `True` except in dev # mode. # Default: True # c.LabServerApp.cache_files = True ## Full path of a config file. # See also: JupyterApp.config_file # c.LabServerApp.config_file = &#39;&#39; ## Specify a config file to load. # See also: JupyterApp.config_file_name # c.LabServerApp.config_file_name = &#39;&#39; ## Whether getting a relative (False) or absolute (True) path when copying a # path. # Default: False # c.LabServerApp.copy_absolute_path = False ## Extra paths to look for federated JupyterLab extensions # Default: [] # c.LabServerApp.extra_labextensions_path = [] ## Generate default config file. # See also: JupyterApp.generate_config # c.LabServerApp.generate_config = False ## Handlers appended to the server. # See also: ExtensionApp.handlers # c.LabServerApp.handlers = [] ## Options to pass to the jinja2 environment for this # Default: {} # c.LabServerApp.jinja2_options = {} ## The standard paths to look in for federated JupyterLab extensions # Default: [] # c.LabServerApp.labextensions_path = [] ## The url for federated JupyterLab extensions # Default: &#39;&#39; # c.LabServerApp.labextensions_url = &#39;&#39; ## The interval delay in seconds to refresh the lists # Default: 3600 # c.LabServerApp.listings_refresh_seconds = 3600 ## The optional kwargs to use for the listings HTTP requests as # described on https://2.python-requests.org/en/v2.7.0/api/#requests.request # Default: {} # c.LabServerApp.listings_request_options = {} ## The listings url. # Default: &#39;&#39; # c.LabServerApp.listings_url = &#39;&#39; ## The date format used by logging formatters for %(asctime)s # See also: Application.log_datefmt # c.LabServerApp.log_datefmt = &#39;%Y-%m-%d %H:%M:%S&#39; ## The Logging format template # See also: Application.log_format # c.LabServerApp.log_format = &#39;[%(name)s]%(highlevel)s %(message)s&#39; ## Set the log level by value or name. # See also: Application.log_level # c.LabServerApp.log_level = 30 ## # See also: Application.logging_config # c.LabServerApp.logging_config = {} ## Whether a notebook should start a kernel automatically. # Default: True # c.LabServerApp.notebook_starts_kernel = True ## Whether to open in a browser after starting. # See also: ExtensionApp.open_browser # c.LabServerApp.open_browser = False ## The optional location of the settings schemas directory. If given, a handler # will be added for settings. # Default: &#39;&#39; # c.LabServerApp.schemas_dir = &#39;&#39; ## Settings that will passed to the server. # See also: ExtensionApp.settings # c.LabServerApp.settings = {} ## The url path of the settings handler. # Default: &#39;&#39; # c.LabServerApp.settings_url = &#39;&#39; ## Instead of starting the Application, dump configuration to stdout # See also: Application.show_config # c.LabServerApp.show_config = False ## Instead of starting the Application, dump configuration to stdout (as JSON) # See also: Application.show_config_json # c.LabServerApp.show_config_json = False ## The optional location of local static files. If given, a static file handler # will be added. # Default: &#39;&#39; # c.LabServerApp.static_dir = &#39;&#39; ## paths to search for serving static files. # See also: ExtensionApp.static_paths # c.LabServerApp.static_paths = [] ## Url where the static assets for the extension are served. # See also: ExtensionApp.static_url_prefix # c.LabServerApp.static_url_prefix = &#39;&#39; ## Paths to search for serving jinja templates. # See also: ExtensionApp.template_paths # c.LabServerApp.template_paths = [] ## The application templates directory. # Default: &#39;&#39; # c.LabServerApp.templates_dir = &#39;&#39; ## The optional location of the themes directory. If given, a handler will be # added for themes. # Default: &#39;&#39; # c.LabServerApp.themes_dir = &#39;&#39; ## The theme url. # Default: &#39;&#39; # c.LabServerApp.themes_url = &#39;&#39; ## The url path of the translations handler. # Default: &#39;&#39; # c.LabServerApp.translations_api_url = &#39;&#39; ## The url path of the tree handler. # Default: &#39;&#39; # c.LabServerApp.tree_url = &#39;&#39; ## The optional location of the user settings directory. # Default: &#39;&#39; # c.LabServerApp.user_settings_dir = &#39;&#39; ## Deprecated, use `LabServerApp.allowed_extensions_uris` # Default: &#39;&#39; # c.LabServerApp.whitelist_uris = &#39;&#39; ## The url path of the workspaces API. # Default: &#39;&#39; # c.LabServerApp.workspaces_api_url = &#39;&#39; ## The optional location of the saved workspaces directory. If given, a handler # will be added for workspaces. # Default: &#39;&#39; # c.LabServerApp.workspaces_dir = &#39;&#39; #------------------------------------------------------------------------------ # JupyterNotebookApp(LabServerApp) configuration #------------------------------------------------------------------------------ ## The notebook server extension app. ## # See also: LabServerApp.allowed_extensions_uris # c.JupyterNotebookApp.allowed_extensions_uris = &#39;&#39; ## Answer yes to any prompts. # See also: JupyterApp.answer_yes # c.JupyterNotebookApp.answer_yes = False ## The application settings directory. # Default: &#39;&#39; # c.JupyterNotebookApp.app_settings_dir = &#39;&#39; ## The url path for the application. # Default: &#39;/lab&#39; # c.JupyterNotebookApp.app_url = &#39;/lab&#39; ## Deprecated, use `LabServerApp.blocked_extensions_uris` # See also: LabServerApp.blacklist_uris # c.JupyterNotebookApp.blacklist_uris = &#39;&#39; ## # See also: LabServerApp.blocked_extensions_uris # c.JupyterNotebookApp.blocked_extensions_uris = &#39;&#39; ## Whether to cache files on the server. This should be `True` except in dev # mode. # Default: True # c.JupyterNotebookApp.cache_files = True ## Full path of a config file. # See also: JupyterApp.config_file # c.JupyterNotebookApp.config_file = &#39;&#39; ## Specify a config file to load. # See also: JupyterApp.config_file_name # c.JupyterNotebookApp.config_file_name = &#39;&#39; ## Whether getting a relative (False) or absolute (True) path when copying a # path. # Default: False # c.JupyterNotebookApp.copy_absolute_path = False ## Whether custom CSS is loaded on the page. # Defaults to True and custom CSS is loaded. # Default: True # c.JupyterNotebookApp.custom_css = True ## The default URL to redirect to from `/` # Default: &#39;/tree&#39; # c.JupyterNotebookApp.default_url = &#39;/tree&#39; ## Whether to expose the global app instance to browser via window.jupyterapp # Default: False # c.JupyterNotebookApp.expose_app_in_browser = False ## Extra paths to look for federated JupyterLab extensions # Default: [] # c.JupyterNotebookApp.extra_labextensions_path = [] ## Generate default config file. # See also: JupyterApp.generate_config # c.JupyterNotebookApp.generate_config = False ## Handlers appended to the server. # See also: ExtensionApp.handlers # c.JupyterNotebookApp.handlers = [] ## Options to pass to the jinja2 environment for this # Default: {} # c.JupyterNotebookApp.jinja2_options = {} ## The standard paths to look in for federated JupyterLab extensions # Default: [] # c.JupyterNotebookApp.labextensions_path = [] ## The url for federated JupyterLab extensions # Default: &#39;&#39; # c.JupyterNotebookApp.labextensions_url = &#39;&#39; ## The interval delay in seconds to refresh the lists # See also: LabServerApp.listings_refresh_seconds # c.JupyterNotebookApp.listings_refresh_seconds = 3600 ## The optional kwargs to use for the listings HTTP requests as # described on https://2.python-requests.org/en/v2.7.0/api/#requests.request # See also: LabServerApp.listings_request_options # c.JupyterNotebookApp.listings_request_options = {} ## The listings url. # Default: &#39;&#39; # c.JupyterNotebookApp.listings_url = &#39;&#39; ## The date format used by logging formatters for %(asctime)s # See also: Application.log_datefmt # c.JupyterNotebookApp.log_datefmt = &#39;%Y-%m-%d %H:%M:%S&#39; ## The Logging format template # See also: Application.log_format # c.JupyterNotebookApp.log_format = &#39;[%(name)s]%(highlevel)s %(message)s&#39; ## Set the log level by value or name. # See also: Application.log_level # c.JupyterNotebookApp.log_level = 30 ## # See also: Application.logging_config # c.JupyterNotebookApp.logging_config = {} ## Whether a notebook should start a kernel automatically. # Default: True # c.JupyterNotebookApp.notebook_starts_kernel = True ## Whether to open in a browser after starting. # See also: ExtensionApp.open_browser # c.JupyterNotebookApp.open_browser = False ## The optional location of the settings schemas directory. If given, a handler # will be added for settings. # Default: &#39;&#39; # c.JupyterNotebookApp.schemas_dir = &#39;&#39; ## Settings that will passed to the server. # See also: ExtensionApp.settings # c.JupyterNotebookApp.settings = {} ## The url path of the settings handler. # Default: &#39;&#39; # c.JupyterNotebookApp.settings_url = &#39;&#39; ## Instead of starting the Application, dump configuration to stdout # See also: Application.show_config # c.JupyterNotebookApp.show_config = False ## Instead of starting the Application, dump configuration to stdout (as JSON) # See also: Application.show_config_json # c.JupyterNotebookApp.show_config_json = False ## The optional location of local static files. If given, a static file handler # will be added. # Default: &#39;&#39; # c.JupyterNotebookApp.static_dir = &#39;&#39; ## paths to search for serving static files. # See also: ExtensionApp.static_paths # c.JupyterNotebookApp.static_paths = [] ## Url where the static assets for the extension are served. # See also: ExtensionApp.static_url_prefix # c.JupyterNotebookApp.static_url_prefix = &#39;&#39; ## Paths to search for serving jinja templates. # See also: ExtensionApp.template_paths # c.JupyterNotebookApp.template_paths = [] ## The application templates directory. # Default: &#39;&#39; # c.JupyterNotebookApp.templates_dir = &#39;&#39; ## The optional location of the themes directory. If given, a handler will be # added for themes. # Default: &#39;&#39; # c.JupyterNotebookApp.themes_dir = &#39;&#39; ## The theme url. # Default: &#39;&#39; # c.JupyterNotebookApp.themes_url = &#39;&#39; ## The url path of the translations handler. # Default: &#39;&#39; # c.JupyterNotebookApp.translations_api_url = &#39;&#39; ## The url path of the tree handler. # Default: &#39;&#39; # c.JupyterNotebookApp.tree_url = &#39;&#39; ## The optional location of the user settings directory. # Default: &#39;&#39; # c.JupyterNotebookApp.user_settings_dir = &#39;&#39; ## Deprecated, use `LabServerApp.allowed_extensions_uris` # See also: LabServerApp.whitelist_uris # c.JupyterNotebookApp.whitelist_uris = &#39;&#39; ## The url path of the workspaces API. # Default: &#39;&#39; # c.JupyterNotebookApp.workspaces_api_url = &#39;&#39; ## The optional location of the saved workspaces directory. If given, a handler # will be added for workspaces. # Default: &#39;&#39; # c.JupyterNotebookApp.workspaces_dir = &#39;&#39; #------------------------------------------------------------------------------ # ServerApp(JupyterApp) configuration #------------------------------------------------------------------------------ ## The Jupyter Server application class. ## Set the Access-Control-Allow-Credentials: true header # Default: False # c.ServerApp.allow_credentials = False ## Set the Access-Control-Allow-Origin header # # Use &#39;*&#39; to allow any origin to access your server. # # Takes precedence over allow_origin_pat. # Default: &#39;&#39; # c.ServerApp.allow_origin = &#39;&#39; ## Use a regular expression for the Access-Control-Allow-Origin header # # Requests from an origin matching the expression will get replies with: # # Access-Control-Allow-Origin: origin # # where `origin` is the origin of the request. # # Ignored if allow_origin is set. # Default: &#39;&#39; # c.ServerApp.allow_origin_pat = &#39;&#39; ## DEPRECATED in 2.0. Use PasswordIdentityProvider.allow_password_change # Default: True # c.ServerApp.allow_password_change = True ## Allow requests where the Host header doesn&#39;t point to a local server # # By default, requests get a 403 forbidden response if the &#39;Host&#39; header # shows that the browser thinks it&#39;s on a non-local domain. # Setting this option to True disables this check. # # This protects against &#39;DNS rebinding&#39; attacks, where a remote web server # serves you a page and then changes its DNS to send later requests to a # local IP, bypassing same-origin checks. # # Local IP addresses (such as 127.0.0.1 and ::1) are allowed as local, # along with hostnames configured in local_hostnames. # Default: False # c.ServerApp.allow_remote_access = False ## Whether to allow the user to run the server as root. # Default: False # c.ServerApp.allow_root = False ## Answer yes to any prompts. # See also: JupyterApp.answer_yes # c.ServerApp.answer_yes = False ## " # Require authentication to access prometheus metrics. # Default: True # c.ServerApp.authenticate_prometheus = True ## The authorizer class to use. # Default: &#39;jupyter_server.auth.authorizer.AllowAllAuthorizer&#39; # c.ServerApp.authorizer_class = &#39;jupyter_server.auth.authorizer.AllowAllAuthorizer&#39; ## Reload the webapp when changes are made to any Python src files. # Default: False # c.ServerApp.autoreload = False ## The base URL for the Jupyter server. # # Leading and trailing slashes can be omitted, # and will automatically be added. # Default: &#39;/&#39; # c.ServerApp.base_url = &#39;/&#39; ## Specify what command to use to invoke a web # browser when starting the server. If not specified, the # default browser will be determined by the `webbrowser` # standard library module, which allows setting of the # BROWSER environment variable to override it. # Default: &#39;&#39; # c.ServerApp.browser = &#39;&#39; ## The full path to an SSL/TLS certificate file. # Default: &#39;&#39; # c.ServerApp.certfile = &#39;&#39; ## The full path to a certificate authority certificate for SSL/TLS client # authentication. # Default: &#39;&#39; # c.ServerApp.client_ca = &#39;&#39; ## Full path of a config file. # See also: JupyterApp.config_file # c.ServerApp.config_file = &#39;&#39; ## Specify a config file to load. # See also: JupyterApp.config_file_name # c.ServerApp.config_file_name = &#39;&#39; ## The config manager class to use # Default: &#39;jupyter_server.services.config.manager.ConfigManager&#39; # c.ServerApp.config_manager_class = &#39;jupyter_server.services.config.manager.ConfigManager&#39; ## The content manager class to use. # Default: &#39;jupyter_server.services.contents.largefilemanager.AsyncLargeFileManager&#39; # c.ServerApp.contents_manager_class = &#39;jupyter_server.services.contents.largefilemanager.AsyncLargeFileManager&#39; ## DEPRECATED. Use IdentityProvider.cookie_options # Default: {} # c.ServerApp.cookie_options = {} ## The random bytes used to secure cookies. # By default this is a new random number every time you start the server. # Set it to a value in a config file to enable logins to persist across server sessions. # # Note: Cookie secrets should be kept private, do not share config files with # cookie_secret stored in plaintext (you can read the value from a file). # Default: b&#39;&#39; # c.ServerApp.cookie_secret = b&#39;&#39; ## The file where the cookie secret is stored. # Default: &#39;&#39; # c.ServerApp.cookie_secret_file = &#39;&#39; ## Override URL shown to users. # # Replace actual URL, including protocol, address, port and base URL, # with the given value when displaying URL to the users. Do not change # the actual connection URL. If authentication token is enabled, the # token is added to the custom URL automatically. # # This option is intended to be used when the URL to display to the user # cannot be determined reliably by the Jupyter server (proxified # or containerized setups for example). # Default: &#39;&#39; # c.ServerApp.custom_display_url = &#39;&#39; ## The default URL to redirect to from `/` # Default: &#39;/&#39; # c.ServerApp.default_url = &#39;/&#39; ## Disable cross-site-request-forgery protection # # Jupyter server includes protection from cross-site request forgeries, # requiring API requests to either: # # - originate from pages served by this server (validated with XSRF cookie and token), or # - authenticate with a token # # Some anonymous compute resources still desire the ability to run code, # completely without authentication. # These services can disable all authentication and security checks, # with the full knowledge of what that implies. # Default: False # c.ServerApp.disable_check_xsrf = False ## handlers that should be loaded at higher priority than the default services # Default: [] # c.ServerApp.extra_services = [] ## Extra paths to search for serving static files. # # This allows adding javascript/css to be available from the Jupyter server machine, # or overriding individual files in the IPython # Default: [] # c.ServerApp.extra_static_paths = [] ## Extra paths to search for serving jinja templates. # # Can be used to override templates from jupyter_server.templates. # Default: [] # c.ServerApp.extra_template_paths = [] ## Open the named file when the application is launched. # Default: &#39;&#39; # c.ServerApp.file_to_run = &#39;&#39; ## The URL prefix where files are opened directly. # Default: &#39;notebooks&#39; # c.ServerApp.file_url_prefix = &#39;notebooks&#39; ## Generate default config file. # See also: JupyterApp.generate_config # c.ServerApp.generate_config = False ## DEPRECATED. Use IdentityProvider.get_secure_cookie_kwargs # Default: {} # c.ServerApp.get_secure_cookie_kwargs = {} ## The identity provider class to use. # Default: &#39;jupyter_server.auth.identity.PasswordIdentityProvider&#39; # c.ServerApp.identity_provider_class = &#39;jupyter_server.auth.identity.PasswordIdentityProvider&#39; ## DEPRECATED. Use ZMQChannelsWebsocketConnection.iopub_data_rate_limit # Default: 0.0 # c.ServerApp.iopub_data_rate_limit = 0.0 ## DEPRECATED. Use ZMQChannelsWebsocketConnection.iopub_msg_rate_limit # Default: 0.0 # c.ServerApp.iopub_msg_rate_limit = 0.0 ## The IP address the Jupyter server will listen on. # Default: &#39;localhost&#39; # c.ServerApp.ip = &#39;localhost&#39; ## Supply extra arguments that will be passed to Jinja environment. # Default: {} # c.ServerApp.jinja_environment_options = {} ## Extra variables to supply to jinja templates when rendering. # Default: {} # c.ServerApp.jinja_template_vars = {} ## Dict of Python modules to load as Jupyter server extensions.Entry values can # be used to enable and disable the loading ofthe extensions. The extensions # will be loaded in alphabetical order. # Default: {} # c.ServerApp.jpserver_extensions = {} ## The kernel manager class to use. # Default: &#39;jupyter_server.services.kernels.kernelmanager.MappingKernelManager&#39; # c.ServerApp.kernel_manager_class = &#39;jupyter_server.services.kernels.kernelmanager.MappingKernelManager&#39; ## The kernel spec manager class to use. Should be a subclass of # `jupyter_client.kernelspec.KernelSpecManager`. # # The Api of KernelSpecManager is provisional and might change without warning # between this version of Jupyter and the next stable one. # Default: &#39;builtins.object&#39; # c.ServerApp.kernel_spec_manager_class = &#39;builtins.object&#39; ## The kernel websocket connection class to use. # Default: &#39;jupyter_server.services.kernels.connection.base.BaseKernelWebsocketConnection&#39; # c.ServerApp.kernel_websocket_connection_class = &#39;jupyter_server.services.kernels.connection.base.BaseKernelWebsocketConnection&#39; ## DEPRECATED. Use ZMQChannelsWebsocketConnection.kernel_ws_protocol # Default: &#39;&#39; # c.ServerApp.kernel_ws_protocol = &#39;&#39; ## The full path to a private key file for usage with SSL/TLS. # Default: &#39;&#39; # c.ServerApp.keyfile = &#39;&#39; ## DEPRECATED. Use ZMQChannelsWebsocketConnection.limit_rate # Default: False # c.ServerApp.limit_rate = False ## Hostnames to allow as local when allow_remote_access is False. # # Local IP addresses (such as 127.0.0.1 and ::1) are automatically accepted # as local as well. # Default: [&#39;localhost&#39;] # c.ServerApp.local_hostnames = [&#39;localhost&#39;] ## The date format used by logging formatters for %(asctime)s # See also: Application.log_datefmt # c.ServerApp.log_datefmt = &#39;%Y-%m-%d %H:%M:%S&#39; ## The Logging format template # See also: Application.log_format # c.ServerApp.log_format = &#39;[%(name)s]%(highlevel)s %(message)s&#39; ## Set the log level by value or name. # See also: Application.log_level # c.ServerApp.log_level = 30 ## # See also: Application.logging_config # c.ServerApp.logging_config = {} ## The login handler class to use. # Default: &#39;jupyter_server.auth.login.LegacyLoginHandler&#39; # c.ServerApp.login_handler_class = &#39;jupyter_server.auth.login.LegacyLoginHandler&#39; ## The logout handler class to use. # Default: &#39;jupyter_server.auth.logout.LogoutHandler&#39; # c.ServerApp.logout_handler_class = &#39;jupyter_server.auth.logout.LogoutHandler&#39; ## Sets the maximum allowed size of the client request body, specified in the # Content-Length request header field. If the size in a request exceeds the # configured value, a malformed HTTP message is returned to the client. # # Note: max_body_size is applied even in streaming mode. # Default: 536870912 # c.ServerApp.max_body_size = 536870912 ## Gets or sets the maximum amount of memory, in bytes, that is allocated for use # by the buffer manager. # Default: 536870912 # c.ServerApp.max_buffer_size = 536870912 ## Gets or sets a lower bound on the open file handles process resource limit. # This may need to be increased if you run into an OSError: [Errno 24] Too many # open files. This is not applicable when running on Windows. # Default: 0 # c.ServerApp.min_open_files_limit = 0 ## DEPRECATED, use root_dir. # Default: &#39;&#39; # c.ServerApp.notebook_dir = &#39;&#39; ## Whether to open in a browser after starting. # The specific browser used is platform dependent and # determined by the python standard library `webbrowser` # module, unless it is overridden using the --browser # (ServerApp.browser) configuration option. # Default: False # c.ServerApp.open_browser = False ## DEPRECATED in 2.0. Use PasswordIdentityProvider.hashed_password # Default: &#39;&#39; # c.ServerApp.password = &#39;&#39; ## DEPRECATED in 2.0. Use PasswordIdentityProvider.password_required # Default: False # c.ServerApp.password_required = False ## The port the server will listen on (env: JUPYTER_PORT). # Default: 0 # c.ServerApp.port = 0 ## The number of additional ports to try if the specified port is not available # (env: JUPYTER_PORT_RETRIES). # Default: 50 # c.ServerApp.port_retries = 50 ## Preferred starting directory to use for notebooks and kernels. # Default: &#39;&#39; # c.ServerApp.preferred_dir = &#39;&#39; ## DISABLED: use %pylab or %matplotlib in the notebook to enable matplotlib. # Default: &#39;disabled&#39; # c.ServerApp.pylab = &#39;disabled&#39; ## If True, display controls to shut down the Jupyter server, such as menu items # or buttons. # Default: True # c.ServerApp.quit_button = True ## DEPRECATED. Use ZMQChannelsWebsocketConnection.rate_limit_window # Default: 0.0 # c.ServerApp.rate_limit_window = 0.0 ## Reraise exceptions encountered loading server extensions? # Default: False # c.ServerApp.reraise_server_extension_failures = False ## The directory to use for notebooks and kernels. # Default: &#39;&#39; # c.ServerApp.root_dir = &#39;&#39; ## The session manager class to use. # Default: &#39;builtins.object&#39; # c.ServerApp.session_manager_class = &#39;builtins.object&#39; ## Instead of starting the Application, dump configuration to stdout # See also: Application.show_config # c.ServerApp.show_config = False ## Instead of starting the Application, dump configuration to stdout (as JSON) # See also: Application.show_config_json # c.ServerApp.show_config_json = False ## Shut down the server after N seconds with no kernelsrunning and no activity. # This can be used together with culling idle kernels # (MappingKernelManager.cull_idle_timeout) to shutdown the Jupyter server when # it&#39;s not in use. This is not precisely timed: it may shut down up to a minute # later. 0 (the default) disables this automatic shutdown. # Default: 0 # c.ServerApp.shutdown_no_activity_timeout = 0 ## The UNIX socket the Jupyter server will listen on. # Default: &#39;&#39; # c.ServerApp.sock = &#39;&#39; ## The permissions mode for UNIX socket creation (default: 0600). # Default: &#39;0600&#39; # c.ServerApp.sock_mode = &#39;0600&#39; ## Supply SSL options for the tornado HTTPServer. # See the tornado docs for details. # Default: {} # c.ServerApp.ssl_options = {} ## Paths to set up static files as immutable. # # This allow setting up the cache control of static files as immutable. It # should be used for static file named with a hash for instance. # Default: [] # c.ServerApp.static_immutable_cache = [] ## Supply overrides for terminado. Currently only supports "shell_command". # Default: {} # c.ServerApp.terminado_settings = {} ## Set to False to disable terminals. # # This does *not* make the server more secure by itself. # Anything the user can in a terminal, they can also do in a notebook. # # Terminals may also be automatically disabled if the terminado package # is not available. # Default: False # c.ServerApp.terminals_enabled = False ## DEPRECATED. Use IdentityProvider.token # Default: &#39;<DEPRECATED>&#39; # c.ServerApp.token = &#39;<DEPRECATED>&#39; ## Supply overrides for the tornado.web.Application that the Jupyter server uses. # Default: {} # c.ServerApp.tornado_settings = {} ## Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded- # For headerssent by the upstream reverse proxy. Necessary if the proxy handles # SSL # Default: False # c.ServerApp.trust_xheaders = False ## Disable launching browser by redirect file # For versions of notebook > 5.7.2, a security feature measure was added that # prevented the authentication token used to launch the browser from being visible. # This feature makes it difficult for other users on a multi-user system from # running code in your Jupyter session as you. # However, some environments (like Windows Subsystem for Linux (WSL) and Chromebooks), # launching a browser using a redirect file can lead the browser failing to load. # This is because of the difference in file structures/paths between the runtime and # the browser. # # Disabling this setting to False will disable this behavior, allowing the browser # to launch by using a URL and visible token (as before). # Default: True # c.ServerApp.use_redirect_file = True ## Specify where to open the server on startup. This is the # `new` argument passed to the standard library method `webbrowser.open`. # The behaviour is not guaranteed, but depends on browser support. Valid # values are: # # - 2 opens a new tab, # - 1 opens a new window, # - 0 opens in an existing window. # # See the `webbrowser.open` documentation for details. # Default: 2 # c.ServerApp.webbrowser_open_new = 2 ## Set the tornado compression options for websocket connections. # # This value will be returned from # :meth:`WebSocketHandler.get_compression_options`. None (default) will disable # compression. A dict (even an empty one) will enable compression. # # See the tornado docs for WebSocketHandler.get_compression_options for details. # Default: None # c.ServerApp.websocket_compression_options = None ## The base URL for websockets, # if it differs from the HTTP server (hint: it almost certainly doesn&#39;t). # # Should be in the form of an HTTP origin: ws[s]://hostname[:port] # Default: &#39;&#39; # c.ServerApp.websocket_url = &#39;&#39;
07-19
### 配置Jupyter Notebook服务器并解决密码设置失败的问题 在配置Jupyter Notebook服务器时,用户需要确保生成配置文件、正确设置密码,并处理可能出现的密码设置失败问题。以下是详细的配置步骤和解决方案: #### 生成Jupyter Notebook配置文件 首先,使用以下命令生成Jupyter Notebook的配置文件: ```bash jupyter notebook --generate-config ``` 该命令会在用户目录下生成一个`.jupyter`文件夹,并在其中创建`jupyter_notebook_config.py`配置文件。此文件用于设置Jupyter Notebook的运行参数,包括绑定的IP地址、端口、密码等[^3]。 #### 设置Jupyter Notebook密码 在设置密码时,运行以下命令: ```bash jupyter notebook password ``` 系统会提示输入并确认密码。如果两次输入的密码不一致,会提示警告信息“password warning: passwords do not match”。为避免此问题,建议手动输入密码,并确保两次输入完全一致,避免使用特殊字符或空格[^1]。 #### 手动修改配置文件设置密码 如果通过命令行设置密码失败,可以手动修改配置文件。找到`jupyter_notebook_config.py`文件,并修改`c.NotebookApp.password`字段: ```python c.NotebookApp.password = &#39;your_hashed_password&#39; ``` 密码字段需要使用哈希值,可以通过以下Python代码生成: ```python from notebook.auth import passwd print(passwd(&#39;your_password&#39;)) ``` 运行后,输入密码并获取哈希值,将其填入配置文件中。此方法可绕过密码匹配检查,直接设置密码[^1]。 #### 配置Jupyter Notebook服务器访问 为了允许远程访问Jupyter Notebook服务器,需要修改配置文件中的绑定地址和端口: ```python c.NotebookApp.ip = &#39;0.0.0.0&#39; c.NotebookApp.port = 8000 c.NotebookApp.open_browser = False c.NotebookApp.allow_remote_access = True ``` 以上配置允许Jupyter Notebook在所有网络接口上监听,并通过指定端口访问。用户可以在本地浏览器中输入`https://<服务器IP>:<端口>`来访问Jupyter Notebook[^2]。 #### 处理密码设置失败问题 如果在设置密码时遇到失败,可能的原因包括: - **密码输入不一致**:确保两次输入的密码完全一致。 - **密码包含非法字符**:避免使用特殊字符或空格。 - **配置文件损坏**:删除`~/.jupyter/jupyter_notebook_config.json`文件后重新设置密码。 - **权限问题**:确保用户对`.jupyter`目录具有读写权限,可通过以下命令修改权限: ```bash chmod -R u+rw ~/.jupyter ``` #### 启动Jupyter Notebook服务器 完成配置后,启动Jupyter Notebook服务器: ```bash jupyter notebook ``` 服务器启动后,用户可以通过本地浏览器访问Jupyter Notebook并使用设置的密码登录。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值