Realtime Android: deterministisch gebruiksgemak

Reading time: 6 minutes

Author:

Pieter Edelman

Het is jammer dat Android niet over realtimecapaciteiten beschikt, want het gemak voor gebruiker en ontwikkelaar zou menig industriële applicatie ten goede komen. Onderzoekers van Siemens hebben onderzocht wat de mogelijkheden zijn en concludeerden dat een apart realtime gedeelte naast de Android-omgeving de beste aanpak is. In dit artikel doen zij de softwarebasis uit de doeken.

De gebruiksvriendelijkheid voor zowel de Android-gebruiker als -programmeur staat in schril contrast tot de soberheid van doorsnee embedded-Linux-oplossingen, de werkpaarden van industrieel Linux. Terwijl de leercurve van een Android-telefoon minimaal is, kunnen simpele machinetaken met een beroerde human-machine interface (HMI) onoverkomelijk lastig worden. De combinatie van het grafisch rijke Android met realtimecapaciteiten kan een remedie vormen voor zowel de gebruikers als programmeurs van embedded realtime systemen.

Door de wol geverfde programmeurs van desktoptoepassingen ervaren aanzienlijke obstakels wanneer ze hun aandacht verleggen naar embedded Linux, maar de ontwikkelomgeving van het Google-OS is voor iedereen toegankelijk in plaats van gericht op enkele specialisten. Dat is een belangrijk voordeel van realtime Android. Bovendien biedt een dergelijke combinatie mogelijkheden voor consolidatie van de hardware: multi- en manycore systemen maken het mogelijk om de traditioneel gescheiden taken van tijdskritieke besturing en de gebruikersinterface op een enkel hardwareplatform uit te voeren. We zijn daarom anderhalf jaar geleden met een project gestart om dit te realiseren, met een Xoom-tablet van Motorala als ontwikkelbord.

Figuur 1: De eenvoudigste manier om Android toe te voegen aan een realtime industrieel systeem, is door het naast een realtime laag te plaatsen en deze twee met elkaar te laten communiceren.

Belangrijke gebeurtenissen

Vanzelfsprekend is voor de kernel realtimeondersteuning een vereiste. Wij hebben hierbij ingezet op de preempt_rt-extensie, vooral omdat deze code in de toekomst waarschijnlijk volledig wordt opgenomen in de Linux-mainline. Preempt_rt is geen realtime kernel in de klassieke academische betekenis van de term, maar met statistische-verificatie- en testmechanismes is een bovengrens voor de latency te bepalen. De industrie heeft zich in sterke mate achter deze aanpak geschaard voor standaard embedded-Linux-toepassingen.

Aan de userland-kant van Android krijg je te maken met Dalvik, in essentie een virtuele machine voor Java maar dan met een registergebaseerde architectuur in plaats van een stackgebaseerde aanpak. Het Android-systeem is hier bijna geheel op geënt. Er zijn twee mogelijkheden om dit uit te breiden met realtimemogelijkheden. De eerste is om Dalvik te vervangen door een klassieke JVM met realtimecapaciteiten en de Android-omgeving waar nodig aan te passen. De tweede is om de technieken en design patterns van dergelijke JVM-implementaties te gebruiken om Dalvik zélf van realtimemogelijkheden te voorzien.

Onze inschatting na een codeanalyse was echter dat het benodigde werk bij beide aanpakken onze ontwikkelcapaciteit te boven zou gaan, maar ook dat het resultaat te mager zou zijn vergeleken met de moeite die we erin moesten steken. In plaats daarvan hebben we dus gekozen voor een derde oplossing waarbij we de Java- en realtime componenten onafhankelijk naast elkaar draaien (Figuur 1).

Deze aanpak dicteert dat de realtime onderdelen direct de hardware aanspreken en dus worden geschreven in C of een andere laagniveautaal die niet omgezet hoeft te worden door de virtuele machine. Om data uit te wisselen tussen het applicatiedomein in Java en het realtimedomein hebben we een transactioneel mechanisme ontworpen, waarbij de dataoverdracht ofwel volledig wordt uitgevoerd ofwel volledig wordt afgebroken. Deze aanpak voorkomt dat het dataverkeer ongelimiteerde latency‘s introduceert als het realtime gedeelte moet wachten tot het binnenhalen van data compleet is. Dankzij deze uitwisseling kan de HMI-laag bijvoorbeeld aangepaste parameters naar het regelalgoritme sturen, of kan de realtime laag de HMI inseinen over belangrijke gebeurtenissen.

