Журнал LinuxFormat - перейти на главную

LXF155:Android:программирование

Материал из Linuxformat
Перейти к: навигация, поиск


Ваши программные наработки не пропадут даром

Содержание

ARM и Android: Про­грам­ми­ро­ва­ние

Часть 1: Вве­де­ние. Ан­д­рей Бо­ров­ский на­чи­на­ет рас­сказ о том, как пе­ре­не­сти на Android про­грам­мы Linux, на­пи­сан­ные на C или C++.

ОС Android хо­ро­ша тем, что по­зво­ля­ет нам пи­сать соб­ст­вен­ные при­ло­жения для мо­биль­ных уст­ройств са­мых раз­ных клас­сов. А еще эта ОС хо­ро­ша тем, что осно­ва­на на яд­ре Linux. Стан­дарт­но­го спо­со­ба пе­ре­но­са и на­пи­сания ко­да Linux на Android не су­ще­ст­ву­ет, но на­стоя­щих ли­нук­сои­дов никогда не пу­га­ли нестан­дарт­ные под­хо­ды.

Что та­кое Android? Сре­ди мно­гих оп­ре­де­лений воз­мож­но и та­кое: Android – это пер­вый ди­ст­ри­бу­тив (те­перь уже це­лое се­мей­ст­во ди­ст­ри­бу­ти­вов) Linux, ко­то­рый при­шел­ся по ду­ше ши­ро­ким сло­ям поль­зо­ва­те­лей. Мы дав­но уже не меч­та­ем о рас­про­странении Linux на ра­бо­чих сто­лах. Когда в мо­ду во­шли нетбу­ки, мы на­дея­лись, что имен­но эти уст­рой­ст­ва ста­нут плат­фор­мой, на ко­то­рой Linux су­ме­ет по­теснить дру­гие ОС. Но это­го не слу­чи­лось. Не­ко­то­рые пред­ска­зы­ва­ли, что Linux смо­жет от­вое­вать свою нишу при пе­ре­хо­де с 32-бит­ных ПК на 64-бит­ные, но и это­го не про­изош­ло. Ус­пех при­шел там, где его не очень-то и жда­ли – в ми­ре мо­биль­ных те­ле­фо­нов и план­шет­ных ком­пь­ю­те­ров. Яв­ля­ет­ся ли Android на­стоя­щей ОС Linux? На мой взгляд – безуслов­но. Яд­ро Android – это слег­ка мо­ди­фи­ци­ро­ван­ное яд­ро Linux се­мей­ст­ва 2.6.

От­ли­чия яд­ра Android от стан­дарт­ных ядер Linux 2.6.x пе­ре­чис­ле­ны и ра­зо­бра­ны во мно­гих пуб­ли­ка­ци­ях, ко­то­рые мож­но най­ти в се­ти. Мы не станем пе­ре­чис­лять эти от­ли­чия, а по­зна­ко­мим­ся с неко­то­ры­ми из них на прак­ти­ке. Как все вы, конеч­но, знае­те, Android от­ли­ча­ет­ся от осталь­ных сис­тем на осно­ве Linux тем, что при­клад­ные про­грам­мы пи­шут­ся на язы­ке Java и вы­пол­ня­ют­ся на спе­ци­аль­ной вир­ту­аль­ной ма­шине. Для вы­во­да гра­фи­ки ис­поль­зу­ет­ся не X-сер­вер, а бо­лее под­хо­дя­щая для мо­биль­ных уст­ройств гра­фи­че­­ская сис­те­ма.

Раз­ра­бот­ка для Android: дру­гой взгляд

Все это, од­на­ко, не ме­ша­ет нам пи­сать для Android про­грам­мы на язы­ках C и C++ и ком­пи­ли­ро­вать их в ма­шин­ный код, ко­то­рый бу­дет вы­пол­нять­ся непо­сред­ст­вен­но про­цес­со­ром (в этих стать­ях мы рас­смат­ри­ва­ем сис­те­мы Android на осно­ве про­цес­со­ра ARM). Для это­го нам нуж­но взгля­нуть на ОС Android как на осо­бый ва­ри­ант Linux со сво­им соб­ст­вен­ным на­бо­ром биб­лио­тек и несколь­ко нестан­дарт­ны­ми пра­ви­ла­ми сбор­ки при­ло­жений.

За­чем пи­сать про­грам­мы для Android на C?

