---
title: 'Süreçler Arası İletişim'
source: 'https://academia.sh/tr/kurslar/c-sistem-programlama/surecler-arasi-iletisim'
course: 'C ile Sistem Programlama'
language: tr
updated: '2026-10-21T05:02:45+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'boruya yazılan bir değerin ebeveyne bir kez ulaştığını ve ikinci okumanın 0 baytlık dosya sonuyla bittiğini gösterir'
    - 'MAP_SHARED ile kurulan bir eşlemin aynı sayfayı iki sürece birden gösterdiğini, borudaki aktarımla karşılaştırarak ayırt eder'
    - 'bir borunun ileti sınırı taşımadığını, kendi uzunluk önekiyle kurulan bir çözümü çalıştırarak ölçer'
---

# Süreçler Arası İletişim

Bir boru baytları bir kez tasir ve okunan her bayt akistan duser; mmap ile MAP_SHARED kurulan bir eslem ise ayni sayfayi iki surece birden gosterir ve bu ikisi ayni derste iki farkli sinifa duser.

Önceki ders bir sinyalin bıraktığı izi ölçtü: bir bayrak, ana akışa bir kez ulaşan bir
aktarım. Bu ders aynı sınıfı iki süreç arasında ölçüyor, ve karşısına kasıtlı olarak
farklı bir sınıf koyuyor. Bir **boru**, çocuğun yazdığı baytları ebeveyne taşır — okunan
her bayt akıştan düşer, bu **aktarım**dır. Bir **`mmap` paylaşımı** ise aynı bellek
sayfasını iki sürece birden gösterir — biri yazdığında öteki aynı anda görür, bu
**ortak**tır. Aynı derste iki sınıfın yan yana durması, aralarındaki farkı görünür
kılıyor.

## Boruya Yazılan Değer

`gozlem.h` ve `gozlem.c` değişmiyor. Bu dersin kendi dosyası `iletisim_olcumu.c`, dört
ölçümü sırayla koşturuyor: boru, paylaşılan eşlem, iki yönlü bir kanal, ve uzunluk
önekiyle ayrılan iki ileti.

```c
// gozlem.h — bir degisikligin kime ulastigini siniflayan kayit
#ifndef GOZLEM_H
#define GOZLEM_H

enum Sinif { KOPYA, ORTAK, AKTARIM, GOZLENMEYEN };

/* Bir degisikligi kaydeder: gordu, degisikligi yapmayan tarafin onu
   okuyup okuyamadigidir (1 ya da 0). */
void gozlem(const char *degisiklik, enum Sinif sinif, int gordu);

/* Sinif basina sayilari ve ulasan / ulasmayan toplamini basar. */
void gozlem_ozeti(void);

#endif
```

```c
// gozlem.c — sayilar yalniz bu birimde tutulur
#include <stdio.h>
#include "gozlem.h"

static const char *const adlar[] = { "kopya", "ortak", "aktarim", "gozlenmeyen" };
static int sayilar[4];
static int ulasan = 0;
static int ulasmayan = 0;

void gozlem(const char *degisiklik, enum Sinif sinif, int gordu) {
    /* ortak ve aktarim ulasir, kopya ve gozlenmeyen ulasmaz */
    int beklenen = (sinif == ORTAK || sinif == AKTARIM);
    sayilar[sinif]++;
    if (gordu) {
        ulasan++;
    } else {
        ulasmayan++;
    }
    printf("%-34s %-8s %s%s\n", degisiklik, gordu ? "gordu" : "gormedi",
           adlar[sinif], gordu == beklenen ? "" : "  TUTARSIZ");
}

void gozlem_ozeti(void) {
    printf("siniflar: kopya %d, ortak %d, aktarim %d, gozlenmeyen %d\n",
           sayilar[KOPYA], sayilar[ORTAK], sayilar[AKTARIM], sayilar[GOZLENMEYEN]);
    printf("ulasan %d, ulasmayan %d\n", ulasan, ulasmayan);
}
```