Figuur 2: De totale latency bestaat uit een aantal componenten. De expiration delay is de tijd tussen het moment waarop de hardwaretimer hoorde te verlopen en het moment waarop dit daadwerkelijk gebeurde. De HRT handler delay is de tijd tussen het verlopen van de timer en de start van de hogeresolutietimer (HRT). De HRT duration is de tijd die nodig is om de HRT te verwerken. Na het verwerken van de HRT moet de controle worden doorgegeven aan de realtime toepassing in userland. De schedule in is de tijd tussen de beslissing van de kernel om de controle over de taak aan te vragen en de daadwerkelijke aanvraag. Om het verschil te meten tussen het verwachte timerverloop en de huidige tijd moet de toepassing via het Getclock-mechanisme achter de huidige tijd komen. Deze overhead hebben we Getlock genoemd.

Bijna automatisch

De standaard Linux-kernel ondersteunt realtime noch de benodigde extensies voor het Android-userland: beide aspecten zijn beschikbaar als patchreeksen voor de broncode van bepaalde kernelversies. Maar niet voor de versie die wij nodig hadden: het board support package van Motorola en Google voor de Xoom was gebaseerd op versie 2.6.36 van de Linux-kernel. Omdat zowel het BSP als preempt_rt lastig te porten bleken, hadden we de keuze uit twee opties: ofwel het BSP terugporten naar 2.6.33 en de Android-patches vooruitporten vanuit 2.6.32, ofwel het BSP vooruitporten naar 3.0, waar preempt_rt en Android beide al voor beschikbaar zijn.

Aan de hand van codeanalyse en experimenten besloten we dat de tweede optie de minste moeite zou kosten. Gedurende de acht maanden ontwikkeling tussen versie 2.6.33 en versie 2.6.36 is namelijk veel van de essentiële infrastructuur in de mainline opgenomen voor de het Tegra-platform, waar de Xoom op draait. Die wijzigingen zouden we terug moeten porten. Bovendien vormen de vele wijzigingen in elke Linux-versie in het algemeen een overtuigend argument om geen grote hoeveelheden code via meerdere versies terug te porten.

Het combineren van de preempt_rt- en Android-patches bleek vrij eenvoudig; de patchreeksen voor beide bleken elkaar nagenoeg niet te raken. De afgelopen maanden is die situatie zelfs nog verder verbeterd: de preempt_rt-patches zijn momenteel beschikbaar voor praktisch alle belangrijke kernelversies, en de Android-patches zijn al gedeeltelijk opgenomen in de mainline (hoewel sommige mechanismen hiervan zijn vervangen door functionele equivalenten die de kernelbeheerders als superieur beschouwen). Het integreren van beide sets is nu gereduceerd tot een bijna automatisch proces, en je hoeft geen visionair te zijn om te voorspellen dat dat in de toekomst nog makkelijker zal gaan. De overblijvende obstakels zijn voornamelijk terug te voeren op de BSP‘s, maar dat zijn problemen waar álle embedded-Linux-systemen mee kampen.

Figuur 3: Uit een reeks latencytests waarbij het systeem naast de realtime taak een substantiële niet-realtime belasting te verduren kreeg, blijkt dat de latency‘s niet buiten de grenzen komen. Vertragingen bij de tien langste totale latency‘s (met lijnen aangegeven) bleken te worden veroorzaakt in alle categorieën. De individuele events zijn weergegeven met grijze stippen.

Het viel ons ook op dat niks in de Android-stack een andere realtime-implementatie in de weg staat. I-Pipe/Xenomai of een van de meer specialistische implementaties vormen net zo‘n goede basis. In de transactionele data-uitwisseling tussen de realtime en niet-realtime delen zouden er wel details aangepast moeten worden, maar het basisconcept zou hetzelfde blijven.

Alle categorieën

Aan de hand van kunstmatige tests hebben we de gebruikelijke metingen uitgevoerd om er zeker van te zijn dat onze implementatie geen codepaden bevat die tot onacceptabele latency‘s leiden. Om inzicht te verkrijgen in het aandeel van de verschillende kernel-acties in de algemene latency hebben we het FTrace-raamwerk toegevoegd. De meting is gebaseerd op een userland-toepassing met realtimeprioriteit die de timers registreert. De latency meten we als het verschil tussen het moment dat een timer hoort te verlopen en het moment dat het testprogramma hierop reageert. De totale latency bestaat uit een aantal onderdelen (zie Figuur 2). Om aan het realtimepredicaat te voldoen, moet deze totale tijd begrensd zijn.

In Figuur 3 is per onderdeel de latency weergegeven. Als we kijken naar de tien grootste totale latency‘s, zien we dat de oorzaak van deze lange tijden ligt in uitschieters die in alle categorieën kunnen voorkomen. Daaruit kunnen we opmaken dat lange latency‘s alleen worden veroorzaakt door algemene systeemeffecten en niet door asynchrone events waarvoor nog veranderingen in de code nodig zijn. Ondertussen hebben we de architectuur ook geport naar enkele andere hardwareplatforms, waaronder enkele HMI-panels die daadwerkelijk in industriële controle worden gebruikt. De resultaten zijn vergelijkbaar.