-
Notifications
You must be signed in to change notification settings - Fork 0
/
demon_analyysi.tex
75 lines (50 loc) · 14.7 KB
/
demon_analyysi.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
\chapter{Demoversion toiminta ja analyysi}
\label{ch:demo-analysis}
Ennen tämän työn aloitusta diplomityön tekijä oli kehittänyt ohjelmiston, joka kykeni viestien tilaukseen ja tallentamiseen. Ohjelma oli tarkoitettu demoksi tai prototyypiksi ennen varsinaista toteutusta. Demon tarkoituksena oli opettaa tekijälle IEC 61850 -standardia, todentaa viestien tilaamisen mahdollisuus ja tuoda esille toteutukseen liittyviä ongelmia. Ohjelma kykeni tilaamaan viestejä yhden IED-laitteen kaikilta RCB-instansseilta, prosessoimaan viestit ja tallentamaan ne relaatiotietokantaan.
Demoa ei ollut mahdollista käyttää tuotannossa osana muuta järjestelmää sen arkkitehtuurin ja toiminnan epävarmuuden takia. Tässä kappaleessa käydään läpi demon toimintaa ja siihen liittyviä ongelmia. Demosta on tarkoitus analysoida sen toiminnan epävarmuutta ja saada selville mistä se mahdollisesti johtui. Saatuja tietoja käytetään luvussa \ref{ch:suunnittelu} apuna uuden version suunnittelussa.
\section{Arkkitehtuuri}
\label{ch:demoversio-ja-sen-toiminta}
Demoversio oli ohjelmoitu Ruby-ohjelmointikielellä. Kuvassa \ref{fig:demo-architecture} on esitetty demoversion arkkitehtuuri korkealla tasolla ja kuinka viesti IED-laitteelta siirtyy tietokantaan. Yksi ajettu demoversion prosessi pystyi tilaamaan yhden IED-laitteen kaikki RCB-luok\-ki\-en instanssit. Instanssien tiedot luettiin relaatiotietokannasta. Ohjelmisto prosessoi viestit ja tallensi ne relaatiotietokantaan myöhempää käyttöä varten. Ruby-ohjelmistossa tärkeässä osassa oli \emph{libIEC61850}-kirjasto \cite{libIEC61850-homepage}. libIEC61850-kirjasto on avoimen lähdekoodin C-kielellä toteutettu kirjasto, joka abstrahoi IEC 61850 -standardin matalan tason määrittämiä palvelukutsuja ja datarakenteita helppokäyttöiseksi rajapinnaksi. Kirjasto tarjosi toiminnallisuuden IED-laitteella olevan palvelinohjelmiston sekä IED-laitetta käyttävän asiakasohjelmiston toteuttamiseen. IED-laitteen palvelimelle kirjasto tarjosi funktioita ja rakenteita standardin määrittämien luokkien hierarkian rakentamiseen ja käsittelyyn. IED-laitteen asiakasohjelmalle kirjasto tarjosi funktioita ja rakenteita standardin määrittämiin palveluihin, kuten arvojen lukuun ja asettamiseen, datajoukkojen käyttöön ja viestien tilaamiseen. Demoa toteutettiin käyttäen oikeaa IED-laitetta, joten kirjastosta tarvittiin vain sen asiakasohjelman ominaisuudet. Demon toteutuksen aikana kirjasto todettiin hyväksi vaihtoehdoksi ja sitä päätettiin käyttää myös uuden version toteutuksessa.
\begin{figure}[ht!]
\includegraphics[width=0.8\textwidth]{pictures/demo-architecture.png}
\caption{Ruby:lla toteutetun demoversion arkkitehtuuri ja tiedonsiirto.}
\label{fig:demo-architecture}
\end{figure}
LibIEC61850-kirjasto on rakennettu käyttämään MMS-protokollaa tiedonsiirrossa IED-laitteen ja sen asiakasohjelman välillä IEC 61850 -standardin mukaan. Kuvassa \ref{fig:libiec61850-layer-architecture} on esitetty kirjaston kerrosarkkitehtuuri asiakasohjelmalle. Kirjastoon on toteutettu \emph{laiteabstraktiokerros} (\emph{Hardware Abstraction Layer}, \emph{HAL}). HAL:in avulla kirjasto voi toimia monella eri laitealustalla, ja käyttäjä voi tarvittaessa lisätä oman HAL-implementaation. Demoversiota suoritettiin Linux-käyttöjärjestelmällä, joten kirjastosta käytettiin olemassa olevaa Linux HAL -toteutusta. Kuvassa \ref{fig:libiec61850-layer-architecture} on punaisella merkitty laatikot, jotka kirjaston käyttäjä voi tarjota itse, keltaisella kirjaston uudelleenkäytettävät MMS-protokollan osuudet ja sinisellä IEC 61850 -standardin toteuttavat osuudet. Kuvaan on merkitty vihreällä demoon toteutetut osuudet, eli Ruby-kielelle liitos C-kieleen ja tämän päälle Ruby:lla toteutettu ohjelmisto.
\begin{figure}[ht!]
\includegraphics[width=1\textwidth]{pictures/libiec61850-layer-architecture.png}
\caption{LibIEC61850-kirjaston kerrosarkkitehtuurin komponentit, vihreällä Ruby toteutukseen lisätyt osat (pohjautuu kuvaan \mbox{\cite{libIEC61850-api-overview}}).}
\label{fig:libiec61850-layer-architecture}
\end{figure}
Ruby-koodista C-kielen funktioiden kutsuminen ei ole suoraan mahdollista, vaan kielten väliin täytyy toteuttaa liitos. Demoversiossa liitos oli tehty käyttäen Ruby:lle saatavaa \emph{ruby-ffi}-kirjastoa \cite{ruby-ffi-repo} (\emph{Foreign Function Interface}, \emph{FFI}). Liitoksen avulla Ruby voi kutsua C-kielen funktioita ja käyttää sen struktuureita ja muuttujia. Demossa kirjasto huolehti matalan tason IEC 61850 asiat, ja Ruby-osuus keskittyi liitoksen avulla viestien tilaamiseen, käsittelyyn ja tallentamiseen.
\section{Toiminta}
\label{ch:ongelmakohdat-ja-analysointi}
Ohjelman toiminta on esitetty sekvenssikaavioissa \ref{fig:sequence-diagram-report-subscription} ja \ref{fig:sequence-diagram-report-subscription-processing}. Sekvenssikaavio \ref{fig:sequence-diagram-report-subscription} jatkuu kuvassa \ref{fig:sequence-diagram-report-subscription-processing}. Kuvissa ohjelman kaksi eri silmukkaa on esitetty loop-laatikoilla. Sekvenssikaaviossa osallisena ovat tietokanta, Ruby-ohjelma, libIEC61850-kirjasto, libIEC61850-kirjaston natiivisäie ja IED-laitteen palvelinohjelma. Kirjaston natiivisäie on vastuussa yhteyden ylläpidosta ja datan siirtämisestä IED-laitteen ja kirjastoa käyttävän prosessin välillä. Sekvenssikaavioon on merkitty paksulla suorituksessa olevat palkit, esimerkiksi IED-laitteen palvelinohjelmisto on koko ajan suorituksessa. Tulevassa tekstissä ohjelman toiminta käydään pääpiirteittäin läpi ja kuvien numeroihin viitataan.
IED-laiteella viestejä mahdollisesti generoidaan ja lähetetään samaan aikaan. Seurauksena niitä voi saapua libIEC61850-kirjastolle ennen kuin edellinen viesti on käsitelty. Tätä varten kirjasto toteuttaa sisäisen puskurin viestien vastaanottamiseen. Puskurista prosessoidaan seuraava viesti, kun edellinen on prosessoitu. Toisin sanoen kirjasto ottaa viestejä vastaan sitä mukaa kun ne saapuvat ja ottaa niitä puskurista yksi kerrallaan saapumisjärjestyksessä. Kirjasto varaa yhden puskurin avattua yhteyttä kohti. Jos ohjelma avaa vain yhden yhteyden, kirjaston käyttäjän ei tarvitse huolehtia rinnakkaisuudesta. \cite{libIEC61850-repo}
\begin{figure}
\includegraphics[width=1\textwidth]{pictures/sequence-diagram-report-subscription.png}
\caption{Sekvenssikaavio kuinka Ruby-ohjelma avaa yhteydet ja tilaa kaikki IED-laitteen RCB-instanssit (jatkuu kuvassa \ref{fig:sequence-diagram-report-subscription-processing}).}
\label{fig:sequence-diagram-report-subscription}
\end{figure}
\begin{figure}
\includegraphics[width=1\textwidth]{pictures/sequence-diagram-report-subscription_001.png}
\caption{Sekvenssikaavio kuinka Ruby-ohjelma prosessoi ja tallentaa viestejä libIEC61850-kirjastoa käyttäen (jatkoa kuvalle \ref{fig:sequence-diagram-report-subscription}).}
\label{fig:sequence-diagram-report-subscription-processing}
\end{figure}
Ensimmäisenä ohjelma luki tietokannasta IED-laitteen, sekä sen kaikki RCB-instanssien tiedot. IED-laitteen tietojen avulla ohjelma muodosti yhteyden IED-laitteelle (kohdat 3--11). Tässä vaiheessa libIEC61850-kirjasto käynnistää erillisen natiivisäikeen yhteyden viestien vastaanottoon. Tätä säiettä kirjasto käyttää tulevien viestien vastaanottoon ja lähettämiseen. Yhteyden muodostuksen jälkeen ohjelma käy läpi silmukassa jokaisen IED-laitteen RCB-instanssin ja tilaa ne. RCB-instanssien tilaus tapahtuu kohdissa 12--26. Osa varausprosessia on instanssin arvojen luku ja takaisinkutsufunktion asettaminen, jota kirjasto kutsuu viestin saapuessa. \mbox{\cite{libIEC61850-repo}}
RCB-instanssin arvojen kirjoituksen jälkeen tilaus alkaa ja IED-laite aloittaa viestien lähettämisen (kohta 23). Seurauksena on, että ohjelma joutuu käsittelemään viestejä samalla kun muita RCB-instansseja varataan. Toisin sanoen asetettu takaisinkutsufunktio suoritetaan (kohdat 28--36). Kirjasto käytti samaa lukitusta takaisinkutsufunktion asettamisen ja sen suorituksen ajaksi. Nämä tapahtuvat kohdissa 19--20 ja 33--36. Tilauksen jälkeen ohjelma jää odottamaan viestejä vastaan ja asetettu takaisinkutsufunktio prosessoi viestit ja tallentaa ne tietokantaan. \mbox{\cite{libIEC61850-repo}}
\section{Ongelmien analyysi}
Demo oli toteutettu käyttäen \emph{Ruby on Rails} -kehystä, lyhennetään \emph{RoR}. RoR on tarkoitettu web-sovellusten kehittämiseen Ruby-kielellä. Demo toteutettiin suoritettavaksi RoR-kehyksen tarjoamalla erillisen Ruby-koodin suorituksen mekanismilla \cite{rails-runner}. Mekanismi sallii Ruby-koodin suorituksen RoR:in kontekstissa. Konteksti salli järjestelmän kirjastojen ja koodien käytön demossa. Huonona puolena tässä oli, että yksinkertaisen ohjelman suoritus vaatii järjestelmän kontekstin muistiin lataamisen ennen suoritusta. Tästä seurauksena yksi suoritettu demon prosessi varasi muistia noin 150 Mt, verrattuna uuteen RoR-projektiin, joka vei noin 67 Mt muistia. Ohjelman yksinkertaisuuteen nähden varatun muisti määrä on suuri ja sitä olisi mahdollista pienentää.
Demossa isoimpana ongelmana oli sen huono suorituskyky ja toiminnan epävarmuus RCB-instanssien määrän ollessa esimerkiksi noin 10. RCB-instanssien määrän ollessa liian suuri ohjelma saattoi epäonnistua osan tilaamisessa, koska yhteys aikakatkaistiin arvojen kirjoituksessa. Lisäksi ongelmaksi muodostui usean RCB instanssin tilaamisen kulunut aika. Yhteensä aikaa saattoi kulua noin 30 sekuntia 10 instanssin tilaamiseen. RCB-instanssien määrä vaihtelee IED-laitteen mukaan. Tässä työssä käsitellyt määrät olivat 3--13 instanssia.
Huonoon suorituskykyyn oli pääasiassa syynä Ruby-oletustulkissa (versiosta 1.9 eteenpäin \emph{Yet another Ruby VM}, \emph{YARV}) oleva \emph{globaali tulkkilukitus} (\emph{Global Interpreter Lock}, \emph{GIL}, tai \emph{Global Virtual Machine Lock}, \emph{GVL}). GIL pakottaa Ruby-ohjelman ajoon vain yhdellä ytimellä ja vain yksi säie vuorossa kerrallaan ja on täysin riippumaton käyttöjärjestelmän vuorottajasta \mbox{\cite[s.~131--133]{Odaira2014}}. Tilanteessa, jossa Ruby-koodi on tilaamassa RCB-instanssia ja samalla hetkellä IED-laite lähettää viestin. LibIEC61850-kirjasto kutsuu Ruby:n puolella olevaa takaisinkutsufunktiota ja sen suorituksen ajaksi GIL lukitaan. Toisin sanoen RCB-instanssia tilaava Ruby-koodin suoritus pysähtyy takaisinkutsufunktion ajaksi. Seurauksena on loppujen RCB-instanssien hidas tilaaminen ja yhteyden aikakatkaisut, jos vaihto tapahtuu oikeaan aikaan.
Kuvassa \ref{fig:ruby-gil} on esitetty kuinka Ruby-tulkki vuorottaa kahta ajossa olevaa säiettä. Kuvassa demon Ruby-koodi kutsuu \texttt{Ied""Connection""\_set""RCB""Values()} funktiota, ajo jää kesken ja tapahtuu vaihto, koska viesti saapui. Takaisinkutsufunktio suoritetaan ja suoritus palaa takaisin aikaisempaan funktion suoritukseen. Tässä vaiheessa, jos vaihto on huonolla hetkellä ja kesti liian kauan, tulee yhteyden aikakatkaisu ja RCB-instanssi jää tilaamatta.
Huonoon suorituskykyyn mahdollisesti vaikutti myös lukitus \texttt{report""Handler""Mutex}, jota kirjastossa käytetään, kun takaisinkutsufunktio asetetaan tai sitä suoritetaan. Lukitus aiheuttaa säikeen nukkumisen niin kauan kunnes lukitus vapautuu. Tässä tapauksessa, jos viestin prosessointi kestää liian kauan (kuvassa \ref{fig:sequence-diagram-report-subscription-processing} kohdat 33--36) ja samalla tilataan muita RCB-instansseja (kuvassa \ref{fig:sequence-diagram-report-subscription} kohdat 12--26). Säie joutuu odottamaan lukituksen vapautusta takaisinkutsufunktion asettamisen ajan (kohdat 19--20). Ratkaisuna tähän olisi pitää takaisinkutsufunktio mahdollisimman lyhyenä suoritusajan suhteen. Ruby-kieli on tulkattava kieli verrattuna libIEC61850-kirjaston käännettyyn C-kieleen. Tulkattavan kielen suorituskyky on huonompi kuin valmiiksi konekäskyiksi käännetyn kielen. Edellä mainittujen lisäksi tämä saattoi myös vaikuttaa ohjelman suoritukseen. \mbox{\cite{Kozlovski2017, Storimer2013}}
\begin{figure}[ht!]
\includegraphics[width=1\textwidth]{pictures/ruby-gil.png}
\caption{Ruby-tulkin globaalin lukituksen toiminta, joka vuorottaa ajossa olevia säikeitä riippumatta käyttöjärjestelmän vuorottajasta.}
\label{fig:ruby-gil}
\end{figure}
Demototeutuksessa oli muistivuoto huonon ohjelmoinnin tuloksena. Muistivuoto on tilanne missä ohjelma varaa lisää muistia ilman, että sitä vapautetaan takaisin käyttöjärjestelmälle. Muistivuoto johtui todennäköisesti ohjelmointivirheestä ruby-ffi -kirjastolla. Kun liitos Ruby:sta tehdään C-kieleen, täytyy ohjelmoijan miettiä varatun muistin vapauttamista. Ruby-ohjelmoijan ei normaalisti tarvitse tästä huolehtia automaattisen roskien keruun ansiosta. Muistivuoto havaittiin, kun ohjelma jätettiin suoritukseen pitemmäksi aikaa ja se oli varannut melkein kaiken käyttöjärjestelmän muistista itselleen. Tämän pystyi havaitsemaan helposti ohjelman suorituksen aikana Linuxin htop-ohjelmalla MEM\%-sarakkeesta. Sarake kertoo prosessin käyttämän prosentuaalisen osuuden koko käyttöjärjestelmän muistista \cite{htop-user-guide}. Luku kasvoi tasaisesti ja sen verran nopeasti, että sen havaitseminen oli mahdollista ilman pitempää suoritusaikaa.
\section{Yhteenveto}
Demon jatkokehitys toimivaksi olisi vaatinut paljon vaivaa huomioon ottaen edellä käsitellyt ongelmat. Tämän lisäksi sen kehityksessä ei ollut huomioitu järjestelmän hajautuksen vaatimuksia. Tiedon jakaminen muun järjestelmän kanssa tietokannan kautta ei ole hyvä ratkaisu. Se johtaisi tilanteeseen missä järjestelmän komponentit lukisivat viestejä tietokannasta jatkuvasti, ilman tietoa milloin uusi viesti on saapunut. Tästä aiheutuu turhaa kuormaa tietokannalle ja komponentin saama tieto ei välttämättä ole ajan tasalla. Isoilla muutoksilla demo olisi ollut mahdollista saada täyttämään asetetut vaatimukset ja sen ongelmat korjattua. Demo kuitenkin päätettiin korvata kokonaan uudella toteutuksella sen vaatiman työmäärän takia.
Ohjelmiston huonoon suorituskykyyn ja epävarmuuteen pääasiassa on syynä Ruby-kielen GIL. Varatun muistin määrän oli suuri yksinkertaista ohjelman ajamista varten. Syynä todennäköisesti oli RoR-kehyksen ajoympäristö, joka latasi muistiin muuta järjestelmää ja sen kirjastoja. Ohjelmassa oli muistivuoto, joka todennäköisesti johtui Ruby:n ja C-kielen liitoksen huonosta ohjelmoinnista. Tässä luvussa saatuja tietoja tullaan käyttämään suunnittelussa luvussa \ref{ch:suunnittelu}. Demototeutuksen perusteella libIEC61850-kirjasto todettiin hyväksi ja sitä käytettiin myös uudessa versiossa. Suunnittelussa kysymyksenä jää miettiä mitkä tekniikat valitaan toteutukseen, jotta suorituskykyongelmat vältetään ja varatun muistin koko pidetään kohtuullisena. Muistivuoto ohjelmassa taas vältetään huolellisella ohjelmoinnilla.