```c
// iletisim_olcumu.c — boru, paylasilan eslem ve iki yonlu kanal
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#include "gozlem.h"

/* 1. Boru: cocugun yazdigi deger ebeveyne bir kez ulasir, ikinci okuma dosya sonu verir */
static void boru_olcumu(void) {
    int uc[2];
    if (pipe(uc) != 0) {
        return;
    }
    fflush(stdout);
    pid_t p = fork();
    if (p == 0) {
        int deger = 42;
        close(uc[0]);
        ssize_t n = write(uc[1], &deger, sizeof deger);
        close(uc[1]);
        _exit(n == (ssize_t)sizeof deger ? 0 : 1);
    }
    close(uc[1]);
    int okunan = 0;
    ssize_t birinci = read(uc[0], &okunan, sizeof okunan);
    waitpid(p, NULL, 0);
    char kalan;
    ssize_t ikinci = read(uc[0], &kalan, 1);
    close(uc[0]);
    gozlem("boruya yazilan deger", AKTARIM,
           birinci == (ssize_t)sizeof okunan && okunan == 42 && ikinci == 0);
}

/* 2. Paylasilan eslem: cocugun yazdigi sayfayi ebeveyn ayni anda gorur */
static void eslem_olcumu(void) {
    int *pay = mmap(NULL, sizeof *pay, PROT_READ | PROT_WRITE,
                     MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (pay == MAP_FAILED) {
        return;
    }
    *pay = 0;
    fflush(stdout);
    pid_t p = fork();
    if (p == 0) {
        *pay = 99;
        _exit(0);
    }
    waitpid(p, NULL, 0);
    gozlem("cocugun yazdigi paylasilan deger", ORTAK, *pay == 99);
    munmap(pay, sizeof *pay);
}

/* 3. Iki yonlu kanal: bir uctan yazilan deger oteki uctan okunur */
static void kanal_olcumu(void) {
    int uc[2];
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, uc) != 0) {
        return;
    }
    fflush(stdout);
    pid_t p = fork();
    if (p == 0) {
        close(uc[0]);
        int deger = 7;
        write(uc[1], &deger, sizeof deger);
        int yanit = 0;
        read(uc[1], &yanit, sizeof yanit);
        _exit(yanit == 14 ? 0 : 1);
    }
    close(uc[1]);
    int gelen = 0;
    read(uc[0], &gelen, sizeof gelen);
    int yanit = gelen * 2;
    write(uc[0], &yanit, sizeof yanit);
    int durum = 0;
    waitpid(p, &durum, 0);
    close(uc[0]);
    gozlem("kanaldan gelen yanit", AKTARIM,
           gelen == 7 && WIFEXITED(durum) && WEXITSTATUS(durum) == 0);
}

/* 4. Uzunluk onekiyle ileti siniri: bir borudan gelen iki mesaj birbirine karismadan ayrilir */
static ssize_t tam_oku(int fd, void *arabellek, size_t sayi) {
    size_t okunan = 0;
    while (okunan < sayi) {
        ssize_t n = read(fd, (char *)arabellek + okunan, sayi - okunan);
        if (n <= 0) {
            return n;
        }
        okunan += (size_t)n;
    }
    return (ssize_t)okunan;
}

static void onekli_yaz(int fd, const char *metin) {
    unsigned char uzunluk = (unsigned char)strlen(metin);
    write(fd, &uzunluk, 1);
    write(fd, metin, uzunluk);
}

static void onek_olcumu(void) {
    int uc[2];
    if (pipe(uc) != 0) {
        return;
    }
    fflush(stdout);
    pid_t p = fork();
    if (p == 0) {
        close(uc[0]);
        onekli_yaz(uc[1], "ilk");
        onekli_yaz(uc[1], "ikincimesaj");
        close(uc[1]);
        _exit(0);
    }
    close(uc[1]);

    unsigned char uzunluk1 = 0;
    tam_oku(uc[0], &uzunluk1, 1);
    char birinci[32];
    tam_oku(uc[0], birinci, uzunluk1);
    birinci[uzunluk1] = '\0';

    unsigned char uzunluk2 = 0;
    tam_oku(uc[0], &uzunluk2, 1);
    char ikinci[32];
    tam_oku(uc[0], ikinci, uzunluk2);
    ikinci[uzunluk2] = '\0';

    waitpid(p, NULL, 0);
    close(uc[0]);
    gozlem("onekli iki mesajin dogru ayrilmasi", AKTARIM,
           strcmp(birinci, "ilk") == 0 && strcmp(ikinci, "ikincimesaj") == 0);
}

int main(void) {
    boru_olcumu();
    eslem_olcumu();
    kanal_olcumu();
    onek_olcumu();
    gozlem_ozeti();
    return 0;
}
```

```text
boruya yazilan deger               gordu    aktarim
cocugun yazdigi paylasilan deger   gordu    ortak
kanaldan gelen yanit               gordu    aktarim
onekli iki mesajin dogru ayrilmasi gordu    aktarim
siniflar: kopya 0, ortak 1, aktarim 3, gozlenmeyen 0
ulasan 4, ulasmayan 0
```

