24 Nisan 2015 Cuma

NonBuffered ve Buffered I/O

Bu yazı ile ilgili olarak Devpts başlıklı yazıyı okuyabilirsiniz.Orada da anlatıldığı gibi her process 3 tane posix stream ile başlatılıyor. Ancak bir çok programlama dili işletim sistemi tarafından atana bu posix streamlerin de üzerine başka soyutlamalar getiriyor. Bu yazıda bunlara kısaca bakacağız.

Not : I/O işlemlerinde eğer disk dolarsa ENOSPC hata kodu döndürülür.

Non-Buffered I/O

Non-Buffered I/O için işletim sistemi tarafından sağlanan open() metodu kullanılabilir Örnek :
open(filename, O_RDRW | O_CREAT, 0644);
open aslında 
int open(const char *pathname, int flags) ve
int open(const char *pathname,int flags, mode_t mode);
şeklinde iki imzaya sahip. 3 parametreli metod yani mode parametresi alan metod, flags alanında O_CREATE varsa dosyanın hangi haklarla yaratılacağını belirtmek için kullanılıyor. Yani özeti şu cümle.
mode specifies the permissions to use in case a new file is created.

Page Cache'in Kullanılmaması (Virtual Memory kullanılmaması)

Page Cache, the Affair Between Memory and Files başlıklı yazıda da anlatıldığı gibi non-buffered I/O yapsak bile işletim sistemi halen page cache yapısını kullanmaya devam etmekte. Bu sayfadaki page cache yapısını anlatan şekil aşağıda.



Eğer bu yapıyı da devre dışı bırakıp direkt diske yazmak istersek Linux üzerinde O_DIRECT seçeneği kullanılabilir. Bu durumda Page Cache kullanılmıyor ancak Disk Write Cache halen kullanılıyor.
Burada O_DIRECT açıklanmış.

 O_DIRECT'e karşılık olarak Windows'ta FILE_FLAG_NO_BUFFERING  (önbellekte tutma) seçeneği kullanılabilir.


Small file not committed to disk for over a minute sorusunda da açıklandığı gibi farklı dosya sistemleri farklı cache ayarları kullanabiliyorlar.


Bir dosyanın O_DIRECT seçeneği ile açılıp açılmadığını görmek için How to tell if a given process opened files with O_DIRECT? sorusunun cevabına göz atabilirsiniz. Bu bayrağın kullanılması diske yazma işleminde tüm verinin tamamen yazıldıktan sonra write() metodunun dönmesini garanti etmez! How are the O_SYNC and O_DIRECT flags in open(2) different/alike? sorusunda da bu durum açıklanıyor.

Verinin Diske Yazıldığını Garanti Etmek (Disk Write Cache kullanılmaması)
O_SYNC bayrağı ile verinin diske yazıldığını garanti etmek man sayfasına göre mümkün  çünkü man sayfasında şu cümle geçiyor. 
O_SYNC The file is opened for synchronous I/O. Any write(2)s on the resulting file descriptor will block the calling process until the data has been physically written to the underlying hardware.
Örnek : 
fd = open(filename, O_SYNC);
O_SYNC'e karşılık olarak Windows'ta FILE_FLAG_WRITE_THROUGH kullanılabilir. FILE_FLAG_WRITE_THROUGH seçeneğinin FILE_FLAG_NO_BUFFERING ile kullanılıp kullanılmaması aşağıda açıklanmış.

Ancak HDD, FS, O_SYNC : Throughput vs. Integrity yazısında da açıklandığı gibi HDD de kendi içinde bir önbellek kullanıyor. O_SYNC bayrağı kullanılsa bile bu verinin tamamen manyetik ortama yazıldığını garanti etmiyor.

POSIX guaranteeing write to disk sorusunda ise synch() metodunun kullanılması söylenmiş.

posix_fadvise metodu
Dosya açıldıktan sonra posix_fadvise metodu ile bir çok ince ayar daha yapmak mümkün. Örneğin 

POSIX_FADV_RANDOM :  Bu bayrak ile dosyaya rastgele bir şekilde erişileceğini belirtip işletim sistemi 
tarafından kullanılan read-ahead (önden okuma özelliği kapatılabilir).