О при­чи­нах по­пу­ляр­но­сти уст­ройств с Android сре­ди мас­со­во­го по­тре­би­те­ля ска­за­но уже мно­го, и я рас­про­стра­нять­ся на эту те­му не бу­ду. Но сре­ди поль­зо­ва­те­лей Android есть од­на спе­ци­фи­че­­ская ка­те­го­рия, к ко­то­рой, я уве­рен, при­над­ле­жат и мно­гие чи­та­те­ли на­ше­го жур­на­ла. Речь идет о лю­дях, ко­то­рым скуч­но про­сто поль­зо­вать­ся уст­рой­ст­вом в уста­нов­лен­ных про­из­во­ди­те­лем рам­ках. Эти лю­ди стре­мят­ся за­гля­нуть под ка­пот, уз­нать, как ра­бо­та­ет сис­те­ма из­нут­ри, най­ти ей но­вые, ост­ро­ум­ные и ори­ги­наль­ные при­менения. Тех, кто боль­ше дру­гих преуспел в этом де­ле, ува­жи­тель­но име­ну­ют ха­ке­ра­ми. Ес­ли вы хо­ти­те на­страи­вать свои уст­рой­ст­ва под свои по­треб­но­сти и ис­поль­зо­вать при этом боль­ше ин­ст­ру­мен­тов, чем доступ­но обыч­ным раз­ра­бот­чи­кам Android, тогда этот учебник для вас.

Но есть и еще од­на ка­те­го­рия про­грам­ми­стов, ко­то­рым из­ло­жен­ное да­лее мо­жет быть по­лез­но. Это те лю­ди, ко­то­рые хо­тят изу­чить спе­ци­фи­ку про­грам­ми­ро­вания на мик­ро­про­цес­со­рах ARM. До по­яв­ления уст­ройств Android основ­ным учеб­ным по­со­би­ем для про­грам­ми­стов ARM бы­ли учеб­ные и де­мон­ст­ра­ци­он­ные пла­ты мик­ро­кон­трол­ле­ров раз­лич­но­го уров­ня. При этом, по­ми­мо соб­ст­вен­но сис­те­мы команд ARM, тре­бо­ва­лось изу­чать мно­го до­полнитель­ных ве­щей: прин­ци­пы рас­по­ло­жения про­грам­мы в па­мя­ти, ад­ре­са ре­ги­ст­ров кон­трол­ле­ров и зна­чения, ко­то­рые в эти ад­ре­са нуж­но за­пи­сы­вать.

Спо­ру нет, ес­ли вы со­би­рае­тесь про­грам­ми­ро­вать для ARM «на го­лом же­ле­зе» (на­при­мер, про­грам­ми­ро­вать мик­ро­кон­трол­ле­ры), вы долж­ны знать и понимать все эти ве­щи хо­тя бы для то­го ти­па мик­ро­кон­трол­ле­ров, с ко­то­рым вы ра­бо­тае­те. Про­бле­ма за­клю­ча­ет­ся в том, что в раз­ных ар­хи­тек­ту­рах, осно­ван­ных на ARM, все эти низ­ко­уровневые де­та­ли реа­ли­зо­ва­ны по-раз­но­му. Иногда да­же уст­рой­ст­ва из од­но­го мо­дель­но­го ря­да не об­ла­да­ют об­рат­ной со­вмес­ти­мо­стью. Ес­ли вы толь­ко при­сту­пае­те к изу­чению ARM, вам, вполне спра­вед­ли­во, за­хо­чет­ся иметь де­ло с сис­те­мой, ко­то­рая бы по­зво­ля­ла скон­цен­три­ро­вать­ся на воз­мож­но­стях са­мо­го мик­ро­про­цес­со­ра. Уст­рой­ст­ва Android пре­достав­ля­ют вам та­кую воз­мож­ность. Де­вай­сы, ра­бо­таю­щие под управ­лением Android – это сво­его ро­да IBM PC в ми­ре ARM. Так же, как на ПК, вы мо­же­те на­чать про­грам­ми­ро­вать на ас­семб­ле­ре для ARM Android, не за­бо­тясь о та­ких ве­щах, как за­груз­ка про­грам­мы в па­мять или инициа­ли­за­ция таб­ли­цы век­то­ров пре­ры­ваний.

Тре­тья при­чи­на для освоения ме­ханиз­мов, с по­мо­щью ко­то­рых Android взаи­мо­дей­ст­ву­ет с ко­дом, на­пи­сан­ным на C/C++, свя­за­на с ес­те­ст­вен­ным же­ланием раз­ра­бот­чи­ков пе­ренести имею­щийся у них код Linux (будь то биб­лио­те­ки или ис­полнимые фай­лы) на Android.

Чем ком­пи­ли­ро­вать про­грам­мы для ARM?

