QED - UNIX API w wersji trochę bardziej przyjaznej

Chciałbym opublikować kawałek kodu, który napisałem pod wpływem emocji w ramach nauki na końcowe kolokwium z systemów operacyjnych. Zadania, które dostawaliśmy tam były niemożliwe do zrobienia bez dobrej znajomości API UNIXa dotyczącego komunikacji międzyprocesowej i przygotowania sobie jakiegoś kodu wcześniej. Teoretycznie można było korzystać z Internetu i dowolnych własnych zasobów, ale bez wstępnego przygotowania nie było szans ;). Większość znajomych przygotowywała więc sobie rozwiązania dotychczasowych zadań z laboratorium w nadziei, że będą się dały zaadaptować do kolokwium. QED, bo tak nazywa się biblioteka, powstała jako wynik frustracji po słabym zaliczeniu pierwszego terminu. Opakowuje ona całe to przykre i bolesne w użyciu API IPC UNIX'a do postaci prostych klas i funkcji C++. Większość zadań da się zrobić bez ani jednej linii kodu w czystym UNIXowym API :). Biblioteka QED oraz przykładowe zadania z laboratoriów / kolokwium. Biblioteczka ta zawiera:
  • Uproszczony lexical_cast
  • Obiekty do łatwego synchronizowania operacji na std::ostream(tak, na std::cout też!) w programach wielowątkowych
  • Funkcję do pobierania czasu z milisekundową rozdzielczością
  • Pomocnicze makra zastępujące powtarzający się w kółko kod
  • Pomocnicze funkcje do wygodnego fork() (łącznie z przepinaniem STDIN i STDOUT dziecka); fork-exec idiom trzeba sobie zrobić samemu, ale co to za problem wywołać sobie funkcję exec() :P.
  • Klasy izolujące nienazwane i nazwane pipe'y (FIFO), semafory, pamięć współdzieloną i kolejki komunikatów
  • Inne pomocnicze funkcje
Mam szczerą nadzieję, że kod ten przyda się przynajmniej tym z Czytelników, którzy już za rok (albo i wcześniej) zmierzą się z Systemami Operacyjnymi na IS :). Dlaczego taka nazwa? QED oznacza elektrodynamikę kwantową - bardzo pokręconą dziedzinę fizyki, o której czytałem sobie "do obiadu" w okresie prac nad powyższą biblioteką. Q.E.D. to też łaciński skrót oznaczający Quod erat demonstrandum ("Co było do udowodnienia") - co dobrze współgrało z moją frustracją po pierwszym terminie (na zasadzie: "no, na drugim pokażę!"). Dla niewystarczająco przekonanych, przykład kodu:
//================================================================
//Beta section
//================================================================
int process_beta(void* data)
{
	betaQueue.attach();
	alphaSharedQueue.attach();

	char buffer[1024];

	while(true)
	{
		std::memset(buffer, 0, 1024*sizeof(char));
		
		//get data from msg queue
		betaQueue.receive_message(buffer);

		//cut out last two letters
		buffer[std::max(static_cast<int>(std::strlen(buffer)) - 2, 0)] = '\0';

		//send data via message queue to P1
		alphaSharedQueue.send_message(buffer);
	}

	return 0;
}
Oto kod procesu, który odbiera informacje z kolejki komunikatów, obcina im dwa ostatnie znaki i wysyła je inną kolejką komunikatów. Teraz, wyobraź sobie, że piszesz w czystym UNIX API :P.