POSIX_FADV_SEQUENTIAL : Bu bayrak ile dosyaya daha fazla read-ahead yapılması gerektiği belirtilir.
posix_fadvise(in_fd, 0, 0, POSIX_FADV_SEQUENTIAL);
POSIX_FADV_WILLNEED: İşletim sistemine read-ahead (önden okuma) işlemini ne kadar yapması söylenebilir. Örnek.
posix_fadvise(fd, 0, 512*1024*1024, POSIX_FADV_WILLNEED);
POSIX_FADV_DONTNEED: İşletim sistemine read-ahead için ayrılan bellek alanına ihtiyaç kalmadığı söylenir.

Büyük Dosyaları Okumak

Non-Buffered I/O ile Kullanılan Bazı Metodlar

close metodu
close metodu dosyayı kapatır. Burada anlatıldığı gibi bu metodun döndürdüğü değere mutlaka bakılması tavsiye ediliyor.
Eğer zaten kapatılmış olan bir dosyayı, tekrar kapatmaya kalkışırsak EBADF hatası alırız. Örnek:

fgets ve gets
gets() güvenli bir metod değil, bu yüzden mümkünse fgets() kullanılmalı.

Since the user cannot specify the length of the buffer passed to gets(), use of this function is discouraged. The length of the string read is unlimited. It is possible to overflow this buffer in such a way as to cause applications to fail, or possible system security violations.
fgets'in imzası şöyle
char * fgets ( char * str, int num, FILE * stream );
Metod şöyle kullanılır.

char name[256];
fgets(name,256,stdin);
Önemli bir nokta şu , gets newline karakterini de okunan string'e dahil eder!

fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte ('\0') is stored after the last character in the buffer.
Bu karakterden kurtulmak için şöyle yapılabilir. Böylece \n karakteri yerine \0 koymuş oluruz.
while (fgets(buffer, sizeof buffer, stdin) != NULL) {
  size_t len = strlen(buffer);
  if (len > 0 && buffer[len - 1] == '\n')
    buffer[--len] = '\0';

  // use buffer
  printf("\"%s\"\n", buffer);
}
strlen yerine strcspn kullanılabilir. Veya strtok kullanılabilir. Her ne olursa olsun C ile dosya işlemleri ve string'ler yanyana gelmemesi gereken şeyler. İğrenç :)

ftruncate
Dosyayı verilen büyüklüğe getirir. Bu metod kullanılarak bir dosyanın başından ven sonundan bazı satırları silme örneği burada. ftruncate şu hataları dönebilir.
EROFS : File resides in a readonly file system.
EBADF : File is open for reading only
EINVAL : File descriptor refers to a file on which this operation is impossible.

Buffered I/O
C Dili
İşletim sistemi tarafından atanan 3 posix stream için C dilinde unistd.h dosyasında macrolar tanımlanmıştır. Bu macrolar aşağıdaki gibidir.

Ancak posix streamleri unbuffered I/O için tasarlanmışlardır. C dili ise buffered I/O yapmak için başka yapılar sunmaktadır. Bunlar yapılar stdio.h dosyasında tanımlıdır ve FILE yapılarıdır. Aşağıda bu durumu görebiliriz.



Yani C dilindeki stdin , stdout , stderr FILE struct'larıdır ve macrolardan farklı şeylerdir.

Burada bir konuya daha değinmek lazım. Nasıl olduğunu bilimiyorum ancak stderr stream'inin non-buffered olduğu yazıyor. Dolayısıyla stdout ve sterr aynı terminale yazdıklarında stdout çıktısı daha yazılmadan sterr çıktısını görebiliriz. Benzer sıkıntılar C# içinde geçerli.

Aşağıdaki şekli buradan aldım ve C dilinde buffered I/O yapıldığını daha iyi gösteriyor.
FILE struct'a Yazmak İçin
fprintf kullanılır. Burada dikkat edilmesi gereken nokta fprintf sadece FILE struct'a yazmaya yarar örneğin socket handle için kullanılamaz.