Хо­тя у уст­ройств Android доста­точ­но про­цес­сор­ной мощ­но­сти для то­го, что­бы са­мим ком­пи­ли­ро­вать и со­би­рать про­грам­мы для се­бя, пол­но­цен­ных средств раз­ра­бот­ки под Android, ра­бо­таю­щих на плат­фор­ме Android, по­ка что нет (впро­чем, они мо­гут поя­вить­ся, и очень ско­ро). Сей­час про­грам­мы для Android со­би­ра­ют­ся на обыч­ных ПК, а за­тем за­гру­жа­ют­ся в уст­рой­ст­во Android или в про­грамм­ный эму­ля­тор та­ко­го уст­рой­ст­ва. У ка­ж­до­го уст­рой­ст­ва Android (по крайней ме­ре, из тех, что я ви­дел – а ви­дел я мно­го) есть от­ла­доч­ный разъ­ем, че­рез ко­то­рый мож­но за­гру­жать и от­ла­жи­вать про­грам­мы. Прав­да, не на всех уст­рой­ст­вах этот ме­ханизм от­лад­ки пре­достав­ля­ет нам та­кую сво­бо­ду, как нам бы хо­те­лось (под­робнее об этом бу­дет ска­за­но ниже). Что же ка­са­ет­ся эму­ля­то­ров, то они пре­достав­ля­ют нам пол­ную сво­бо­ду дей­ст­вий, при пол­ной же невоз­мож­но­сти что-нибудь сло­мать. Хо­тя вы­пол­нять про­грам­мы в эму­ля­то­ре, конеч­но, не так ин­те­рес­но, как за­пускать их на на­стоя­щем же­ле­зе.

Вы­бор средств, по­зво­ляю­щих со­би­рать и от­ла­жи­вать на ПК про­грам­мы, пред­на­зна­чен­ные для ARM, весь­ма бо­гат. На­при­мер, па­кет Scratchbox 2, уста­нов­лен­ный со­вме­ст­но с эму­ля­то­ром QEMU, по­зво­лит не толь­ко со­би­рать про­грам­мы для ARM на ПК x86, но и вы­пол­нять их на том же ПК в ре­жи­ме эму­ля­ции раз­лич­ных ар­хи­тек­тур ARM. Одним из са­мых дру­же­ст­вен­ных ди­ст­ри­бу­ти­вов с точ­ки зрения кросс-плат­фор­мен­ной раз­ра­бот­ки для ARM сле­ду­ет при­знать Ubuntu. Этот ди­ст­ри­бу­тив по­зво­ля­ет без тру­да уста­но­вить ин­ст­ру­мен­та­рий сбор­ки, необ­хо­ди­мый для ARM, и дру­гие по­лез­ные ин­ст­ру­мен­ты. Впро­чем, по­сколь­ку на­шей це­ле­вой плат­фор­мой яв­ля­ет­ся Android, мы со­сре­до­то­чим­ся на сред­ст­вах раз­ра­бот­ки, спе­ци­аль­но пред­на­зна­чен­ных для этой плат­фор­мы.

Что та­кое про­грам­ма для ARM Linux

(thumbnail)
Наша программа в окне терминала Android.

Со­вмес­ти­мость про­грамм­но­го обес­пе­чения с неко­то­рой опе­ра­ци­он­ной сис­те­мой мож­но раз­де­лить на несколь­ко уровней. На са­мом нижнем уровне – ис­поль­зо­вание при­ня­то­го в сис­те­ме фор­ма­та ис­пол­няе­мо­го фай­ла и сис­тем­ных вы­зо­вов. На бо­лее вы­со­ком уровне со­вмес­ти­мость пред­по­ла­га­ет ис­поль­зо­вание при­ло­жением стан­дарт­ных биб­лио­тек опе­ра­ци­он­ной сис­те­мы. Иногда про­бле­му со­вмес­ти­мо­сти на вто­ром уровне мож­но обой­ти пу­тем ста­ти­че­­ской ком­по­нов­ки при­ло­жения с необ­хо­ди­мы­ми ему биб­лио­те­ка­ми (в этом слу­чае при­ло­жение мо­жет не по­ла­гать­ся на биб­лио­те­ки ОС). Но это ре­шение име­ет очень ог­раничен­ную об­ласть при­менения. Мно­гие раз­де­ляе­мые биб­лио­те­ки яв­ля­ют­ся «оберт­ка­ми», ко­то­рые пре­достав­ля­ют при­клад­ным про­грам­мам доступ к раз­лич­ным ком­понен­там сис­те­мы. Ско­рее все­го, сто­ронние ана­ло­ги этих биб­лио­тек, ко­то­рые ста­ти­че­­ски со­б­ран­ная про­грам­ма принесет с со­бой, не бу­дут ра­бо­тать пра­виль­но.