**SA12.** Dört satır da `gordu` diyor, ama ikinci satırın sınıfı ötekilerden farklı.
Çocuk süreç boruya `42` yazıyor, ebeveyn `read` ile bunu okuyor — bu bir **aktarım**:
bayt akıştan düşüyor, ikinci `read` çağrısı artık okunacak bir şey kalmadığı için `0`
dönüyor, yani dosya sonu. Bu `0` dönüşü bir hata değil, bir **durum**dur: `read`in
dönüş değeri normalde okunan bayt sayısıdır, negatifse hata, sıfırsa "yazma ucu
kapandı ve arabellekte artık okunacak veri yok" demektir. `boru_olcumu` çocuk tarafında
`close(uc[1])` çağırıyor, sonra `_exit` ediyor — yazma ucunun son kopyası da kapanınca
çekirdek okuma ucuna "artık yeni veri gelmeyecek" bilgisini işte bu `0` dönüşüyle
taşıyor. Eğer çocuk yazma ucunu kapatmasaydı, ikinci `read` veri gelene kadar
bloke olurdu — `0` dönüşü veri yokluğunun değil, **tüm yazıcıların ayrıldığının**
işareti.

Ama `eslem_olcumu` farklı çalışıyor: `mmap(..., MAP_SHARED |
MAP_ANONYMOUS, ...)` çağrısı `fork`tan önce bir bellek sayfası ayırıyor ve bu sayfayı
**paylaşılan** olarak işaretliyor. `fork` süreç belleğini kopyalarken bu özel eşlemi
kopyalamıyor — ebeveyn ve çocuk, aynı fiziksel sayfayı gösteren iki ayrı işaretçiye
sahip oluyor. Çocuk `*pay = 99` yazdığında, ebeveyn `waitpid`den sonra kendi
`*pay`ına baktığında da `99` görüyor — çünkü ikisi de aynı sayfaya bakıyor. Bu **ortak**
sınıfın tanımı: bir kayıt gibi bir kez taşınmıyor, sürekli paylaşılıyor. Burada "dosya
sonu" kavramının hiçbir karşılığı yok: bir bellek sayfasının "tükenmesi" diye bir şey
yok, sayfa süreçlerden biri `munmap` edene ya da süreç sonlanana kadar orada duruyor.

## Boru mu, Paylaşılan Bellek mi

**SA13.** Bu iki düzeneğin görünürlüğü aynı kelimeyle anlatılabilir — "çocuğun yazdığını
ebeveyn görüyor" — ama altındaki mekanizma tamamen farklı. Boru, çekirdeğin tuttuğu bir
arabellektir; yazma ve okuma birer sistem çağrısı, ve okunan veri arabellekten silinir.
İki süreç asla aynı belleğe bakmaz, çekirdek aradaki taşıyıcıdır. `MAP_SHARED` ile
kurulan eşlem ise gerçekten aynı fiziksel sayfayı gösterir; okuma ve yazma sıradan
bellek erişimidir, sistem çağrısı gerekmez, ve veri hiçbir zaman "tükenmez" — sayfa
her okunduğunda orada kalır. Bu yüzden borunun ikinci okuması dosya sonu verirken,
paylaşılan sayfanın ikinci okuması hâlâ aynı değeri verir; `eslem_olcumu` bunu
denemiyor çünkü fark zaten birinci okumada görünüyor.

Bu karşıtlığın pratik sonucu, hangi düzeneğin ne zaman seçileceğidir. Boru sıralı,
tek yönlü bir akış taşıdığı ve çekirdek tarafından senkronize edildiği için
"kimin ne zaman ne gönderdiği" sorusuna doğal bir cevap verir — okuyan taraf
veri gelene kadar bloke olur, yarışa girmez. `MAP_SHARED` ise senkronizasyonu
**vermez**: iki süreç aynı sayfaya aynı anda yazarsa sonuç hangi yazmanın önce
göründüğüne bağlıdır ve bu ders böyle bir çakışmayı kurmuyor, yalnız tek yönlü
bir yazma-sonra-oku sırası kullanıyor. Paylaşılan bellek, sırayı değil yalnız
görünürlüğü veriyor — kilitleme ayrı bir sorumluluk, bu konunun son
dersinde iş parçacıkları için ayrıca ele alınıyor.

