субота, 28. новембар 2009.

elif евалуација

Интересантна промена се десила у gcc Ц препроцесору. Евалуација elif гране препроцесорске директиве "if" се извршава чак и ако је услов дат у if задовољен.

Конкретан пример:

#ifdef
#ifndef FOO_BAR
#define FOO_BAR
#elif FOO_BAR == 0
#endif

Ово ради без проблема на претходним верзијама, на пример 4.3.3 али не и на 4.4.1. Не знам у којој тачно верзији је дошло до промене, али ево извештаја проблема: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36320

Исправна конструкција горе наведеног примера би била:


#ifdef
#ifndef FOO_BAR
#define FOO_BAR 1
#elif FOO_BAR == 0
#endif

петак, 27. новембар 2009.

Секвентна тачка

Односно "Sequence point".

Питање у вези секвентне тачке (ако сам превео добро) често долази на разговорима за посао. Многи нису свесни шта је то иако је у суштини проста ствар. Пре неки дан ме на то потсетио бивши колега, а и ја сам то питање добио на мом разговору.

Секвентна тачка је синтаксни елемент дефинисан језиком који гарантује да ће споредни ефекти евалуације израза са леве стране таквог елемента гарантовано бити завршени пре него што започне евалуација израза са десне стране.

На пример, у Цеу и Це++ то је оператор зарез ',' али не и аримтетички оператори. Осим зареза, има још секвентних тачака као што су '?' и логички оператори.

Кратак, али добар текст се може наћи на википедији.

уторак, 27. октобар 2009.

Решавање судоку

Прошле године су нам у посети били моји родитељи и при том сам сазнао да је мој отац постао пасионирани играч судоку. Мало сам играо и ја, врло интересантна мозгалица. Природно, дошао сам на идеју да решавање "аутоматизујем". У то време сам учио "Scheme" језик - шема ме још занима али се нема времена.

Ипак, склепао сам некакав програмчић а ових дана га пречистио и проверио. Програм следи у целини.

Програм је развијан и тестиран коришћењем "Petite Chez Scheme" http://www.scheme.com/petitechezscheme.html.


; Решавање судоку матрице.
; Функција за решавање је:
; (solve sudo azbuka)
; где је sudo низ који описује почетно
; стање судоку, а azbuka низ који дефинише
; могуће елементе. Класичан судоку ће
; у азбуци имати цифре 1 до 9.


;
; Дефиниција примера судоку проблема
; Судоку таблица се задаје као низ
; по редовима.
;
; Редови и колоне су нумерисане
; бројевима 1 до 9.
;
; Непознати елементи су задати елементом
; који није садржан у азбуци. У примеру
; који следи, елементе које сам хтео да
; прогласим непознатим сам обележио подвлаком.
;
; Резултат обележава решене елементе
; заградом (под листа). Уколико
; једнозначно решење није нађено,
; у загради ће бити изслитана могућа
; решења.
;
(define sudo '(  _1  2 _3  4  5  6 _7 _8 _9
                  4 _5  6  7 _8 _9  1  2  3
                 _7 _8 _9 _1  2  3 _4 _5  6
                  2  3 _4  5  6  7 _8 _9  1
                 _5  6  7 _8 _9  1  2  3  4
                 _8 _9  1  2  3  4  5  6  7
                 _3  4  5  6 _7 _8 _9  1  2
                 _6  7 _8 _9  1  2  3  4  5
                 _9 _1  2  3 _4  5  6 _7  8   ))


