Как отличаются функции Perl Cwd:: cwd и Cwd:: getcwd?

Вопрос

В чем разница между Cwd::cwd и Cwd::getcwd в Perl, вообще говоря, независимо от какой-либо конкретной платформы? Почему у Perl есть и то, и другое? Каково намеренное использование, которое следует использовать в каких сценариях? (Пример использования будет оценен.) Имеет ли это значение? (Предполагая, что я их не смешиваю.) Может ли какой-либо вариант повлиять на переносимость? Какой из них чаще всего используется в модулях?

Даже если я интерпретирую руководство, мы говорим, что, за исключением случаев с квадратом cwd, `pwd` и getcwd просто вызывает getcwd из unistd.h, какова фактическая разница? В любом случае, это работает только на системах POSIX.

Я всегда могу прочитать реализацию, но это ничего не говорит о значении этих функций. Детали реализации могут меняться, а не так определенно. (В противном случае происходит перерыв, что является серьезным делом.)

Что говорится в руководстве

Указание Perls Страница руководства модуля cdd:

Каждая из этих функций вызывается без аргументов и возвращает абсолютный путь к текущему рабочему каталогу.

  • getcwd

    my $cwd = getcwd();

    Возвращает текущий рабочий каталог.

    Предоставляет функцию POSIX getcwd (3) или повторно реализует ее, если она недоступна.

  • УХО

    my $cwd = cwd();

    cwd() является наиболее естественной формой для текущей архитектуры. Для большинства систем он идентичен `pwd` (но без терминатора конечной строки).

И в разделе "Примечания":

  • На самом деле, в Mac OS функции getcwd(), fastgetcwd() и fastcwd() - это все псевдонимы для функции cwd(), которая в Mac OS вызывает `pwd`. Аналогично, функция abs_path() является псевдонимом для fast_abs_path()

Хорошо, я знаю, что в Mac OS 1 нет разницы между getcwd() и cwd(), так как оба фактически сводятся к `pwd`. Но что на других платформах? (Im особенно интересует Debian Linux.)


1 Классические Mac OS, а не OS X. $^O значения MacOS и darwin для Mac OS и OS X, соответственно. Спасибо, @tobyink и @ikegami.

И немного мета-вопрос: как избежать заданий по подобным вопросам для других модулей с очень похожими функциями? Существует ли универсальный способ обнаружения различий, кроме как выкапывать реализацию? (В настоящее время, я думаю, что если в документации не ясно о предполагаемом использовании и различиях, я должен спросить кого-то более опытного или прочитать реализацию сам.)

Ответ 1

Вообще говоря,

Я думаю, идея состоит в том, что cwd() всегда решает внешний, специфичный для ОС способ получения текущего рабочего каталога. То есть, запуская pwd в Linux, command /c cd в DOS, /usr/bin/fullpath -t в QNX и т.д. - все примеры взяты из фактического Cwd.pm. Предполагается, что getcwd() использует системный вызов POSIX, если он доступен, и возвращается к cwd(), если нет.

Почему у нас есть оба? В текущей реализации я считаю, что экспортировать только getcwd() было бы достаточно для большинства систем, но кто знает, почему логика "если syscall доступна, используйте ее, иначе запустите cwd()" может выйти из строя в какой-либо системе (например, на MorphOS в Perl 5.6.1).

В Linux

В Linux cwd() запустится `/bin/pwd` (фактически выполнит двоичный файл и получит его вывод), а getcwd() выдаст системный вызов getcwd(2).

Проверяется фактический эффект через strace

Можно использовать strace(1), чтобы увидеть, что в действии:

Использование cwd():

$ strace -f perl -MCwd -e 'cwd(); ' 2>&1 | grep execve
execve("/usr/bin/perl", ["perl", "-MCwd", "-e", "cwd(); "], [/* 27 vars */]) = 0
[pid 31276] execve("/bin/pwd", ["/bin/pwd"], [/* 27 vars */] <unfinished ...>
[pid 31276] <... execve resumed> )      = 0

Используя getcwd():

$ strace -f perl -MCwd -e 'getcwd(); ' 2>&1 | grep execve
execve("/usr/bin/perl", ["perl", "-MCwd", "-e", "getcwd(); "], [/* 27 vars */]) = 0

Чтение Cwd.pm source

Вы можете посмотреть источники (Cwd.pm, например, в CPAN) и посмотреть, что для Linux cwd() отображается вызов на _backtick_pwd, который, как следует из названия, вызывает pwd в обратных циклах.

Вот фрагмент от Cwd.pm, с моими комментариями:

unless ($METHOD_MAP{$^O}{cwd} or defined &cwd) {
    ...
    # some logic to find the pwd binary here, $found_pwd_cmd is set to 1 on Linux
    ...
    if( $os eq 'MacOS' || $found_pwd_cmd )
    {
        *cwd = \&_backtick_pwd;  # on Linux we actually go here
    }
    else {
        *cwd = \&getcwd;
    }
}

Тест производительности

Наконец, разница между двумя заключается в том, что cwd(), который вызывает другой двоичный файл, должен быть медленнее. Мы можем сделать какой-то тест производительности:

$ time perl -MCwd -e 'for (1..10000) { cwd(); }'

real    0m7.177s
user    0m0.380s
sys     0m1.440s

Теперь сравните его с системным вызовом:

$ time perl -MCwd -e 'for (1..10000) { getcwd(); }'

real    0m0.018s
user    0m0.009s
sys     0m0.008s

Обсуждение, выбор

Но поскольку вы обычно не слишком часто запрашиваете текущий рабочий каталог, обе опции будут работать - если только вы не можете запускать какие-либо процессы по какой-либо причине, связанные с ulimit, ситуацией с памятью и т.д.

Наконец, что касается выбора того, какой из них использовать: для Linux я всегда использовал бы getcwd(). Я полагаю, вам нужно будет сделать свои тесты и выбрать, какую функцию использовать, если вы собираетесь написать переносную часть кода, которая будет работать на какой-то действительно странной платформе (здесь, конечно, Linux, OS X и Windows не входят в список странных платформ).