Что ка­са­ет­ся ARM Linux, то со­вмес­ти­мость про­грамм и ОС на нижнем уровне обес­пе­че­на стан­дар­том EABI (ко­то­рый как раз и опи­сы­ва­ет по­ря­док вы­зо­ва функ­ций и сис­тем­ные вы­зо­вы) и тем фак­том, что при сбор­ке про­грамм для ARM с по­мо­щью ин­ст­ру­мен­та­рия GCC генери­ру­ют­ся фай­лы в фор­ма­те ELF. На уровне биб­лио­тек все об­сто­ит сложнее. По­сколь­ку в Linux су­ще­ст­ву­ет дав­няя тра­ди­ция со­би­рать про­грам­мы спе­ци­аль­но для кон­крет­ной вер­сии ОС, мно­гие ди­ст­ри­бу­ти­вы во­об­ще не за­бо­тят­ся о том, что­бы сто­роннее при­ло­жение мог­ло най­ти необ­хо­ди­мые биб­лио­те­ки. Из это­го сле­ду­ет, что про­стей­шие про­грам­мы для Linux мо­гут ра­бо­тать на всех сис­те­мах ARM Linux, тогда как про­грам­мы, пре­достав­ляю­щие бо­лее ши­ро­кую функ­цио­наль­ность, долж­ны со­би­рать­ся спе­ци­аль­но для ка­ж­до­го ди­ст­ри­бу­ти­ва. Что ка­са­ет­ся ОС Android, то с точ­ки зрения доступ­ных по умол­чанию биб­лио­тек эта ОС от­ли­ча­ет­ся от дру­гих ва­ри­ан­тов Linux боль­ше, чем лю­бые два дру­гих ва­ри­ан­та Linux от­ли­ча­ют­ся друг от дру­га. На­чать хо­тя бы с то­го, что в ка­че­­ст­ве стан­дарт­ной биб­лио­те­ки C вме­сто glibc ис­поль­зу­ет­ся биб­лио­те­ка Bionic, спе­ци­аль­но раз­ра­бо­тан­ная для Android.

На­ши пер­вые опы­ты

Со­брать про­стей­шую про­грам­му Linux, спо­соб­ную ра­бо­тать под управ­лением Android (и дру­гих ARM Linux), не так уж и слож­но. Рас­смот­рим лис­тинг (файл hello.c):

