2011年3月22日星期二

寻找GNOME的休眠命令

为了试验Mac OS X Snow Leopard,把笔记本电脑硬盘全部格式化了。后来又重装了Ubuntu 10.10,但是休眠功能不行了。试着装了hibernate包,再休眠又可以了,在屏幕上可以看到hibernate包的s2disk命令存储内存印象的进度。卸载了hirbernate包,又不能休眠了。可是重装前的Ubuntu 10.10,不用装hibernate也可以休眠的。

记得上大学时候在实验室很旧的计算机上装的Breezy Badger(Ubuntu 5.10),是可以休眠的,可是自己的电脑就没法休眠。现在家用的台式机也是Ubuntu 10.10,没有装hibernate也可以休眠。

我就想知道在GNOME里面点击“Hibernate”后,到底执行了什么命令来休眠?装了hibernate用的是它的s2disk,那么不装的时候呢?我想一切皆有缘由的。

先Google了一下,和/usr/lib/indicator-session/gtk-logout-helper(由indicator-session提供)有关系。apt-get了indicator-session和indicator-applet的源代码,但是没有找到线索。倒是/usr/bin/gnome-session-save --shutdown-dialog(由gnome-session-bin提供)可以弹出关机的提示框:

用apt-get下载源代码,用grep大法搜索hibernate,在gnome-session的gsm-manager.c里面找到了如下的代码:

static void
manager_attempt_hibernate (GsmManager *manager)
{
        gboolean  can_hibernate;
        GError   *error;
        gboolean  ret;

        can_hibernate = up_client_get_can_hibernate (manager->priv->up_client);
        if (can_hibernate) {

                /* lock the screen before we suspend */
                manager_perhaps_lock (manager);

                error = NULL;
                ret = up_client_hibernate_sync (manager->priv->up_client, NULL, &error);
                if (!ret) {
                        g_warning ("Unexpected hibernate failure: %s",
                                   error->message);
                        g_error_free (error);
                }
        }
}


里面的up_client_hibernate_sync应该就是执行具体休眠操作的函数了。可是这个函数从哪里来呢?我没有什么浏览源码的工具,只有find、grep和vim。其实Google基本就够了,直接Google搜索这个函数名,哦,原来是upower的函数。再apt-get下来upower的源代码,这个函数在upower的libupower-glib/up-client.c里面,是这么写的:

gboolean
up_client_hibernate_sync (UpClient *client, GCancellable *cancellable, GError **error)
{
        gboolean ret;
        GError *error_local = NULL;

        g_return_val_if_fail (UP_IS_CLIENT (client), FALSE);
        g_return_val_if_fail (client->priv->proxy != NULL, FALSE);

        ret = dbus_g_proxy_call (client->priv->proxy, "Hibernate", &error_local,
                                 G_TYPE_INVALID, G_TYPE_INVALID);
        if (!ret) {
                /* DBus might time out, which is okay */
                if (g_error_matches (error_local, DBUS_GERROR, DBUS_GERROR_NO_REPLY)) {
                        g_debug ("DBUS timed out, but recovering");
                        ret = TRUE;
                        goto out;
                }

                /* an actual error */
                g_warning ("Couldn't hibernate: %s", error_local->message);
                g_set_error (error, 1, 0, "%s", error_local->message);
        }
out:
        if (error_local != NULL)
                g_error_free (error_local);
        return ret;
}


dbus_g_proxy_call函数执行了实际的休眠操作,Google一下立刻得到其文档。至于这个“Hibernate”的method怎么调的,我不懂DBus,没搞清楚。歪打正着地在src/linux/up-backend.c里面找到了答案:
#define UP_BACKEND_SUSPEND_COMMAND              "/usr/sbin/pm-suspend"
#define UP_BACKEND_HIBERNATE_COMMAND            "/usr/sbin/pm-hibernate"
#define UP_BACKEND_POWERSAVE_TRUE_COMMAND       "/usr/sbin/pm-powersave true"
#define UP_BACKEND_POWERSAVE_FALSE_COMMAND      "/usr/sbin/pm-powersave false"
原来用的硬盘上的pm-hibernate命令(由pm-utils包提供)。这个命令的落实,在/usr/lib/pm-utils/pm-functions脚本里面可以找到:
do_hibernate()
{
        [ -n "${HIBERNATE_MODE}" ] && \
        grep -qw "${HIBERNATE_MODE}" /sys/power/disk && \
        echo -n "${HIBERNATE_MODE}" > /sys/power/disk
        echo -n "disk" > /sys/power/state
}
好了,就此打住了。至于HIBERNATE_MODE的设置,再不往下追究了,否则就没完没了了。

找到这个休眠命令的过程很纠结。以后要提高开发本领,至少是读代码的本领,才能在类似的postmortem分析中快速找到答案。

没有评论:

发表评论