`kanal_olcumu`, `socketpair` ile kurulan iki yönlü bir kanalı bir kez gösteriyor. Bu
kanal da bir boru gibi çekirdek üzerinden akan, tükenen bir taşıyıcı — aradaki fark
tek yönlü değil çift yönlü olması: aynı iki uçtan hem yazılabiliyor hem okunabiliyor.
Ders burada ağ soketi açmıyor; `AF_UNIX` yerel bir uç noktadır, adres ya da bağlantı
noktası hiç yazdırılmıyor. Çocuk bir değer gönderiyor, ebeveyn onu ikiyle çarpıp geri
yolluyor, çocuk yanıtı okuyup çıkış koduna yazıyor — bu da **aktarım**, çünkü kanal
üzerinden akan her değer bir kez okunup tüketiliyor. `pipe` ile `socketpair` arasındaki
fark yön sayısıdır, sınıf değil: `pipe` tek yönlü bir dosya tanıtıcı çifti verir,
`socketpair` her iki tanıtıcıdan da hem okunup hem yazılabilen tam çift yönlü bir
kanal verir — ikisi de aynı çekirdek arabellekleme kuralına, aynı tükenme davranışına
tabidir.

Bu tabiiyetin somut bir sonucu var: `socketpair`ın iki ucu birbirinden bağımsız iki
tanıtıcı gibi görünse de, her iki tanıtıcının da yazma tarafı kapanmadan öteki uç
dosya sonu görmez — tıpkı `pipe`da olduğu gibi. Fark yalnızca kimin kime yazabildiği:
`pipe`da bir uç yalnız yazabilir, öteki yalnız okuyabilir; `socketpair`da her iki
uç da her iki yönde kullanılabilir. Bu yüzden `kanal_olcumu` çocuğun kanalı hangi
sırayla kapattığı konusunda dikkatli olmalı — kendi tanıtıcısını yanıtı gönderdikten
sonra kapatmazsa, ebeveyn tarafındaki okuma sonsuza kadar bekleyebilirdi, çünkü
çekirdek hâlâ açık bir yazma ucu görür ve dosya sonu üretmez. `onek_olcumu`daki
`onekli_yaz` çağrılarının ardından `close(uc[1])` çağrılması da aynı kuralın bir
başka görünümü: ebeveyn tarafı iki iletiyi okuduktan sonra beklemeye devam etmiyor,
çünkü çocuk kendi yazma ucunu açıkça kapatıyor.

Bu üç düzenek arasındaki seçim, veri akışının şekline bağlı. Tek yönlü ve sıralı
bir akış için `pipe` en basit araç; hem gönderme hem alma yönü aynı iki uçtan
gerekiyorsa `socketpair` fazladan bir kanal açmayı önlüyor; büyük ya da sık
paylaşılan veri için kopyalama maliyetini tamamen ortadan kaldırmak gerekiyorsa
`MAP_SHARED` devreye giriyor, ama o zaman senkronizasyon sorumluluğu tasarımcıya
kalıyor.

## Sınırlayıcı Ölçüm

**SA14.** Bir boru bir **akıştır**, ileti sınırı taşımaz. Önceki üç ölçümdeki her
`write` çağrısı tek bir `read` ile karşılandı, ama bu garanti genel değil: iki ayrı
`write` çağrısı bir tek `read` ile birlikte gelebilir, ya da bir `write`ın verisi
birden çok `read` çağrısına bölünebilir. Boru, gönderilen baytları bir kuyrukta tutar
ve nereye bir iletinin bittiğini, ötekinin başladığını bilmez — bu bilgiyi taşıyan
hiçbir şey yoktur.

**SA15.** `onek_olcumu` bu sınırı gerçekten kuruyor ve gerçekten sınıyor. Çocuk süreç aynı
boruya art arda iki ileti yazıyor — `"ilk"` ve `"ikincimesaj"` — ama ham metni değil,
`onekli_yaz` her iletinin önüne tek baytlık bir uzunluk alanı koyuyor. Ebeveyn tarafı
bu iki iletiyi tek bir `read` çağrısıyla değil, `tam_oku` yardımcı işleviyle okuyor:
önce uzunluk baytını, sonra tam olarak o kadar veri baytını. `tam_oku`nun kendisi de
bir garantiyi telafi ediyor — tek bir `read` çağrısı istenen baytların hepsini
getirmeyebilir, bu yüzden işlev istenen sayıya ulaşana kadar döngüde okumaya devam
ediyor. İki iletinin baytları aynı boruda art arda, aralarında hiçbir ayraç olmadan
akıyor; okuyan tarafı doğru yerden kesen tek şey, her iletinin kendi taşıdığı uzunluk
bilgisi. Ölçüm `"ilk"` ve `"ikincimesaj"`ın karışmadan, doğru sınırlarla geri
kurulduğunu doğruluyor — bu ders üç iletiyi tek bir okumada gösterdiği için kendi
başına bir ileti sınırı çizmiyordu, ama `onek_olcumu` artık çiziyor: basılan yalnız
yeniden kurulmuş iletiler, boruya ham baytlar olarak değil.