;
; Азбука.
; Азбука може бити произвољна све док задовољава
; услов да је број елемената једнак квадрату
; целобројног позитивног броја.
;       
(define azbuka '(1 2 3 4 5 6 7 8 9))

(define a2 '(0 1 2 3))
(define s2 '( ? 1 2 3
              3 2 1 ?
              1 3 0 2
              2 0 ? 1))
              


;
; Основна функција за решавање судоку.
;
(define (solve sudo azbuka)
    (solve1 sudo azbuka 0))

; 
; Помоћна функција за решавање
;
(define (solve1 sudo azbuka rc)
    (cond
   ((>= rc (* (list-len azbuka) (list-len azbuka))) sudo)
      (else
        (solve1
          (append 
            (front (reduce sudo azbuka) rc)
            (cons
              (possible (reduce sudo azbuka) azbuka (row-idx azbuka rc) (col-idx azbuka rc))
              (skip-n (reduce sudo azbuka) (1+ rc))))
          azbuka
          (1+ rc)))))



;
; Редукуј све непознате чланове на листе могућих
; решења
;
(define (reduce sudo azbuka)
 (reduce1 sudo azbuka 0))

;
; Помоћна функција.
; Редукуј све непознате чланове на листе могућих
;
(define (reduce1 sudo azbuka rc)
 (cond
  ((> (1+ rc) (* (list-len azbuka) (list-len azbuka))) '())
  (else
   (cons
    (possible sudo azbuka (row-idx azbuka rc) (col-idx azbuka rc))
    (reduce1 sudo azbuka (1+ rc))))))


;
; Помоћна функција - број елемената у 
; датој листи.
;
(define (list-len lst)
    (cond
      ((or (null? lst) (is-atom? lst)) 0)
      (else
        (1+ (list-len (cdr lst))))))
        
; 
; Помоћна функција - израчунава димензију под-судоку
; матрице.
;
(define (koren azbuka) (sqrt (list-len azbuka)))
                                                                     
;                                                                    
; Одређивање природе вредности aorl
; Враћа #f уколико дата вредност није
; атом.
;
(define is-atom? atom?)

;(define (is-atom? aorl)
;    (cond
;      ((or 
;           (list? aorl)
;           (not (number? aorl))) #f)
;      (else (atom? aorl))))

; 
; Да ли је дати елемент слово азбуке
;
(define (member? at lst)
 (cond
  ((null? lst) #f)
  (else
   (or (eq? at (car lst))
    (member? at (cdr lst))))))

;
; Да ли је дата вредност задата?
;
(define (is-given? aorl azbuka)
    (and (is-atom? aorl) (member? aorl azbuka)))

;
; Да ли је вредност решена?
;
; Решена је само онда када је представљена
; листом са тачно једним елементом који
; није 0.
;
(define (is-solved? lat azbuka)
    (cond
      ((eq? 1 (list-len lat)) #t)
      (else (is-given? lat azbuka))))

;
; Помоћна функција - број елемената у 
; датој листи.
;
(define (list-len lst)
    (cond
      ((or (null? lst) (is-atom? lst)) 0)
      (else
        (1+ (list-len (cdr lst))))))

;
; Прескочи n елемената и врати
; n-ти елемент.
;
(define (skip-n sudo n)
    (cond
      ((null? sudo) '())
      ((eq? 0 n) sudo)
      (else
        (skip-n (cdr sudo) (1- n)))))

;
; Врати листу од n елемената почев од
; датог елемента sudo
;
(define (front sudo n)
    (cond
      ((null? sudo) '())
      ((eq? 0 n) '())
      (else
        (cons (car sudo) (front (cdr sudo) (1- n))))))

;
; Издвој ред n
; 
(define (row sudo azbuka n)
    (cond
      ((null? sudo) '())
      (else
        (front (skip-n sudo (* (1- n) (list-len azbuka))) (list-len azbuka)))))

;
; Помоћна функција за издвајање колоне
;
(define (getcol sudo azbuka n)
    (cond
      ((null? sudo) '())
      ((eq? 0 n) '())
      (else
        (cons (car sudo) (getcol (skip-n sudo (list-len azbuka)) azbuka (1- n))))))

;
; Издвоји колону n
;
(define (col sudo azbuka n)
    (cond
      ((null? sudo) '())
      (else
        (getcol (skip-n sudo (1- n)) azbuka (list-len azbuka)))))
       
;
; помоћна функција - врати ред (почев од 1)
; на основу датог индекса листе која дефинише
; судоку. Индекс почиње са 0.
; 
(define (row-idx azbuka idx)
 (1+ (floor (/ idx (list-len azbuka)))))
 
;
; помоћна функција - врати колону (почев од 1)
; на основу датог индекса листе која дефинише
; судоку. Индекс почиње са 0.
; 
(define (col-idx azbuka idx)
 (1+ (- idx (* (1- (row-idx azbuka idx)) (list-len azbuka)))))

;
; Помоћна функција за издвајање "мини судоку" односно
; подматрице
;
(define (sub-sudo-row sudo azbuka r c rn koren)
 (cond 
  ((> rn koren) '())
  ((null? sudo) '())
 (else
  (append
   (front (skip-n (row sudo azbuka (+ rn (* (1- r) koren))) (* (1- c) koren)) koren)
   (sub-sudo-row sudo azbuka r c (1+ rn) koren)))))

;
; Издвој мини судоку (a пута a, где је а корен
; броја елемената у азбуци) са задате позиције.
; Позиције мини судоку су нумерисане 1 до (list-len azbuka).
;
(define (sub-sudo sudo azbuka r c)
 (sub-sudo-row sudo azbuka r c 1 (koren azbuka)))

;
; 
;
(define (extract-solved su azbuka)
    (cond
      ((null? su) '())
      ((is-solved? (car su) azbuka) 
    (cons 
  (cond
    ((list? (car su)) (caar su))
    (else
    (car su)))
    (extract-solved (cdr su) azbuka)))
      (else
        (extract-solved (cdr su) azbuka))))

;
; Разлика две судоку
;
(define (diff su lat)
    (cond
      ((null? su) '())
      ((null? lat) su)
      ((member? (car su) lat) (diff (cdr su) lat))
      (else
        (cons (car su) (diff (cdr su) lat)))))

;
; Да ли је вредност at члан низа lat?
;
(define (member? at lat)
    (cond
      ((null? lat) #f)
      ((eq? at (car lat)) #t)
      (else
        (member? at (cdr lat)))))

;
; Врати сва немогућа решења за дату позицију.
; (сви елементи азбуке који су већ дати или
; нађени у датом реду, колони и под судоку).
;
(define (allsolved sudo azbuka r c)
    (append
      (extract-solved (row sudo azbuka r) azbuka)
      (extract-solved (col sudo azbuka c) azbuka)
      (extract-solved 
  (sub-sudo 
   sudo azbuka (ceiling (/ r (koren azbuka))) (ceiling (/ c (koren azbuka))))
  azbuka)))


;
; Врати листу могућих решења за дати елемент.
; 
(define (possible sudo azbuka r c)
    (cond
      ((is-solved? (element sudo azbuka r c) azbuka)
       (element sudo azbuka r c))
      (else
        (diff azbuka (allsolved sudo azbuka r c)))))

;
; Помоћна функција. Врати дати елемент.
; Ред и колона почињу од 1
;
(define (element sudo azbuka r c)
    (car (skip-n sudo (+ (* (list-len azbuka) (1- r)) (1- c)))))


gcc и груписање функција

У неким ретким случајевима, потребно је оптимизовати програм тако да се смањи растојање између функција које се често позивају. Могући добитак од овакве оптимизације је смањење "TLB misses" односно потребе да процесор учитава податке за превођење виртуелне у физичку адресу за дати блок. Друга могућа добит је искоришћење различитих нивоа кеширања: уколико су функције физички близу, велика је вероватноћа да ће, док се прва извршава, друга бити учитана у кеш.

Конкретан пример који имам је оптимизација кода за "лењо разрешавање" адреса симбола у динамички повезаним програмима (lazy binding).

Наш оперативни систем (QNX Neutrino) има динамичко повезивање, али не "уме" да то уради "лењо" односно на захтев. Како сада ради, наш динамички повезивач разрешава све адресе симбола на самом почетку, пре него што програм стигне у "main". Последњих неколико дана сам радио на имплементацији која већ постоји на већини јуникс система, а која подразумева разрешавање симбола функција у тренутку првог позива.

Како између два разрешења може доста кода да се изврши, велика је вероватноћа да ће функције потребне за лењо разрешење већ бити избачене из кеша, а такође из процесорске табеле за превођење адреса (TLB) избачене ставке које се односе на физичке странице меморије у којима су те функције. Груписањем функција тако да буду близу једна другој, идеално у једној физичкој страници меморије, ће смањити број промашаја.

Приликом прве компилације установио сам да су функције на приличном растојању једна од друге. Конкретно, приликом дисасемблирања објектног фајла, добио сам ово:

bind_func     0x000000cc
 resolve_func  0x000029e0
 resolve_rels  0x00001010
 lookup_global 0x00000c68

Види се да би фунције биле распоређене у бар 3 меморијске странице (под условом да је страница 4KiB).

Да бих груписао исте, искористио сам атрибут фунције "hot" уведен са gcc верзијом 4.3. Горе наведеним функцијама сам у декларацију додао: __attribute__((hot)). Gcc тако означене функције оптимизује агресивније али и групише у посебну секцију ".text.hot". На крају процеса повезивања, функције су распоређене једна до друге:

bind_func    0x0003f3f8
 resolve_rels 0x0003f418
 resolve_func 0x0003f88c

Интересантно је да је lookup_global "нестала" односно апсорбована у функције које је позивају.

У сваком случају, уместо 4 физичке странице сада функције могу да стану у само једну и распоређене су једна уз другу чиме се повећава шанса да у тренутку позива већ буду у кешу.


Тек треба да упоредим да ли се овиме добија нешто што може да се измери. Вероватно ће доста зависити од процесора па и самог уређаја.

субота, 24. октобар 2009.

7-zip

Коначно сам инсталирао 7-zip (http://www.7-zip.org/) и код куће. Већ неколико месеци га користим на послу. 7-zip "разуме" различите формате архива, између осталих .zip, .rar, .bz2...

WinZip ми стварно неће недостајати.

уторак, 29. септембар 2009.

"Безбедне" Ц функције за манипулацију текста

Често се нађем у ситуацији да нисам сигуран да ли одређена ц функција за манипулацију текста (snprintf, strncpy, strncat, ...) исписује тачно "n" карактера, да ли резултирајући стринг завршава нулом итд.

Ради лакшег потсећања, ево примера за сваку од функција:


#include <stdio.h>
#include <string.h>

static void koristi_snprintf(const char *const tekst)
{
    char buf[10];

    snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s", tekst);
    /* Текст који смо добили у buf је можда скраћен, али је коректан са
     * нулом на крају. */
    printf("%s\n", buf);
}


static void koristi_strncpy(const char *const iz)
{
    char u[10];
    const unsigned s = sizeof(u)/sizeof(u[0]);

    strncpy(u, iz, s - 1);
    u[s - 1] = '\0'; /* Морамо ручно да завршимо стринг. */

    printf("%s\n", u);
}


static void koristi_strncat(const char *const tekst)
{
    char buf[10];
    const unsigned s = sizeof(buf)/sizeof(buf[0]);

    strncpy(buf, "12345", s - 1);
    buf[s - 1] = '\0';

    /* Број карактера који функција може да дода је мања за један
     * због завршне нуле и мања за дужину стринга који је већ у
     * баферу. */
    strncat(buf, tekst, s - 1 - strlen(buf));
    printf("%s\n", buf);
}


static void koristi_strncmp(const char *const tekst)
{
    const char buf[] = "12345";
    const unsigned s = sizeof(buf)/sizeof(buf[0]);

    if (strncmp(buf, tekst, s) == 0)
 printf("Prvih %d karaktera su isti\n", s);
    else
 printf("Razlika postoji u prvih %d karaktera\n", s);
}



int main()
{
    koristi_snprintf("Zdravo. Ovaj tekst je predugacak.");
    koristi_strncpy("Zdravo. Ovaj tekst je predugacak.");
    koristi_strncat("Zdravo. Ovaj tekst je predugacak.");
    koristi_strncmp("12345678");
    return 0;
}

четвртак, 28. мај 2009.

Условна дијагностика у јави

Постоји неколико широко распрострањених библиотека за исписивање дијагностичких порука (енгл. logging). На пример, java.util.logging.

У својим програмима користим ово, али ми недостаје условна компилација. Оно што у Ц/Ц++ могу да урадим са #ifdef  . . . не могу у јави. Тако сам приморан да дијагностику коју користим искључиво за време развоја, и не желим да буде присутна у крајњем производу морам да пишем овако: 



void foo(int bar) {
if (DEBUG)
logger.trace("U foo(" + bar + ")");
}


Међутим, са јавом 1.5, дошла је и кључна реч assert. Ово је добродошло јер омогућава убацивање провера у сам код, а да то не ремети перформансе крајњег производа. Као посебан бонус, који није доступан у Ц/Ц++, асерти се могу укључити по потреби једноставним давањем аргумента -ea јавиној виртуелној машини.

То ме је подстакло да направим једноставан систем за условну дијагностику сличан #ifdef приступу у Ц/Ц++. Користим постојеће пакете (на пример java.util.logging) али унутар асерта. Наравно, ово није могуће урадити директно јер, на жалост, интерфејси дефинисани у овим пакетима не враћају никакву вредност. Мени је било потребно да сваки метод враћа вредност true да бих исти могао да користим унутар assert исказа:



void foo(int bar) {
assert logger.trace("U foo(" + bar + ")");
}


Ово ми даје управо оно што сам желео: условну дијагностику која је у нормалном режиму рада потпуно избачена оптимизацијом. То је чак и боље од Ц/Ц++ условне компилације јер се са јавом коначна компилација врши на рачунару корисника: уколико је јава виртуелна машина стартована са опцијом -ea онда ће дијагностика бити исписивана, у супротном не.

Моја имплементација се састоји у дефинисању интерфејса:



/**
* Copyright (c) 2007 Aleksandar Ristovski.
* All rights reserved. This program and the accompanying
* materials are made available under the terms of the
* Eclipse Public License v1.0 which accompanies this
* distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Aleksandar Ristovski - initial API and implementation
*/
package org.java3declipse.logger;

/** ILogger interface.
*
* This interface is very similar to existing logger
* interfaces, with one significant difference: the
* implementation should always return true.
*
* <p> <b>Rationale:</b>
*
* <p> Logging is a nice feature but can have performance
* impact. If we simply call logger's log method, even if
* the logging level is lower than stated, arguments get
* evaluated. We typically have string concatenations in our
* log message which can have significant impact on memory
* fragmentation and speed.
*
* <p> Some approaches use conditional which is much better
* performance-wise. I don't like this approach as the
* statement is still there. <p> This approach suggests
* using <code>assert</code>. When program is started with
* '-ea', logging is 'on'. When program is started without
* assertions enabled, the logging statements are completely
* optimized out. <p> Typical example:
*
* <pre>
* import org.java3declipse.logger.ILogger;
* import org.java3declipse.logger.Logger;
*
* public class MyClass {
* private static ILogger logger =
* Logger.getLogger(MyClass.class.getSimpleName());
*
* public MyClass() {
* assert logger.info("MyClass constructor");
* }
* }
* </pre>
*
*
* @author Aleksandar Ristovski
*/
public interface ILogger {
boolean info(String msg);
boolean warning(String msg);
boolean error(String msg);
boolean error(String msg, Throwable t);
boolean debug(String msg);
boolean debug(String msg, Throwable t);
}


И имплементацији истог:



/**
* Copyright (c) 2007 Aleksandar Ristovski.
* All rights reserved. This program and the accompanying
* materials are made available under the terms of the
* Eclipse Public License v1.0 which accompanies this
* distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Aleksandar Ristovski - initial API and implementation
*/
package org.java3declipse.logger;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;

/**
* Logger class - default implementation of ILogger interface.
*
* Wrapper for a generic logger. This class provides default
* implementation for ILogger interface.
*
* @see ILogger
* @author Aleksandar Ristovski
*
*/
public class Logger implements ILogger {
private ILogger logger_;

/** Constructor, using initialized
* <code>java.util.logging.Logger</code>.
*
* @param logger - initialized
* <code>java.util.logging.Logger</code>
*/
public Logger(java.util.logging.Logger logger) {
logger_ = this.new JavaUtilLogger(logger);
}

/** Constructor - using user implementation of ILogger.
*
* This constructor can be used if logger other than
* <code>java.util.logging.Logger</code> is desired.
*
* @param logger - initialized object that
* implements <code>ILogger</code> interface
*/
public Logger(ILogger logger) {
logger_ = logger;
}

public boolean info(String msg) {
return logger_.info(msg);
}

public boolean warning(String msg) {
return logger_.warning(msg);
}

public boolean error(String msg) {
return logger_.error(msg);
}

public boolean error(String msg, Throwable t) {
return logger_.error(msg, t);
}

public boolean debug(String msg) {
return logger_.debug(msg);
}

public boolean debug(String msg, Throwable t) {
return logger_.debug(msg, t);
}

private class JavaUtilLogger implements ILogger {
final java.util.logging.Logger javaUtilLogger;
public JavaUtilLogger(java.util.logging.Logger logger) {
javaUtilLogger = logger;
}
public boolean info(String msg) {
javaUtilLogger.info(msg);
return true;
}
public boolean warning(String msg) {
javaUtilLogger.warning(msg);
return true;
}
public boolean error(String msg) {
javaUtilLogger.severe(msg);
return true;
}
public boolean error(String msg, Throwable t) {
javaUtilLogger.log(Level.SEVERE, msg, t);
return true;
}
public boolean info(String string, Throwable e) {
javaUtilLogger.log(Level.INFO, string, e);
return true;
}
public boolean debug(String msg) {
javaUtilLogger.log(Level.FINE, msg);
return true;
}
public boolean debug(String msg, Throwable t) {
javaUtilLogger.log(Level.FINE, msg, t);
return true;
}

}

private static class SimpleFormatter extends Formatter {
@Override
public String format(LogRecord record) {
StringBuffer sb = new StringBuffer(80);
final String ln = record.getLoggerName();
final String msg = record.getMessage();
int level = record.getLevel().intValue();
if (level == Level.INFO.intValue())
sb.append("[INFO] ");
else if (level == Level.WARNING.intValue())
sb.append("[WARNING] ");
else if (level == Level.SEVERE.intValue())
sb.append("[SEVERE] ");
if (null != ln) {
sb.append(record.getLoggerName());
if (ln.length() + msg.length() > 75)
sb.append("\n");
else
sb.append("\t");
}
sb.append(record.getMessage());
sb.append("\n");
final Throwable thr = record.getThrown();
if (null != thr) {
ByteArrayOutputStream btbuff
= new ByteArrayOutputStream(80);
sb.append("Exception: ");
thr.printStackTrace(new PrintStream(btbuff));
sb.append(btbuff.toString());
sb.append("\n");
}
return sb.toString();
}
}

/** Convenience routine.
*
* Creates logger based on java.util.logging.Logger
* class.
*
* @param ID - logger id. Typically
* <code>Classname.class.getSimpleName()</code>
* @return logger
*/
public static ILogger getLogger(String ID) {
java.util.logging.Logger logger
= java.util.logging.Logger.getLogger(ID);
Handler h = new ConsoleHandler();
h.setFormatter(new SimpleFormatter());
logger.setUseParentHandlers(false);
logger.addHandler(h);
return new Logger(logger);
}
}

субота, 25. април 2009.

QNX operativni sistem - tekst objavljen u PCPress

Ово је оригинални текст који је са неким мањим изменама објављен у часопису PCPress, у летњем двоброју 2008 (број 146).





QNX Operativni Sistem

Ugrađeni računarski sistemi1) su danas u najširoj primeni: od digitalnih foto aparata, mobilnih telefona pa do upravljačkih sistema na avionima i satelitima. Ovi sistemi su u proteklih nekoliko decenija, od jednostavnih kontrolera sa specifičnim softverom za određen upravljački zadatak prerasli u kompleksne sisteme na kojima se izvršava mnoštvo aplikacija – od kritičnih upravljačkih aplikacija do grafičkog korisničkog interfejsa. Porast broja aplikacija koje takvi uređaji istovremeno izvršavaju je neizbežno postavio zahtev za operativnim sistemom koji bi to omogućio a da pri tome ne kompromituje stabilnost i odziv sistema u realnom vremenu za sve vremenski kritične zadatke. U ovom tekstu će biti predstavljen jedan takav operativni sistem, Neutrino RTOS2) kanadske firme QNX Software Systems.

Autor: Aleksandar Ristovski

O QNX Software Systems

QNX [1] je kanadska firma osnovana početkom 80-tih godina, kada su njeni osnivači Gordon Bel i Den Dodž primenili stečeno znanje sa fakulteta o RTOS i napravili komercijalan proizvod. Sa vremenom je kompanija rasla i u periodu od oko 27 godina od firme sa dva zaposlena prerasla u kompaniju od nekih 300 zaposlenih i mrežom kancelarija za podršku i prodaju na više kontinenata.

Evolucija

QNX-ov prvi ozbiljan komercijalan uspeh je bio napravljen operativnim sistemom koji se zvao „QNX4“. Već sa QNX4 QNX je nudio dobru podršku za različite vrste perifernih uređaja, ali još uvek bio ograničen na Intelove 32-bitne x86 procesore i platforme bazirane na njima. Podrška multiprocesorskim sistemima nije postojala, a programski interfejs je bio specifičan za QNX4. Ipak, veliki uspeh ovog operativnog sistema je obezbedio važno mesto QNX kompanije u svetu ugrađenih računarskih sistema i utabao put za sledeći veoma uspešan operativni sistem za rad u realnom vremenu nazvan Neutrino.

Neutrino operativni sistem je bio prirodna evolucija QNX4 operativnog sistema. Stratezi QNX kompanije su pravilno procenili da su osnovni sputavajući faktori za dalji rast bili ograničena podrška savremenih procesora i nestandardni programski interfejs. Tako je odlučeno da osnovne novine u Neutrino operativnom sistemu budu podrška za još četiri procesorske familije: SuperHitachi (SH4), Mips32, PowerPC i Arm, kao i standardizacija programskog interfejsa na osnovu POSIKS3) standarda.

Arhitektura jezgra

QNX operativni sistemi spadaju u red mikro-jezgarnih operativnih sistema. Svi procesi se izvršavaju u zasebnim zaštićenim korsničkim adresnim prostorima što je obezbeđeno hardverskim menadžmentom memorije (MMU). Komunikacija izmedju procesa se vrši razmenom poruka4).

Mikro-jezgarna arhitektura je u QNX operativnim sistemima implementirana veoma rigorozno: u zaštićenom režimu rada se izvršava samo mikro-jezgro, dok sve drugo, uključujući drajvere, fajl sistem, protokole, pa čak i proces menadžer radi svaki u svom zasebnom korisničkom adresnom prostoru. To praktično znači da svaka od tih komponenti može da se zaustavi, zameni i ponovo startuje bez zaustavljanja rada mikro jezgra - mogućnost kojom malo drugih mikro-jezgarnih operativnih sistema mogu da se pohvale.

Jedna od najvažnijih karakteristika operativnog sistema jeste strategija raspoređivanja niti izvšavanja5). QNX jezgro nudi raznovrsne strategije raspoređivanja izvršavanja programskih niti što je takođe jedna od karakteristika Neutrina. Osim uobičajenih, postoje i patentirane strategije kao što je adaptivno raspoređivanje po particijama6) koji obezbeđuje visoku odzivnost sistema čak i u slučajevima u kojima jedna od niti visokog prioriteta greškom uzima preveliki procenat procesorskog vremena [3].
Danas takođe veoma važna je i podrška višeprocesorskim sistemima a Neutrino RTOS tu podršku nudi na bazi simetričnih multiprocesorskih sistema (SMP) [5].
Podrška razvoju aplikacija

Pored jezgra, QNX nudi i čitav niz softverskih paketa za podršku različitim platformama8). Tu je i niz programa za POSIKS kompatibilno okruženje, kao i razvojni alati. Razvojni alati su bazirani na GNU kompajleru (gcc) [4] i debageru (gdb) [5] čije prilagođene varijante dolaze sa razvojnim alatima. Grafičko razvojno okruženje za razvoj aplikacija "Momentics" bazriano je na Eclipse platformi i njenoj CDT ekstenziji [7]. Iako je teoretski moguće programirati u različitim programskim jezicima, najčešće se aplikacije razvijaju u C i C++. Java virtuelna mašina je takođe dostupna.

Ugrađeni računarski sistemi često nemaju nikakav prikaz (tzv. "bezglavi" uređaji) ali nisu retki sistemi od kojih se traži bogata grafika. Za takve primene QNX nudi Foton9), svoju grafičku platformu, a odskora i grafički sistem baziran na Adobe Flash. Foton je dosta siromašan u poređenju sa uobičajenim desktop grafičkim okruženjima na koje ste navikli, ali je svejedno simpatičan i zadovoljava većinu potreba u ugrađenom računarskom svetu. Nova inicijativa sa Adobe Flash je veoma interesantna: umesto da nudi grafičku platformu opšte namene po ugledu na desktop računare (Foton), QNX je odlučio da ponudi mogućnost razvoja kompletnog grafičkog okruženja u Adobe Flash-u [8]. Ovo ima i te kako smisla ako se pogleda u kakve uređaje Neutrino ide: na primer, mobilnom telefonu verovatno ne treba standardna grafička komponenta prozorskog okvira, paleta aplikacija i slično. Mnogo je verovatnije da će dizajner uređaja želeti da napravi svoju sopstvenu varijantu korisničkog interfejsa sa lepim animacijama i jedinstvenim izgledom, a Adobe Flash upravo to omogućava. Da stvar bude bolja, znanje koje su programeri stekli programirajući "action script" za veb stranice mogu gotovo direktno da primene i ovde.

Otvoren izvorni kod i otvoreni razvoj

Prošle godine, krajem 2007-me, QNX je napravio interesntan potez. Otvorio je svoj izvorni kod koji se sada može pregledati i skinuti preko Interneta. Za nekomercijalne primene je besplatan što znači da ga može koristiti svako za učenje i evaluaciju. Ova činjenica se rado koristi po fakultetima širom sveta gde se Neutrino koristi u raznim istraživačkim projektima.

Osim otvorenog izvornog koda, QNX je takođe "otvorio" svoj razvoj. Tako na interesnim grupama [2] možete uživo pratiti šta se događa, koji bagovi su ispravljeni, o kakvim novinama se razmišlja i slično. Možete čak i da učestvujete u diskusijama ukoliko ste zaintersovani.

Ipak, primena u komercijalnim proizvodima nije besplatna i podleže uobičajenim poslovnim obavezama a u skladu sa licencom. Cena za komercijalne proizvode varira u velikom rasponu, plaća se po isporučenom uređaju6) a obavezna je i kupovina razvojnog okruženja. Ovo razvojno okruženje se takođe može skinuti sa Interneta i podleže sličnoj licenci kao i izvorni kod, odnosno za nekomercijalnu upoterbu i u akademske svrhe je besplatan.

Za entuzijaste
Za one zainteresovane da se bliže upoznaju sa Neutrinom, sve što je potrebno za početak je brza Internet konekcija. Na stranicama interesnih grupa možete naći veze na potrebne fajlove za instalaciju [11]. Registracija korisničkog naloga je obavezna, ali besplatna. Uobičajeno je da se za razvoj ("host") koriste Windows ili Linux mašine, ali je moguće raditi i direktno na Neutrinu. Ja, konkretno, koristim uglavnom Windows ili Linux za razvoj a kao ciljnu mašinu koristim ili VirtualPC [9] ili VMWare [10] virtuelnu mašinu. Za uputstvo za instalaciji i brzi početak, pogledajte [12].


Korisne Internet adrese

[1] QNX početna stranica http://www.qnx.com
[2] QNX stranica za intersnu grupu http://community.qnx.com (besplatna registracija potrebna za pristup izvornom kodu)
[3] POSIKS standard http://www.unix.org/single_unix_specification/ (besplatna registracija obavezna), http://en.wikipedia.org/wiki/POSIX
[4] GCC - kompajler http://gcc.gnu.org/
[5] GDB - debager http://sourceware.org/gdb/
[6] Eclipse platforma http://www.eclipse.org/
[7] QNX SMP TDK: http://photon.qnx.com/download/download/9421/smp_tdk.pdf
[8] QNX i Adobe Flash: http://www.qnx.com/news/pr_2609_1.html
[9] Virtual PC - besplatan ali dostupan samo za Windows http://www.microsoft.com/windows/products/winfamily/virtualpc/default.mspx
[10] VMWare - http://www.vmware.com/
[11] QNX Momentics - http://www.qnx.com/products/getmomentics/
[12] Uputstvo za instalaciju i početak: http://www.qnx.com/developers/docs/6.3.2/momentics/quickstart/about.html

Fusnote

1) engl. Embedded systems
2) engl. Real Time Operating System - operativni sistem za rad u realnom vremenu
3) eng. POSIX - Portable Operating System Interface
4) engl. Message Passing
5) engl. Thread Scheduling
6) engl. Adaptive Partition Scheduling
7) engl. Runtime
8) engl. Board Support Package
9) engl. Photon


четвртак, 23. април 2009.

LaTeX и ћирилица

Коначно сам, уз помоћ Страхиње Радића, успео да генеришем ћирилични текст из латекса.

Још увек није савршено јер немам "babel" модул (не знам тачно из ког разлога), па сам морао да искоментаришем ту линију.

\documentclass{article}
\usepackage[utf8x]{inputenc}
\usepackage[T2A]{fontenc}
% \usapackage{babel}
\begin{document}
АБВГДЂ % наравно, сачувано као УТФ-8
\end{document}

Следећи корак, генерисање хтмл-а из латекса. Постоји програм, latex2html који на жалост не ради код мене, али верујем да могу да га оспособим. Крајњи циљ је да пишем ове чланке за вебник (блог) у латексу а онда генеришем одговарајући хтмл. Треба "потрефити" стилове како бих могао да користим ".css".

О том у неком следећем издању.

уторак, 14. април 2009.

Форматирање изворног кода за вебник

После дужег тражења одговарајућег алата за форматирање изворног кода који се појављује у вебнику, коначно сам нашао задовољавајуће решење.

Користим "GNU source-highlight" http://www.gnu.org/software/src-highlite/ .

Са командне линије радим ово:

$ source-highlight --no-doc --css=code.css test.c

где је садржај code.css овакав:



body {  background-color: white;  }

/* the color for context lines (when specified with line ranges) */
.context { color: gray; }

.keyword { color: blue; font-weight: bold; }
.type { color: darkgreen; }
.usertype, .classname { color: teal; }
.string { color: red; font-family: monospace; }
.regexp { color: orange; }
.specialchar { color: pink; font-family: monospace; }
.comment { color: brown; font-style: italic; }
.number { color: purple; }
.preproc { color: darkblue; font-weight: bold; }
.symbol { color: darkred; }
.function { color: black; font-weight: bold; }
.cbracket { color: red; }
.todo { font-weight: bold; background-color: cyan; }

/* line numbers */
.linenum { color: black; font-family: monospace; }

/* Internet related */
.url { color: blue; text-decoration: underline; font-family: monospace; }

/* other elements for ChangeLog and Log files */
.date { color: blue; font-weight: bold; }
.time, .file { color: darkblue; font-weight: bold; }
.ip, .name { color: darkgreen; }

/* for Prolog, Perl */
.variable { color: darkgreen; }
.italics { color: darkgreen; font-style: italic; }
.bold { color: darkgreen; font-weight: bold; }

/* for LaTeX */
.underline { color: darkgreen; text-decoration: underline; }
.fixed { color: green; font-family: monospace; }
.argument, .optionalargument { color: darkgreen; }
.math { color: orange; }
.bibtex { color: blue; }

/* for diffs */
.oldfile { color: orange; }
.newfile { color: darkgreen; }
.difflines { color: blue; }

/* for css */
.selector { color: purple; }
.property { color: blue; }
.value { color: darkgreen; font-style: italic; }


Затим сам исти садржај додао у шаблон блога, али без "body" линије. Уместо ње сам додао:

pre {  background-color: #EEEEFF;  }



И то је све. Још увек има простора за убрзавање овог процеса, али елементи су ту.

Assert

Волим да користим асерте. Корисни су као само документујући код али и као помоћ при развоју.

Асерти су провере истинитости одређеног израза. Уколико евалуација израза који се прослеђује макроу „assert” за резултат даје нулу, програм се прекида уз поруку о изразу, имену фајла и линији. Порука може да се разликује у зависности од компајлера и система на коме се програмира, али ће све мање-више имати одређену информацију о томе где се асерт десио.

Када се програм искомпајлира са дефинисаним макроом „NDEBUG”, асерти нестају (макро assert бива дефинисан као ((void)0) и избачен као линија без ефекта).

Током развоја, важно је користити програм компајлиран без овог макроа (односно са укљученим асертима).

Један тривијалан пример:


#include <assert.h>
#include <string.h>

int mojstrlen(const char * const p)
{
assert(p != NULL);

return strlen(p);
}


У примеру се види предност (и мана) овог приступа. Иако не можемо да забранимо кориснику функције да проследи NULL вредност, омогућујемо му да открије своју грешку у току развоја. Такође, само кратким погледом на функцију кориснику (програмеру) ће бити јасно да треба да обезбеди не-нул вредност.

Сама чињеница да асерти не постоје у коначном програму који се испоручује их чини адекватним само за оне случајеве који не смеју да се десе на терену, али истовремено их чине идеалним за време тестирања.

Рецимо да хоћемо да направимо робусну библиотеку и затим је дистрибуирамо корисницима од којих не можемо да очекујемо да знају да вредност показивача не сме да буде нула, онда асерт није довољан, а можда ни адекватан. У том случају би било примереније урадити нешто овако:


int mojstrlen(const char *const p)
{
if (p == NULL)
return 0;
return strlen(p);
}



Као правило, ја користим асерте на функцијама које нису експортоване из библиотека, него су намењене за коришћење унутар једног програма. Како често није очигледно шта одређен логички исказ проверава, онда уз исказ додам и текст који описује проверу. У примеру горе, то би изгледало овако:


int mojstrlen(const char * const p)
{
assert(p != NULL && !!"Pokazivac 'p' ne sme biti NULL");

return strlen(p);
}

среда, 8. април 2009.

конст коректност 2


Интересантност о којој до данас нисам мислио. Колега ми је отворио очи. Наиме, ради се овоме: колега је послао фајл са Ц кодом на преглед. Његов код је имао функције које су изгледале отприлико овако:


/* Primer const argumenta u definiciji. */
static int foo(const int bar)
{
if (bar > 7)
global_bar = bar - 7;
}


На то сам ја коментарисао да обично не "губим" време декларишући формални аргумент конст, на шта ми је одговорио (потпуно исправно) да је у дефиницији конст врло корисна, за разлику од декларације.

Конст формални аргумент у декларацији сам коментарисао, али ја лично никада нисам размишљао о томе да дефиниција може да дода модификатор конст формалном аргументу, а да тиме не дефинише неку нову функцију.

На пример, нека је у заглављу оваква декларација:


/* Deklaracija funkcije. */
int funkcija(const char *);


А имплементација може овако:


/* Definicija funkcije. */
int funkcija(const char * const ime)
{
// ...
}


Заиста, овако се кориснику функције (који обично гледа прототип декларисан у заглављу) не намеће имплементациони детаљ за њега (корисника) небитан, док се читаоцу имплементације одмах даје до знања да се вредност формалног аргумента "ime" неће мењати у телу функције. Дакле, даје читаоцу сличну информацију као и конст модификатор на локалној варијабли.

Даље је интересантно приметити да додавање конст модификатора формалном аргументу у декларацији у суштини не мења декларацију. На пример, ове две декларације декларишу исту функцију.



/* Ekvivalentne deklaracije funkcije. */
int funkcija(const char *);
int funkcija(const char *const);



Oва друга је очигледно рогобатнија и представља једну од оних декларација у којима сам причао и у претходном чланку.