FILE Struct ile ilgili Diğer Metodlar
Listeyi buradan aldım.
fopen , fclose, freopen
fread, fwrite,
fflush,
fseek
fseek metodunu complexity'si burada tartışılmış.

fgetc,getc, getchar,
fputc, putc, putchar,
fputs

printf, fprintf, sprintf
scanf,fscanf,sscanf

fopen
Dosyayı sadece yazmak için açmak için "w" kullanmalıyız. Örnek:
FILE *file = fopen("/path","w")
freopen
freopen metodu yazısına taşıdım.

fputc
Tek bir karakter yazar.
fputc('1',file);
fputs
fputs sadece NULL ile biten string'i yazar. Eğer dosyaya NULL yazmak istiyorsak fwrite kullanmalıyız.

fscanf
scanf ailesinden olan bu metod ile dosyadan formatlı olarak veri okumak mümkün.


Buffer'ın Boyunu Değiştirmek
C Dili
I/O için kullanılan tampon'unun (buffer) boyunu değiştirmek için setbuf metodu kullanılır. Buffer yöntemini (unbuffered, line buffered, fully buffered) değiştirmek içinse  setvbuf metodu kullanılır.

setvbuf için örnek:
FILE* outf = //...
char obuffer[BUFSIZ];
//Full buffering: tampon tamamen dolunca yaz ve tamamen boşalınca oku
setvbuf(outf, obuffer, _IOFBF, sizeof(obuffer));
Buffer'ın boyunu seçerken BUFSIZ macrosu kullanılabilir. Bu macro her sistemde farklı bir değere sahip ve stream işlemleri için en uygun değeri taşıdığı yazılı.

NonBuffered I/O'dan Buffered I/O'ya Geçmek
C Dili
NonBuffered I/O'yu temsil eden bir stream fdopen() metodu ile buffered FILE* haline getirilebilir. Windows üzerinde ise _fdopen() kullanılır. Örnek:

int file;
FILE *stream = fdopen (file, "r");

Buffered I/O'dan NonBuffered I/O'ya Geçmek
C dili
Buffered I/O'yu temsil eden FILE* yapısı fileno (FILE* stream) metodu kullanılarak işletim sistemi tarafından kullanılan stream (file descriptor) numarasına çevirilebilir. Burada, gcc -std=gnu99 veya -D_POSIX_SOURCE seçenekleri ile derlenmesi gerektiği söylenmiş. Örnek:


Buffer'ı Boşaltmaya Zorlamak
C Dili
Bu iş için fflush() metodu kullanılıyor. Yanlız input için kullanılan bir stream üzerinde fflush kullanmak tanımsız bir davranış. Bu yüzden yapmamak lazım.

Buffer'ın Wide Oriented veya Byte Oriented Çalıştığını Kontrol Etmek
Sadece C dilinde  Byte-oriented veya Wide-oriented çalışma durumu var. Implementing perror() - issue sorusunda fwide kullanma örneği mevcut.
C++ Dili
std::cin , std::cout , std::cerr  C dilindeki yapılara karşılık geliyorlar. std::clog std::cerr ile aynı aycak std::cerr non-buffered iken std::clog buffered bir yapı.

C++ streamleri ile kullanılabilen özel karakterler var. 
\f : Ekranı temizlemek için kullanılabilir. 
\t : Bir tab karakteri koymak için kullanılabilir.

Java Dili
Java'da Flushable arayüzü ve bu arayüzü gerçekleştiren bir sürü sınıf var.

Bir diğer ilginç nokta ise Java ile gelen InputStream sınıfının non-buffered olmasına rağmen yine de available() diye bir metod sunması. Bu metod ile bloke olmadan okunabilecek byte sayısını almak mümkün. 

22 Nisan 2015 Çarşamba

Thread'i Durdurmak

Giriş
Thread'i kolay ve temiz bir şekilde durdurmak her programlama dilinde sıkıntıdır. Aşağıda notlarım var.

