9 Mart 2016 Çarşamba

Data Alignment

C++
Data Alignment yazısına taşıdım.

alignof metodu
alignof yazısına taşıdım.

alignas metodu
alignas ile bir verinin nasıl align edilmesini istediğimizi belirtiriz. POD içindeki bir alan yüzünden fazlaca yer israf ediliyorsa kullanılabilir. Örnek:

struct st
{
    short a; //2 byte
    int *b; // 8 byte (64 bit işletim sistemi)
    char ch;//1 byte
};

11 byte kullanılması gerekirken, 8 byte alan yüzünden 8'in katı olan 24 byte'lık alan ayırılır.

Daha az alan kullanmak için nasıl align edilmesi gerektiği alignas ile belirtirlir.
struct alignas(char) aligned_struct
{
    short a;
    int *b;
    char ch;
};
Bu durumda 12 byte'lık alan ayırılır. VS 2012 alignas'i desteklemiyor!

offsetof metodu
struct içindeki alanın kaçıncı byte'tan başladığını belirtir. Örnek:
struct Test
{
 char data1;
 char data2;
}
cout << offsetof (Test,data1); // 0 verir
cout << offsetof (Test,data2); // 2 verir
struct Test {short data1, short data2, double data3} ile offsetlerin şöyle başladığı gözlemlenebilir.
struct Test
{
  short data1; //2
  short data2; //2
  char[4] pad1;//4
  double data3;//8
}

offsetof macrosu sadece standard layout olan sınıflarda kullanılabilir. Bu macro ile encapsulation kırılabilir. Örnekte private olan bir nesnenin value alanına erişilebiliyor.
#include<iostream>
using namespace std;

class A
{
    int value;
 public:
    A(){value = 1;}
    ~A(){}

    void print(){cout << value << endl;}
 };

int main()
{
    A a;
    int* p = (int*)(&a);
    *p = 20;
    a.print();//output is 20.
}
__attributed__((packed))
gcc ile kullanılır. Padleme yapma anlamına gelir. Şöyle yaparız.
struct __attribute__((__packed__)) structure
{
   int id1;
   char name;
   int id2;
   char c;
   float percentage;
};

pragma pack
Microsoft ve gcc platformunda kullanılır. Pragma'ya verilen sayıdan büyük olan alanlar verilen sayıya göre hizalanır. Eğer alan pragma sayısından küçükse bir şey yapılmaz.

Verinin 1 byte olarak hizalanması için  örnek:
#pragma pack(push, 1)
typedef struct {
    unsigned char __reserved : 1;
    unsigned char dont_fragment : 1;
    unsigned char more_fragment : 1;
    unsigned short fragment_offset : 13; 
} ipv4_fragmenting;
#pragma pack(pop)
Şöyle de yapılabilir.
#pragma pack(1)
typedef struct {
    unsigned char __reserved : 1;
    unsigned char dont_fragment : 1;
    unsigned char more_fragment : 1;
    unsigned short fragment_offset : 13; 
} ipv4_fragmenting;
#pragma pack()
Bir başka örnekte veri 4 byte olarak hizalanıyor.
#pragma pack(push,4)
struct  MyStruct
{  
    uint32_t i1;        /* offset=0  size=4 */
    uint32_t i2;        /* offset=4  size=4 */
    uint16_t s1;        /* offset=8  size=2 */
    unsigned char c[8]; /* offset=10 size=8 */
    uint16_t s2;        /* offset=18 size=2 */
    uint16_t s3;        /* offset=20 size=2 */
                        /* offset=22 padding=2 (needed to align MyStruct) */
} ; // total size is 24

C#
Interop ile C++ kodlarını çağırırken struct'lara müdahale etmek mümkün.

MarshalAs Anotasyonu
Bu anotasyon managed ve unmanaged kod arasında, belleğin nasıl dönüştürüleceğini UnmanagedType enumeration ile kontrol eder.
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
struct PSTSensor
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
    public string name;
    public int id;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
    public float[] pose;
    public double timestamp;
}
Tiplerin Büyüklüğü
Dikkatli olunması gereken nokta boolean tipi. Boolean 1 byte değildir. Diğer tiplerde sorun çıkmaz. Mesela aşağıdaki byte[] 3 byte büyüklüğündedir.
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct struct1
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public byte[] values;
}
Çıktı olarak 3 alırız.
Console.WriteLine("sizeof array of bytes: "+Marshal.SizeOf(typeof(struct1)));
Ancak boolean array 12 byte büyüklüğündedir
[StructLayout(LayoutKind.Sequential, Pack = 4)]
struct struct2
{
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public bool[] values;
}
Çıktı olarak 12 alırız.
Console.WriteLine("sizeof array of bools: " + Marshal.SizeOf(typeof(struct2)));

