Процесс Загрузчика Unix/Linux

s/$f/$g/ заменяет первое вхождение $f $g на каждой строке. Если Вы хотите заменить только первое вхождение $f в целом файле необходимо сказать так. Это - то, в чем Вы в конечном счете выполнили sed с 0,/$f/ s/$f/$g/ (замена $f $g до и включая первое вхождение $f). В Perl можно записать это в более подробном, но более легком для понимания пути как это (примечание: посмотрите ниже для заключения в кавычки проблем):

perl -i -pe 'if ($n==0) {s/$f/$g/; $n=1;} elsif ($n==1) {s/$f/$h/; $n=2}'

Ваш код страдает от многочисленных проблем заключения в кавычки; Вы столкнетесь с проблемой, если Ваши имена файлов будут содержать пробел, globbing символы или непечатные символы (такие как последовательности байта, которые не существуют в текущей локали). К счастью, эти проблемы легко устранить.

Во-первых, некоторые универсальные проблемы оболочки. Всегда подстановки переменных двойной кавычки "$foo" и замены команды "$(foo)" если Вы не знаете, почему необходимо оставить их, закрыл кавычки. Если Вы оставляете их, закрыл кавычки, результат разделяется на отдельные слова везде, где он содержит пробел, и каждое слово рассматривают как шаблон шарика. Таким образом, если переменная, оказывается, не содержит разделенный от пробела список шаблонов шарика, поместите двойные кавычки вокруг этого. Кроме того, я рекомендую использовать $(…) вместо `…`; они эквивалентны, за исключением того, что вложенное заключение в кавычки внутри `…` ненадежно (также, ` легко перепутан с ').

Не анализируйте вывод ls. Если необходимо действовать на все файлы в каталоге, оболочка имеет встроенную конструкцию, которая работает: globbing. Вместо $(ls /path/to/directory), записать /path/to/directory/*. Это генерирует имена файлов с путем к каталогу; это почти всегда, в чем Вы нуждаетесь так или иначе, и если Вы не делаете, можно или звонить cd заранее или снимите все или часть каталога. Ниже, я использую ${f#*/*/}, что означает $f с самым коротким соответствием префикса */*/ неизолированный.

for f in .templates/template_text/*; do
  g=$(cat "$f")
  h=$(cat ".templates/template_html/${f#*/*/}")
  find to_process/ -type f …
done

С find, можно использовать более простую конструкцию -exec, хотя -print0 объединенный с xargs -0 работы также. Не использовать xargs без -0, поскольку это ожидает вход, заключенный в кавычки специфическим способом который find не производит.

find to_process/ -type f -exec perl … {} +

Следующий выпуск - то, что Вы вставляете строки $f, $g и $h непосредственно в Вашем sed или регулярном выражении жемчуга. Это неправильно: они переменная не содержат регулярное выражение с разделителем (/ в обоих случаях) заключенный в кавычки. С sed необходимо было бы сделать передачу заключения в кавычки на строках, добавив обратную косую черту перед любой из /*.\[ в $f и перед любой из \&/ в $g и $h. С Perl существует более простой путь: передайте значения через среду и обязательно скажите Perl, который, что Вы имеете, строка и не regexp.

export f g h
find to_process/ -type f -exec perl -i -e '
    if ($n==0) {s/\Q$ENV{f}/$ENV{g}/; $n=1;}
    elsif ($n==1) {s/\Q$ENV{f}/$ENV{h}/; $n=2}}
' {} +
7
09.10.2012, 16:38
2 ответа

Пользователь обычно встречается с тремя типами файлов ELF —.o файлы, регулярные исполняемые файлы и совместно использованные библиотеки. В то время как все эти файлы служат различным целям, их внутренние файлы структуры весьма схожи.

Одно универсальное понятие среди всех различных типов файлов ELF (и также a.out и много других форматов исполняемого файла) является понятием раздела. Раздел является набором информации подобного типа. Каждый раздел представляет часть файла. Например, исполняемый код всегда помещается в раздел, известный как .text; все переменные данных, инициализированные пользователем, помещаются в раздел, известный как .data; и неинициализированные данные помещаются в раздел, известный как .bss.

На самом деле можно создать формат исполняемого файла, где все смешано (как MS DOS). Но деление исполняемых файлов в разделы имеет важные преимущества. Например, после того как Вы загрузили исполняемые части исполняемого файла в память, эти ячейки памяти не должны изменяться. На современной архитектуре машины диспетчер памяти может отметить части памяти, только для чтения, такой, что любая попытка изменить местоположение постоянной памяти приводит к смерти программы и дампу ядра. Таким образом, вместо того, чтобы просто говорить, что мы не ожидаем, что конкретная ячейка памяти изменится, мы можем указать, что любая попытка изменить местоположение постоянной памяти является фатальной ошибкой, указывающей на ошибку в приложении. Однако обычно Вы не можете индивидуально установить состояние только для чтения для каждого байта памяти — вместо этого можно индивидуально установить меры защиты регионов памяти, известной как страницы. На i386 архитектуре размер страницы составляет 4 096 байтов — таким образом Вы могли указать, что обращается 0-4095, только для чтения, и байты 4096 и перезаписываемы, например.

Учитывая, что мы хотим все исполняемые части исполняемого файла в постоянной памяти и всех модифицируемых местоположениях памяти (таких как переменные) в перезаписываемой памяти, оказывается самым эффективным сгруппировать все исполняемые части исполняемого файла в один раздел памяти (раздел .text), и все модифицируемые области данных вместе в другую область памяти (впредь известный как раздел .data).

Дальнейшее различие сделано между переменными данных, которые пользователь инициализировал и переменные данных, которые не инициализировал пользователь. Если пользователь не указал начальное значение переменной, нет никакого опустошительного пространства смысла в исполняемом файле для хранения значения. Таким образом инициализированные переменные сгруппированы в раздел .data, и неинициализированные переменные сгруппированы в раздел .bss, который является особенным, потому что он не занимает место в файле — он только говорит, сколько пространства необходимо для неинициализированных переменных.

Когда Вы просите, чтобы ядро загрузило и выполнило исполняемый файл, оно запускается путем рассмотрения заголовка изображения для подсказок о том, как загрузить изображение. Это определяет местоположение раздела .text в исполняемом файле, загружает его в соответствующие части памяти и отмечает эти страницы как только для чтения. Это затем определяет местоположение раздела .data в исполняемом файле и загружает его в адресное пространство пользователя, на этот раз в памяти чтения-записи. Наконец, это находит местоположение и размер раздела .bss от заголовка изображения, и добавляет соответствующие страницы памяти к адресному пространству пользователя. Даже при том, что пользователь не указал начальные значения переменных, помещенных в .bss, условно ядро инициализирует всю эту память для обнуления.

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

10
27.01.2020, 20:16
  • 1
    Это могло быть добавлено, то динамическое подключение, сделан ld-linux.so, который является процессом пространства пользователя. –  jofel 09.10.2012, 15:03
  • 2
    @jofel: да, Вы совершенно правы. Ради человека, который задал этот вопрос, позволяют мне сказать, что ld-linux.so является интерпретатором, который запускается от заголовков эльфа ядром. Целевая точка входа ELF установлена во вспомогательном векторе типа "ЗАПИСЬ".The, ядро открывает требуемый интерпретатор, отображает регионы памяти и запускает ее выполнение в точке входа ELF ld. Затем загрузчик анализирует целевой файл ELF, выполняет его работу загрузчика и устанавливает EIP для предназначения для точки входа ELF. Это - то, как динамическое подключение сделано ld-linux.so. –  The Dark Knight 09.10.2012, 15:08
  • 3
    Хорошо нет. Ядро (или динамический компоновщик) не заботится о разделах (.text, .data, и т.д.) вообще. Они используют таблицу заголовка программы, которая описывает сегменты для загрузки в память. –  ysdx 10.09.2015, 00:49

Это зависит от ОС.

Например, в GNU Hurd исполняемый файл загружается сервером exec .

В более типичной монолитной ОС это выполняется следующим образом:

  1. ядро ​​отображает исполняемый файл и динамический компоновщик в памяти;

  2. динамический компоновщик отображает разделяемые объекты в памяти.

Само ядро ​​Linux хранится в виде файла ELF: он загружается загрузчиком (например, GRUB).

4
27.01.2020, 20:16

Теги

Похожие вопросы