Worker Thread
Bu tür thread'leri durdurmanın en temiz yolu, kuyruğa özel bir job eklemek. Örnek
void SimpleWorkQueue::tellAllThreadsToStop()
{
    std::unique_lock<std::mutex> locker(lock);
    stopping = true;

    for(std::thread& thread: workers)
    {
        work.push_back([](){ throw TerminateThread(); });
    }
}


// Now our main thread loop.
// looks like this.
// Note: We can remove 'finished' from everywhere.
void SimpleWorkQueue::workerAction()
{
    while(true)
    {
        std::function<void()> item = getNextWorkItem();
        try
        {
            item();
        }
        catch(TerminateThread const& e)
        {
            break;
        }
        catch(...)
        {
        }
    }
}

WIN32
TerminateThread() asla kullanılmamalı. Win32 ile bir thread sınıfı gelmediği için durdurma kontrolünü kendimiz yazmalıyız. Thread'i başlatmadan önce bir event yaratırız. Event'ler aynı bir bayrak gibi kullanılabilir.
HANDLE hevent_killme = CreateEvent(...);
Daha sonra bu event thread'e geçilir.

static DWORD WINAPI thread_callback (LPVOID param)
{
  ...
  while(WaitForSingleObject(hevent_killme, 0) != WAIT_OBJECT_0)
  {
    // do stuff
  }

  return 0;
}
Thread'i durdurmak isteyen event'i tetikler.
void killthread (void)
{
  SetEvent(hevent_killme);
  WaitForSingleObject(hthread_the_thread, INFINITE);
  CloseHandle(hevent_killme);
  CloseHandle(hthread_the_thread);
} 

Java
Thread.stop
Bu metod depricate edilmiş ve kullanılmamalı. Sebebi şu:

This method is inherently unsafe. Stopping a thread with Thread.stop causes it to unlock all of the monitors that it has locked (as a natural consequence of the unchecked ThreadDeath exception propagating up the stack). If any of the objects previously protected by these monitors were in an inconsistent state, the damaged objects become visible to other threads, potentially resulting in arbitrary behavior.

Thread.Interrupt
Bir thread'i durdumanın en kolay ve temiz yolu Thread.Interrupt () metodunu çağırmak.

An interrupt is an indication to a thread that it should stop what it is doing and do something else. It's up to the programmer to decide exactly how a thread responds to an interrupt, but it is very common for the thread to terminate.


Thread.Interrupt Ne Yapar?
Metod aslında sadece bir bayrağı kaldırır. Metod tamamen işbirliği esasına dayanır. Bir thread asla bir başka thread'i durdurmaya zorlayamaz. Diğer thread'in de gönül rızası göstererek durmayı kabul etmesi gerekir.


Interrupts this thread. First the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown.

If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

If this thread is blocked in an I/O operation upon an interruptible channel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a ClosedByInterruptException.

If this thread is blocked in a Selector then the thread's interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector's wakeup method were invoked.

If none of the previous conditions hold then this thread's interrupt status will be set.

Thread.interrupted
Çalışan thread döngü içinde interrupted() metodunu ile durdurulmasının talep edilip edilmediğini kontrol etmeli. Metodun açıklaması şöyle. Thread bu metodu çağırdıktan sonra bayrak indirilir.

Eğer thread kendi içinde halen bu bayrağı kontrol ediyorsa, yine thread içinde Thread.currentThread.interrupt() ile bayrağı tekrar kaldırmak gerekebilir.

static boolean interrupted(): Tests whether the current thread has been interrupted.
Basit bir örnek
public void run()
{
    while(!Thread.interrupted)
    {
     ...
    }
}

Thread.isInterrupted
Eğer başka bir thread'in interrupt edilip edilmediğini anlamak istersek isInterrupted() metodu kullanılır

boolean isInterrupted(): Tests whether this thread has been interrupted.

InterruptedException
Eğer interrupt edilen thread bir nesne üzerinde bloke olmuş bekliyorsa InterruptedException alır.

Thread.isInterrupted true dönerse InterruptedException atılması öneriliyor. Ancak interrupt edildiğimizi anladıktan sonra neden exception atmak lazım anlamadım.
if (Thread.interrupted()) throw new InterruptedException(); 