UnmanagedType çeşitleri
Örnek:

ByValTStr
C tarzı karakter arrayleri için kullanılır.

ByValArray
Primitive bir tip array'i tanımlamak için kullanılır.


Memory Mapped File

Giriş
Bu yazı ile ilgili olarak Segmentation Fault ve IPC (Inter Process Communication) başlıklı yazıları okuyabilirsiniz.

Memory Mapped I/O genellikle iki iş için kullanılır. Bunları Memory Mapped File ve Memory Mapped I/O olarak ayırdım. Aslında her ikisi de bir dosyaya yazmak gibi düşünülebilir ancak anlam olarak farklı olduklarını düşündüğüm için iki kısımda inceledim.

Memory Mapped File
Memory Mapped File ile çalışırken açılan dosya byte array gibi kullanılıyor. Dosya RAM'e sayfalar şeklinde yükleniyor.

Memory Mapped I/O ile Yapılan Değişikliklerin Senkronizasyonu

Eğer mmap ile bir dosya açılmış ise, RAM üzerinde yapılan değişiklikler mysnch() veya munmap() metodlarıdan birisi çağırılıncaya kadar diske aktarılmaz.

POSIX
Temel iki metod mmap() ve munmap()'tir.

mmap metodu
mmap ile Memory Mapped File yazısına taşıdım.

msynch metodu
msynch() ile kullanılabilen bazı parametreler aşağıdaki gibidir. mysnch() sadece değişmiş sayfaları diske aktarır.
MS_ASYNC : Performs asycnhronous write
MS_SYNC    : Performs sycnhronous write
MS_INVALIDATE : Invalidate cached data

munmap metodu
mmap'in sonucu olan bellek alanını ve büyüklüğünü parametre olarak geçeriz. Şöyle yaparız
munmap(buf, fs.st_size);

Java
Java ile bu kadar detayı bilmeye de gerek yok. Aşağıdakine benzer bir kod parçası ile çok büyük bir dosya yaratılabilir. Kullanılan önemli sınıflar RandomAccessFile ve FileChannel. Buffer sınıfları olarak ta ByteBuffer veya MappedByteBuffer kullanılabiliyor. ByteBuffer zaten abstract bir sınıf ! Bu yüzden ByteBuffer.allocate() ve ByteBuffer.allocateDirect() metodları var.Örnek:


Write için örnek:
static int length = 0x8FFFFFF; // 128 Mb
FileChannel
channel = new RandomAccessFile("test.dat", "rw").getChannel();
ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, length );
for (int index=0;index< lenght; index++)
buffer .put (1)
Read için örnek:

How to read files quickly sayfasından aldığım bir başka örnek ise aşağıda. Aşağıdaki kod parçası tüm dosyayı yüklüyor. Burada dikkat edilmesi gereken nokta Android gibi platformlarda çok büyük dosyaları okumamak.

FileInputStream f = new FileInputStream( name );
FileChannel ch = f.getChannel( );
MappedByteBuffer mb = ch.map( ch.MapMode.READ_ONLY, 0L, ch.size( ) );
byte[] barray = new byte[SIZE];
long checkSum = 0L;
int nGet;
while( mb.hasRemaining( ) )
{
    nGet = Math.min( mb.remaining( ), SIZE );
    mb.get( barray, 0, nGet );
    for ( int i=0; i<nGet; i++ )
        checkSum += barray[i];
}
Boost
mapped_file_source sınıfı ile salt okunur erişim sağlamak mümkün. Portable Large File Access By Memory Mapped I/O in C++ Using Boost sorusunda çok büyük dosyaları, tamamen hafızaya yüklemeden okuma örneği verilmiş.

C#
MemoryMappedFile Sınıfı yazısına taşıdım.

Memory Mapped I/O (Hafızaya eşlenmiş giriş/çıkış)