`onekli_yaz` uzunluğu `unsigned char` olarak taşıyor, yani tek baytlık bir alan —
en çok 255 baytlık bir iletiyi tanımlayabiliyor. Bu ölçüm için yeterli, çünkü
`"ikincimesaj"` bu sınırın çok altında; ama gerçek bir protokolde ileti büyüklüğü
öngörülemezse tek bayt yetmez, iki ya da dört baytlık bir alan gerekir ve o zaman
bayt sırası (`endianness`) sorusu araya girer — gönderen ve alan makine aynı sırayla
mı yazıp okuyor. Bu ders o soruyu açmıyor, çünkü boru her iki ucu da aynı makinede
çalıştırıyor; ağ üzerinden konuşan bir protokolde uzunluk alanının sırası açıkça
sabitlenmeli, aksi hâlde bir uçta doğru okunan sayı öteki uçta yanlış çıkar.
Önemli olan kural değişmiyor: uzunluk alanı ne kadar büyük olursa olsun, onu taşıyan
düzenek aynı — önce ne kadar veri geleceğini oku, sonra tam o kadarını oku.

`tam_oku`nun döngüsü kozmetik değil, boru semantiğinin doğrudan sonucu. Çekirdek
bir `write` çağrısındaki baytları tek bir birim olarak teslim etmeyi garanti etmez;
okuyan taraf `read` çağırdığında çekirdek o an boru arabelleğinde ne kadar bayt
birikmişse onu döndürür, istenenden azını döndürebilir. `boru_olcumu` ve
`eslem_olcumu` ölçümlerinde bu hiç görünmedi, çünkü yazılan değerler birkaç bayt
uzunluğundaydı ve tek `read` çağrısı hep yetti; ama genel kural bu değil. `tam_oku`
istenen sayıya ulaşılana kadar okumaya devam ederek bu garantisizliği bir kez, tek
bir yerde telafi ediyor — çağıran taraf artık "kaç kez `read` gerekti" sorusuyla
uğraşmıyor, `tam_oku`nun kendisi kısa okumaları görünmez kılıyor. `n <= 0` dönen
anı ayrıca kontrol ediyor, çünkü `0` boru kapandığını, negatif değer ise bir hatayı
gösteriyor; ikisi de "tam sayıda bayt bekleniyordu ama gelmedi" durumudur ve
döngü bu durumda sonsuza kadar dönmek yerine hemen çıkıyor.

## Özet

- Bu ders iki farklı sınıfı yan yana koyuyor: boru **aktarım**dır, `MAP_SHARED` eşlemi
  **ortak**tır — ikisi de "çocuğun yazdığını ebeveyn görür" cümlesiyle anlatılsa da
  mekanizmaları ayrı.
- Boru çekirdek üzerinden akan, okunduğunda tükenen bir taşıyıcıdır; ikinci okuma
  `0` bayt döner, çünkü tüm yazma uçları kapanmıştır — bu veri yokluğu değil,
  yazıcıların ayrıldığının işaretidir.
- `MAP_SHARED | MAP_ANONYMOUS` ile kurulan bir eşlem aynı fiziksel sayfayı iki sürece
  birden gösterir; bir taraf yazdığında öteki taraf aynı sayfayı okur, veri tükenmez —
  ama sıralamayı değil yalnız görünürlüğü verir, kilitleme ayrı bir sorumluluktur.
- `socketpair` iki yönlü bir kanal kurar; bu da bir aktarımdır, çünkü akan her değer
  bir kez okunup tüketilir. Ağ soketi açılmaz, adres ya da bağlantı noktası basılmaz.
- Bir boru ileti sınırı taşımaz; `onek_olcumu` bunu uzunluk önekiyle gerçekten kurup
  iki iletiyi karıştırmadan geri kurarak gösteriyor.

## Sonraki Adım

Bir sonraki ders — bu konunun sonuncusu — aynı süreç içinde kalıyor ama süreç yerine iş
parçacığına geçiyor: kilit altında yazılan bir sayaç, `join` sonrasında ana iş
parçacığına ulaşan bir **ortak** paylaşım.