15 Nisan 2015 Çarşamba

Spring Service Sınıfları

Giriş
Klasik bir servis sınıfı veritabanıyla çalışmak için kullanılır. Şöyle tanımlanır.

@Service
public class UserServiceImpl implements UserService
{
  @Autowired
  private UserDAO userDAO;

  @Override
  @Transactional
  public void addUser( User user )
  {
    userDAO.addUser( user );
  }
}

14 Nisan 2015 Salı

Oracle VM Virtual Box

Virtual Box
vboxsf
Guest Linux ile Host Windows arasında dizin paylaşmak için şöyle yapılır

1. Windows'ta D:\Paylasilan isimli bir dizin acilir.

2. Linux'ta bu dizin mount edilir.

mnt -t vboxsf Paylasilan /mnt/paylas

VMWare

1. VmPlayer kurulum sonrasında VMWare Network Adapter VMNetx isminde bir veya daha fazla network device yaratır.
2. .vmx dosyası çift tıklanarak imaj çalıştırılır.
3. İmaj çalışırken ana işletim sistemine dönmek için Ctrl ve Alt tuşlarına basılır.
4. Eğer bir .iso dosyası CD olarak tanıtılmak istenirse Player/Removable Devices menüsü kullanılarak yapılabilir.  Böylece .iso dosyasından başka yazılım kurulumu yapılabilir.
5. Eğer Windows'ta Share edilmiş bir dizin Linux'a tanıtılmak istenirse Player/Removable Devices menüsü kullanılarak yapılabilir. Paylaşılan dizin  /mnt/hgfs/Paylasilan gibi bir sekilde görülür.
6. İmaj için işlemci ve bellek ayarını ayarlamak gerekebilir.

8 Nisan 2015 Çarşamba

C89

Giriş
C89 artık kullanılmayan bir standart. Bazı özellikleri şöyleydi.

Değişkenlerin Metod Başında Tanımlanması
Zamanın derleyicileri çok akıllı olmadıkları için değişkenler metodun başında tanımlanınca ne kadar stack ayrılması gerektiğini çok daha kolay hesaplıyorlardı.

3 Nisan 2015 Cuma

Unix Linux türevleri

Unix Linux Türevleri
İşletim sistemleri sürekli yeni versiyonları ile gündeme geliyorlar. Ben de konuyu anlamak için aşağıdaki şekli Unix Wars başlıklı yazıdan aldım.
İki tane ana Unix dalı var. Biri BSD diğeri ise System V ailesi.
Bir başka şekli ise buradan aldım ve aynı aile üyelerini gösterecek şekilde işaretledim.


Her iki aile içinde de beni en çok ilgilendiren şey socket arayüzünün BSD dalı tarafından standartlaştırılması.

Unix'lerin Ortak Noktası
Unix işletim sistemlerinin ortak noktası Single Unix Specification'a uyumlu olmaları. Single Unix Specification sunulan araçlardan, API'lere kadar herşeyin nasıl olması gerektiğini belirtiyor. Ayrıca bir Unix işletim sisteminin The Open Group'a kayıt olması gerekir.

Linux'ların Ortak Noktası
Linux'lar ise Linux Standard Base dokümanına uyumlular. Bu doküman aynı zamanda bir ISO standardı.

POSIX
Unix ve Linux'un ortak buluşma noktası ise Posix. Her iki standard Posix uyumlu oldukları için, Unix ve Linux birbirlerine benzerler.




1 Nisan 2015 Çarşamba

Circular Buffer

Circular Buffer Ne Zaman Kullanılır

1. Object Pool için kullanılır. Read ve Write indeksleri kullanılarak belli sayıda nesne yaratılır.
2. Last K Item problemi için kullanılır. Bir dosyanın en son K satırını okuma probleminde çözüm olarak circular buffer kullanılır. Buffer doldukça başa döner. Böylece her zaman K tane nesne elimizde olur.

Temel Varsayımlar
1. Circular Buffer içinde bir array tutar.
2. Array'in boyu buffer yaratılırken verilir ve değiştirilmez.