Bazen bir cihaz (peripheral) ile haberleşmek için de kullanıldığı olur. Özellikle Linux'ta device driver kernelspace'te çalıştığı için userspace'te çalışan uygulamalarla haberleşmek için bu yöntemi kullanabilir. Örneğin sabit bir hafıza alanı belli bir cihaza tahsis edilir ve cihaz/sürücü bu alanı dinleyerek verilen komut ve verileri çalıştırır. How does the OS interact with peripherals like sound cards/ video cards etc sorusunda benzer bir cevabı görebilirsiniz. memory mapped i/0 working in linux sorusunda da anlatıldığı gibi I/O bus'ın hızının düşük olması (örneğin hızlı grafikler için yetersiz kalabilir) veya CPU mimarisinin yeterince pin'e sahip olmaması (microcontroller CPU'larda az sayıda pin bacağı bulur) gibi sebepler de Memory Mapped I/O yapılmasını tetikleyen etkenlerdir. Bu işlem için mmap metodunu MAP_FIXED parametresi ile kullanmak gerekir.Aşağıdaki örnekte /dev/mem'in başladığı adresten sonra gelen 0x48000000 offsetinden başlayarak  0x10000 uzunluğundaki hafıza alanı bizim uygulamamıza getiriliyor.
MAP_FIXED ile dikkat edilmesi gereken nokta, verilen adresin page size değerinin tam katı olması.


Bir başka örnekte mmap'e geçilmesi gereken en büyük bellek alanının (2. parametre) kernel içinde ayrıldığı için bir üst sınır olduğu belirtilmiş ve değerinin ne olacağı gösterilmiş.

Memory Mapped Mutex
Aşağıdaki örnekte fork() ile ikiye ayrılan uygulamaların aynı mutex'i kullanması gösterilmiş.

MAP_ANONYMOUS ile file descriptor kullanılmamış. Bu bayrak POSIX standardında tanımlı bir parçası değil!


6 Mart 2016 Pazar

Posix Eşzamanlılık Yapıları - Barrier

Giriş
Bariyerler bir thread grubunun bir aşamadan diğerine geçerken birbirlerini beklemelerini sağlar. Bariyeri geçen her thread bloke olur ve diğerlerinin de gelmesini bekler. Tüm threadler bariyeri geçince hepsi tekrar serbest kalır ve çalışmaya devam ederler. Bariyerlerin Condition'lara göre tek dezavantajları wait() metodları için timeout verilememesi.

Barrier Nesnesi
Şöyle tanımlanır
pthread_barrier_t barrier;
pthread_barrier_destroy metodu
Örnek ver.

pthread_barrier_init metodu
Şöyle yaparız.
pthread_barrier_init(&barrier, NULL, N);
pthread_barrier_wait metodu
Şöyle yaparız.
pthread_barrier_wait(&barrier);


2 Mart 2016 Çarşamba

procfs ve tmpfs

tmpfs nedir?
tempfs bellekte yaratılan dosya sistemidir. Bilgisayar kapanınca tüm dosyalar silinir.

uname
uname - unix name anlamına geliyor - ile sistemin adı öğrenilebilir. Örnek:


#include <sys/utsname.h>

struct utsname name;

int result = uname(&name);
if (result == 0)
    printf("OS: %s\n", name.sysname);

procfs nedir?
procfs, linux tarafından sağlanan çeşitli dosya sistemlerinden bir başkası. En önemli görevi, kernel ile programlar arasında bilgi akışını sağlamak.

Procfs'ten Okuma
Bu dosya sistemini parse ederek okumak her zaman güvenilir olmayabilir. Eğer dosya sistemi tekrar tekrar okunacaksa, en güvenli yol dosyayı açma, okuma ve kapama şeklinde olmalı.

Procfs'e Yazma
Yazma işlemini şöyle yaparız.
#include<fcntl.h>

main()
{
    int fd=open("/proc/MY_PROC_FILE", O_RDWR);
    write(fd, "linux is awesome", 16);
    return 0;
}
Basit bir procfs modülü
Şöyle yaparız.
int open_callback(struct inode *p, struct file *q)
{
  ...
  return 0;
}

ssize_t write_callback(struct file *p, const char __user *buffer, size_t len,
                       loff_t *s)
{
  ...
  return 0;
}

static struct proc_dir_entry *my_proc_entry;

static struct file_operations fs={
    .open=open_callback,
    .read=read_callback,
    .write=write_callback,
    .release=release_callback
};