void _start() {

char * s = “Hello World\n”;

__asm__ (“mov r0, #0; mov r1, %[sptr]; mov r2, #13; swi 0 × 900004” // _write(0, s, 13);

[sptr] “r” (s)
“r0”, “r1”, “r2”);

__asm__ (“mov r0, #0; swi 0x900001”); // _exit(0);

}

По­че­му _start()?

Ес­ли вы ду­мае­те, что точ­кой вхо­да в про­грам­му яв­ля­ет­ся функ­ция main(), то оши­бае­тесь. При ис­поль­зо­вании GCC по умол­чанию точ­кой вхо­да яв­ля­ет­ся функ­ция _start(), ко­то­рая до­бав­ля­ет­ся ком­по­нов­щи­ком ав­то­ма­ти­че­­ски при ис­поль­зо­вании биб­лио­те­ки вре­мени вы­полнения glibc (с по­мо­щью спе­ци­аль­но­го клю­ча точ­ку вхо­да мож­но из­менить).

Функ­ция _start() под­го­тав­ли­ва­ет вы­зов функ­ции main(), вы­зы­ва­ет main(), а так­же га­ран­ти­ру­ет, что по­сле вы­хо­да из main() про­грам­ма за­вер­шит­ся кор­рект­ным об­ра­зом. Вы мо­же­те за­вер­шить про­грам­му непо­сред­ст­вен­но из main() с по­мо­щью функ­ции exit(). Эта функ­ция иниции­ру­ет сис­тем­ный вы­зов Linux, ко­то­рый тут же за­вер­шит ва­шу про­грам­му. Но обыч­но так не де­ла­ют. В боль­шин­ст­ве про­грамм вы­ход из функ­ции main() осу­ще­ст­в­ля­ет­ся с по­мо­щью опе­ра­то­ра return, а это, са­мо по се­бе, не при­ве­дет к за­вер­шению про­грам­мы. Вы­ход из функ­ции main() пе­ре­да­ет управ­ление все той же функ­ции _start(), ко­то­рая за­вер­ша­ет про­грам­му с по­мо­щью сис­тем­но­го вы­зо­ва exit().

В на­шем при­ме­ре мы во­об­ще от­ка­за­лись от функ­ции main() (за нена­доб­но­стью), а за­од­но от­ка­за­лись и от биб­лио­те­ки вре­мени вы­полнения (в ОС Android ис­поль­зу­ет­ся соб­ст­вен­ный ва­ри­ант этой биб­лио­те­ки). Вме­сто то­го, что­бы вы­зы­вать стан­дарт­ные функ­ции printf() и exit(), мы ис­поль­зу­ем сис­тем­ные вы­зо­вы Linux на­пря­мую. Тон­кий ва­ри­ант про­грам­мы необ­хо­ди­мо ком­пи­ли­ро­вать с клю­чом -nostdlib, ко­то­рый при­ка­зы­ва­ет ком­по­нов­щи­ку во­об­ще не под­клю­чать биб­лио­те­ку вре­мени вы­полнения к про­грам­ме:

arm-linux-gnueabi-gcc hello.c -o hello -nostdlib

Те­перь нуж­но ско­пи­ро­вать по­лу­чив­ший­ся дво­ич­ный файл на уст­рой­ст­во Android и при­дать ему ат­ри­бут ис­пол­няе­мо­го фай­ла с по­мо­щью ко­ман­ды chmod. Те­перь по­лу­чив­ший­ся файл мож­но за­пустить в эму­ля­то­ре тер­ми­на­ла Android. Вы уви­ди­те стро­ку «Hello World!» (или что вы там на­пи­са­ли). Ес­ли вас пу­га­ет оби­лие встро­ен­но­го ас­семб­ле­ра в при­ве­ден­ном при­ме­ре, не пе­ре­жи­вай­те. Мы от­ка­жем­ся от ис­поль­зо­вания ас­семб­ле­ра, как толь­ко нау­чим­ся под­клю­чать биб­лио­те­ки Android к на­шей про­грам­ме.

Хо­тя рас­смот­рен­ный при­мер не де­мон­ст­ри­ру­ет ника­ких осо­бых воз­мож­но­стей Android, оз­на­ко­мить­ся с ним по­лез­но, осо­бен­но для тех, кто рань­ше не стал­ки­вал­ся с ана­то­ми­ей про­грамм, пред­на­зна­чен­ных для Linux. Де­ло в том, что когда мы про­грам­ми­ру­ем с ис­поль­зо­ванием glibc, мы мо­жем не за­бо­тить­ся о том, ка­кая функ­ция яв­ля­ет­ся точ­кой вхо­да в про­грам­му и ка­кой сис­тем­ный вы­зов необ­хо­дим для кор­рект­но­го за­вер­шения ее ра­бо­ты. Про­грам­ми­сты, пи­шу­щие для обыч­ных ва­ри­ан­тов Linux, мо­гут и во­все это­го не знать. При на­пи­сании же про­грамм Linux для Android знать эти ве­щи необ­хо­ди­мо, по­сколь­ку по умол­чанию сис­те­ма сбор­ки ко­да ARM для Android ори­ен­ти­ро­ва­на на сбор­ку раз­де­ляе­мых биб­лио­тек, а не ис­полняемых фай­лов. Так что, на­при­мер, биб­лио­те­ка Bionic не пре­достав­ля­ет нам функ­цию _start(), и нам при­дет­ся са­мим реа­ли­зо­вы­вать ана­лог этой функ­ции.

В прин­ци­пе го­во­ря, да­же про­стая воз­мож­ность досту­па к сис­тем­ным вы­зо­вам Linux по­зво­ля­ет про­грам­ме сде­лать не так уж и ма­ло. Но пол­но­цен­ное про­грам­ми­ро­вание на язы­ке C для Android воз­мож­но толь­ко в том слу­чае, ес­ли мы мо­жем за­дей­ст­во­вать в сво­ей про­грам­ме «фир­мен­ные» биб­лио­те­ки Android. А вот тут все ста­но­вит­ся го­раз­до сложнее.

Сис­тем­ные вы­зо­вы ARM Linux

Ме­ра и по­ря­док пе­ре­да­чи ар­гу­мен­тов сис­тем­ных вы­зо­вов ARM Linux со­от­вет­ст­ву­ют та­ко­вым на плат­фор­ме Intel, так что для опи­сания кон­крет­но­го вы­зо­ва мож­но ис­поль­зо­вать до­ку­мен­та­цию по вы­зо­вам для Intel.

От­но­си­тель­но спе­ци­фи­ки ARM пре­ж­де все­го нуж­но от­ме­тить, что су­ще­ст­ву­ет два ре­жи­ма сис­тем­ных вы­зо­вов – ста­рый и но­вый (но­вый яв­ля­ет­ся ча­стью стан­дар­та EABI). Нынешние яд­ра ARM Linux под­дер­жи­ва­ют оба ре­жи­ма (и в при­ве­ден­ном вы­ше при­ме­ре мы ис­поль­зо­ва­ли ста­рый ва­ри­ант), но ис­поль­зо­вать ре­жим EABI, ра­зу­ме­ет­ся, пред­поч­ти­тельнее. У двух ре­жи­мов мно­го об­ще­го: ар­гу­мен­ты вы­зо­ва пе­ре­да­ют­ся в ре­ги­ст­рах r0–r6, а сам вы­зов вы­пол­ня­ет­ся с по­мо­щью ин­ст­рук­ции SWI. Ог­раничение в 7 ре­ги­ст­ров унас­ле­до­ва­но от Linux x86; на RISC-плат­фор­ме ARM мож­но бы­ло бы ис­поль­зо­вать боль­ше ре­ги­ст­ров.

При ста­ром ре­жи­ме вы­зо­ва но­мер вы­зо­ва яв­ля­ет­ся ча­стью опе­ран­да ин­ст­рук­ции SWI. Вот как вы­гля­дит вы­зов write() (0 × 04):

MOV r0, де­ск­рип­тор_фай­ла;

MOV r1, ад­рес_бу­фе­ра;

MOV r2, дли­на_бу­фе­ра;

SWI 0x900004;

В ре­жи­ме EABI но­мер вы­зо­ва пе­ре­да­ет­ся в ре­ги­ст­ре r7, а опе­ран­дом SWI всегда яв­ля­ет­ся зна­чение 0 × 0. Еще од­но от­ли­чие свя­за­но с пе­ре­да­чей 64-бит­ных ар­гу­мен­тов. В обо­их ре­жи­мах их пе­ре­да­ют в па­ре 32-бит­ных ре­ги­ст­ров, но в ста­ром ре­жи­ме это про­сто два сле­дую­щих неза­ня­тых ре­ги­ст­ра, а в ре­жи­ме EABI – сле­дую­щая па­ра ре­ги­ст­ров, в ко­то­рой пер­вый ре­гистр име­ет чет­ный но­мер. Тот же вы­зов _write() в ре­жи­ме EABI бу­дет вы­гля­деть так:

MOV r0, де­ск­рип­тор_фай­ла;

MOV r1, ад­рес_бу­фе­ра;

MOV r2, дли­на_бу­фе­ра;

MOV r7, #4;

SWI 0x0;

По­ми­мо про­че­го, ме­ж­ду дву­мя ре­жи­ма­ми су­ще­ст­ву­ет од­но, не сра­зу за­мет­ное, но важ­ное раз­ли­чие: ре­жим EABI мо­жет ис­поль­зо­вать­ся как в ре­жи­ме ARM MODE (32-бит­ные ин­ст­рук­ции), так и в бо­лее ком­пакт­ном THUMB MODE. Ста­рый ре­жим не мо­жет ис­поль­зо­вать­ся в THUMB MODE про­сто по­то­му, что чис­ло 0 × 900000 не по­мес­тит­ся в 16-бит­ном опе­ран­де ин­ст­рук­ции SWI ре­жи­ма THUMB.

Пре­одо­ление пре­пят­ст­вий

По­сле то­го как про­грам­мист со­брал про­грам­му, у него возникает вполне ес­те­ст­вен­ное же­лание уста­но­вить эту про­грам­му на це­ле­вом уст­рой­ст­ве и за­пустить. С уст­рой­ст­ва­ми, ра­бо­таю­щи­ми под управ­лением Android, этот про­цесс мо­жет на­толк­нуть­ся на неко­то­рые слож­но­сти. А мо­жет и не на­толк­нуть­ся. Все за­ви­сит от уст­рой­ст­ва – точнее, от то­го, ка­кая имен­но вер­сия Android на нем ис­поль­зу­ет­ся и ка­кие до­полнения внес в нее про­из­во­ди­тель.

В прин­ци­пе, все, что нам нуж­но сде­лать – это ско­пи­ро­вать файл про­грам­мы в уст­рой­ст­во хранения дан­ных Android (же­ла­тель­но, в раз­дел, от­фор­ма­ти­ро­ван­ный в фай­ло­вой сис­те­ме Ext3) и при­дать фай­лу ат­ри­бу­ты ис­полняемо­го с по­мо­щью ко­ман­ды chmod. Са­мый про­стой спо­соб сде­лать это – восполь­зо­вать­ся ути­ли­той adb (Android Debug Dridge), ко­то­рая вхо­дит в со­став Android SDK. Что­бы восполь­зо­вать­ся ко­ман­да­ми ути­ли­ты adb, необ­хо­ди­мо под­клю­чить уст­рой­ст­во Android к ПК че­рез USB-порт, по ко­то­ро­му уст­рой­ст­во Android под­клю­ча­ет­ся как внешнее уст­рой­ст­во (а не как хост-уст­рой­ст­во). Обыч­но этот разъ­ем вы­полнен в форм-фак­то­ре mini-USB (mini-A или mini-B; об­за­ве­ди­тесь со­от­вет­ст­вую­щим пе­ре­ходником). На са­мом уст­рой­ст­ве Android необ­хо­ди­мо вклю­чить ре­жим от­лад­ки (он вклю­ча­ет­ся в раз­де­ле на­стро­ек уст­рой­ст­ва, в груп­пе «При­ло­жения»).

Ес­ли под­клю­чение вы­полнено пра­виль­но, на панели со­стояния Android поя­вит­ся зна­чок с жуч­ком и надпись «От­лад­ка по USB раз­ре­ше­на».

Те­перь мож­но по­про­бо­вать досту­чать­ся до уст­рой­ст­ва Android с по­мо­щью ути­ли­ты adb. Ко­ман­да

./adb devices

долж­на по­ка­зать спи­сок под­клю­чен­ных уст­ройств. Ес­ли эта ко­ман­да со сво­ей за­да­чей не спра­ви­лась, зна­чит, ли­бо что-то не так с под­клю­чением, ли­бо уст­рой­ст­во «не хо­чет» взаи­мо­дей­ст­во­вать с от­ла­доч­ной сис­те­мой. Ес­ли уст­рой­ст­во ото­бра­зи­лось в спи­ске, мож­но по­про­бо­вать дру­гие ко­ман­ды. Ко­ман­да

./adb push

ско­пи­ру­ет файл про­грам­мы с ПК на уст­рой­ст­во Android, а ко­ман­да

./adb shell

(thumbnail)
Программа-менеджер Android SDK.

пре­доста­вит вам при­ми­тив­ную обо­лоч­ку команд­ной стро­ки на уст­рой­ст­ве, с по­мо­щью ко­то­рой вы мо­же­те вы­звать ути­ли­ту chmod для из­менения ат­ри­бу­тов фай­ла. Ес­ли обе ко­ман­ды вы­полнились успеш­но – по­здравь­те се­бя: ва­ше уст­рой­ст­во иде­аль­но под­хо­дит для изу­чения ОС Android. В этом слу­чае вы да­же мо­же­те вы­полнить файл про­грам­мы непо­сред­ст­вен­но из обо­лоч­ки adb. Но увы, из тех Android-уст­ройств, что по­па­да­ли в мои ру­ки, толь­ко нетбук Toshiba AC-100 ока­зал­ся на­столь­ко «сго­вор­чи­вым». В дру­гих слу­ча­ях уст­рой­ст­ва ли­бо во­об­ще от­ка­зы­ва­лись вы­пол­нять ко­ман­ды push и shell, ли­бо не по­зво­ля­ли вы­полнить ко­ман­ду chmod, без ко­то­рой осталь­ные дей­ст­вия не име­ют смыс­ла.

Впро­чем, от­чаи­вать­ся ра­но. Есть мно­го спо­со­бов ско­пи­ро­вать файл в хранили­ще дан­ных Android. Мож­но под­клю­чить уст­рой­ст­во к ПК как обыч­ный внешний диск, мож­но да­же уста­но­вить на уст­рой­ст­во Android сер­вер FTP. На уст­рой­ст­во Android нуж­но уста­но­вить также про­грам­му-эму­ля­тор тер­ми­на­ла. За­пустив та­кую про­грам­му, вы по­па­де­те в ту же са­мую обо­лоч­ку команд­ной стро­ки, ко­то­рую пре­достав­ля­ет adb shell. По­про­буй­те вы­звать ко­ман­ду chmod из эму­ля­то­ра тер­ми­на­ла. Ес­ли и это не уда­лось, зна­чит, про­из­во­ди­тель ва­ше­го уст­рой­ст­ва на­деж­но за­бло­ки­ро­вал воз­мож­ность за­пуска на нем нестан­дарт­ных про­грамм. По­мочь мо­жет по­лу­чение прав ад­минист­ра­то­ра на уст­рой­ст­ве («ру­то­вание»), ко­то­рое ка­ж­дый вы­пол­ня­ет на свой страх и риск (как минимум, эта опе­ра­ция при­во­дит к по­те­ре га­ран­тии на де­вайс, но мож­но по­те­рять и боль­ше), или сме­на про­шив­ки – еще бо­лее рис­ко­ван­ное де­ло. Дру­гой ва­ри­ант – ис­поль­зо­вать для тес­ти­ро­вания нестан­дарт­ных про­грамм про­грамм­ный эму­ля­тор уст­рой­ст­ва Android, ко­то­рый так­же вхо­дит в со­став SDK.

По­сле то­го как вир­ту­аль­ное уст­рой­ст­во соз­да­но и за­пу­ще­но, вы мо­же­те под­клю­чить­ся к нему с по­мо­щью все той же про­грам­мы adb; при этом adb shell сра­зу пре­доста­вит вам пол­но­мо­чия ад­минист­ра­то­ра.

Ин­ст­ру­мен­та­рий раз­ра­бот­чи­ка Android

Для соз­дания про­грамм, ском­пи­ли­ро­ван­ных в ма­шин­ные ко­ды, нам по­на­до­бит­ся Android Software Development Kit (да­лее – SDK) и Android Native Development Kit (да­лее – NDK). Тра­ди­ци­он­но SDK ис­поль­зу­ет­ся для на­пи­сания про­грамм Android, пред­на­зна­чен­ных для вы­полнения на вир­ту­аль­ной ма­шине (чем мы в этой се­рии занимать­ся не бу­дем).

(thumbnail)
Создание виртуальной машины для запуска Android.

NDK пред­на­зна­чен для соз­дания раз­де­ляе­мых биб­лио­тек в ко­дах мик­ро­про­цес­со­ра, ко­то­рые под­клю­ча­ют­ся к при­ло­жениям Android с по­мо­щью ме­ханиз­ма JNI. Эти раз­де­ляе­мые биб­лио­те­ки соз­да­ют­ся для рас­ши­рения доступ­ной при­ло­жениям Android функ­цио­наль­но­сти и для по­вы­шения про­из­во­ди­тель­но­сти в тех слу­ча­ях, когда про­из­во­ди­тель­но­сти вир­ту­аль­ной ма­ши­ны не хва­та­ет.

Про­цесс соз­дания раз­де­ляе­мых биб­лио­тек для Android опи­сан в до­ку­мен­та­ции к NDK, и занимать­ся им мы то­же не бу­дем. Вме­сто это­го мы, при­менив немно­го изо­бре­та­тель­но­сти, за­ста­вим NDK ком­пи­ли­ро­вать и со­би­рать ис­пол­няе­мые фай­лы. Из SDK нам по­на­до­бят­ся толь­ко от­дель­ные ин­ст­ру­мен­ты (на­при­мер, уже упо­мя­ну­тая ути­ли­та adb).

Те­перь, так или ина­че, мы го­то­вы при­сту­пить к на­пи­санию про­грамм, ко­то­рые смо­гут раз­го­ва­ри­вать с ОС Android на ее язы­ке и вве­дут в сту­пор тех поль­зо­ва­те­лей, ко­то­рые при­вык­ли, что при­ло­жения для Android – это фай­лы с рас­ши­рением apk.

В сле­дую­щей час­ти: зна­ко­мим­ся со спе­ци­фи­че­­ски­­ми воз­мож­но­стя­ми яд­ра и биб­лио­те­ки C для ОС Android.

О тер­ми­но­ло­гии

Ес­ли сле­до­вать тер­ми­но­ло­гии, при­ня­той в Java (Java Native Interface и т. д.), тер­ми­ном Native сле­ду­ет обо­зна­чать код, вы­пол­няю­щий­ся непо­сред­ст­вен­но на про­цес­со­ре, ми­нуя вир­ту­аль­ную ма­ши­ну. Но, по­сколь­ку мне уже встре­ча­лось при­менение тер­ми­на Native имен­но к про­грам­мам, на­пи­сан­ным для вир­ту­аль­ной ма­ши­ны Android (или про­грамм, ко­то­рые час­тич­но вы­пол­ня­ют­ся в вир­ту­аль­ной ма­шине, а час­тич­но – на же­ле­зе), для сво­их про­грамм, ко­то­рые це­ли­ком вы­пол­ня­ют­ся на же­ле­зе и не ну­ж­да­ют­ся в вир­ту­аль­ной ма­шине, я бу­ду ис­поль­зо­вать тер­мин «про­грам­мы Linux». Этот тер­мин оп­рав­дан по сле­дую­щим при­чи­нам: в ре­зуль­та­те сбор­ки та­ких про­грамм по­лу­ча­ют­ся фай­лы в фор­ма­те ELF; неко­то­рые из этих про­грамм об­ла­да­ют дво­ич­ной со­вмес­ти­мо­стью с дру­ги­ми сис­те­ма­ми ARM Linux; дру­гие про­грам­мы мо­гут быть пор­ти­ро­ва­ны на дру­гие Unix-сис­те­мы с минималь­ны­ми из­менения­ми или во­все без из­менений. По­следнее за­ме­чание не от­но­сит­ся, ра­зу­ме­ет­ся, к тем про­грам­мам, ко­то­рые ис­поль­зу­ют спе­ци­фи­че­­ские воз­мож­но­сти яд­ра Android.

Персональные инструменты
купить
подписаться
Яндекс.Метрика