static int start(void)
{
  my_proc_entry=proc_create(file_name, 0, NULL, &fs);
  ...
  return 0;
}

static void stop(void)
{
    remove_proc_entry(file_name, NULL);
    printk(KERN_ALERT "proc module unregistered\n");
}

module_init(start);
module_exit(stop);
MODULE_LICENSE("GPL");



Sistem Hakkında Bilgi Veren Dizinler

/proc/bus/usb
Bu dizin ile usb cihazlarının bilgisine erişmek mümkün. lsusb komutu bu dizini okuyor.

/proc/cpuinfo
İşlemcilerin bilgisine ulaşmak için cat /proc/cpuinfo komutunu çalıştırmak yeterli. lscpu komutu cat komutunun alternatifi olarak kullanılabilir.

Bu komutun çıktısı olan "stepping" güç tasarrufu için kullanılan ve işlemcinin frekansını değiştiren o anki katsayıyı gösterir. Intel işlemcilerinde SpeedStep yazısına bakabilirsiniz.

/proc/devices
Bu dizin ile yüklenmiş olan aygıt sürücülerini görmek mümkün. Aygıtlar karakter ve blok olanlar olmak üzere iki ana gruba ayrılıyorlar.

/proc/meminfo
/proc/meminfo yazısına taşıdım

/proc/net/wireless
Sinyal gücünü gösterir.

/proc/uptime
Sistemin ne kadar zamandır ayakta olduğunu gösterir. Uptime başlıklı yazıda uptime komutu ve /proc/uptime dosyası açıklanmış.

/proc/stats
Bu konuyu yazmak lazım.

/proc/timer_list ve /proc/timer_stats
Bu dizinde yaratılmış timer'ların bilgisine erişmek mümkün.

Uygulama Hakkında Bilgi Veren Dizinler

Bu dosya sisteminde kendi uygulamamıza ait bilgilere erişmek için /proc/self/... altındaki dizinlere erişmek gerekir. Eğer bir başka uygulamaya ait bilgilere erişmek istiyorsak /proc/$pid/... altındaki dizinlere erilmeli.

/proc/$pid/fd
File descriptor'lar hakkında bilgi verir. std out akımının /dev/null'a yönlendirildiğin şöyle anlarız.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

int main (void)
{
    char link[256];
    ssize_t rval;
    rval = readlink("/proc/self/fd/1", link, sizeof(link));
    link[rval] = '\0';

    if (!strcmp(link, "/dev/null")) {
        assert(!"Redirect to /dev/null not allowed!");
    } else {
        printf("All OK\n");
    }

    return 0;
}

/proc/$pid/exe
Çalışan exe'nin nerede olduğunu gösterir.

/proc/$pid/maps
Bu dizinde mmap ile uygulamanın adres alanına getirilmiş virtual memory ile ilgili bilgiler var.
Bu alanı okuyunca verilen sütunların açıklaması ise burada bulunabilir. Benzer bir açıklama burada.

/proc/$pid/smaps
/proc/$pid/maps'in biraz daha genişletilmiş hali. Burada Private_Dirty alanında sadece uygulama tarafından kullanılan, shared memory olmayan bellek miktarının gösterildiği anlatılmış.
7ffffffe7000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]
Size:                100 kB
Rss:                  20 kB
Pss:                  20 kB
Shared_Clean:          0 kB
Shared_Dirty:          0 kB
Private_Clean:         0 kB
Private_Dirty:        20 kB
Referenced:           20 kB
Anonymous:            20 kB
AnonHugePages:         0 kB
Swap:                  0 kB
KernelPageSize:        4 kB
MMUPageSize:           4 kB

/proc$pid/status
Vm ile başlayan sütunlarda virtual memory ile ilgili bilgiler sunulur. VmPeak ve VmSize şimdiye kadar en çok ne kadar virtual memory kullanıldığı, ve şu an ne kadar kullanıldığı bilgisini kilobyte birimiyle gösterir.
~$ cat /proc/1782/status | grep Vm
  VmPeak:     935584 kB
  VmSize:     898484 kB
  VmLck:           0 kB
  VmHWM:      257608 kB
  VmRSS:      186812 kB
  VmData:     394328 kB
  VmStk:         220 kB
  VmExe:          44 kB
  VmLib:       61544 kB
  VmPTE:        1224 kB
  VmSwap:          0 kB