вторник, 30 июня 2015 г.

netstat

Выводит сетевые соединения, таблицы маршрутизации, статистику указанных сетевых интерфейсов, "маскарадируемые" соединения, и multicast memberships

Вывод открытых портов

# netstat -lptu
-l - (--listening)показать только слушающие порты
-p - (--programm)показать PID и имя программы, которой принадлежит открытый сокет
-t - (--tcp)только сокети семейста tcp
-u - (--udp)только сокеты семейста udp


# netstat -tulpn
-n - (--numeric) показать IP адресса, а не имена хостов, а также порты в виде номеров, а не названия протоколов, для которых данные номера считаются "стандартными"

Утилита, которой можно добиться того же:


# lsof -i
Утилита вывода списка открытых файлов
-i - вывести файлы, которых "интернет адресс" соответсвует указанному после ключа, если никакой не указан, то выводятся все Internet и x.25(HP-UX) сетевые файлы (снова можем убедиться в том, что Unix все в ОС считает файлом - даже открытое соединение на сокете). Можем "попросить" вывести все открытые сокеты по протоколы ip6 так: lsof -i6, или только для указанного списка айпишек: lsof -i6 fa:ce:b0:00:: 89:ce:: 

Общение узлов из подсети с глобальной сетью

Предоставляя узлам из подсети доступ к интернету мы сталкиваемся со следующимим проблемами:

1. Возможно мы можем себе позволить приобрести у ISP не такое количество IP адрессов, сколько у нас узлов(обычно это может быть только один).
2. Проблемы безопасности.
3. Разумная экономия на трафике.

пятница, 26 июня 2015 г.

Сказка о том как в JVM каждый обьект имеет свой собственнный монитор

По этому нам не нужно создавать самим мьютекс и с помощью его безопасить обьект разделяемый между потоками.

У меня был вопрос о том, что если мы обьект передаем в синхронизированный блок одного треда, и там внутри он начинает "ожидать", как другие треды к нему обратятся,  если они должны "проинформировать" - но все просто - когда на мониторе вызывается "ждать", блокировка синхронизированного блока отпускается; блокировка также отпускается, когда тред начинает "спать".

stop, suspend, resume are @deprecated. Но если нужно, что тогда?

Если такой функционал нужен, то его нужно реализовывать через флаги.
public class App extends Thread {

  static App self;

  private volatile boolean threadSuspended = false;

  public App(String name) {
    super(name);
  }

  public static void main(String[] args) {

    self = new App("WORKER");

    self.start();

    new Thread(new Runnable() {
      public void run() {
        try {
          Thread.sleep(5000);
          printFromThread("Let's suspend!");
          self._suspend();

          Thread.sleep(5000);
          printFromThread("Let's resume!");
          self._resume();

          Thread.sleep(15000);
          printFromThread("Let's stop!");
          self._stop();

          Thread.sleep(15000);
          printFromThread("I'm going to die...");

        } catch (InterruptedException e) {
          printFromThread("Interrupter is interrupted!!!))))))))))))))))))");
        }
      }
    }, "controller").start();
  }

  private static void printFromThread(String x) {
    System.out.println( "[ " + Thread.currentThread().getName() + " (" + System.nanoTime() + ") ]" + x);
  }

  public void run() {
    Thread thisThread = Thread.currentThread();
    while (self == thisThread) {
      try {
        printFromThread("I'm starting sleeping");
        Thread.sleep(1000);
        printFromThread("I've just finished sleeping");

        synchronized (this) {
          printFromThread("I'm just synchronized");
          while (threadSuspended && self == thisThread) {
            printFromThread("Let's start waiting iteration");
            this.wait();
            printFromThread("Good morning!.....");
          }
        }
      } catch (InterruptedException e) {
        printFromThread("Look's like I was awoken now!");
      }
      repaint();
    }
  }

  public void repaint() {
    printFromThread("<<<<<<<<<<<<<<<<<<< Repainting ... >>>>>>>>>>>>>>>>>>>>>>");
  }

  public synchronized void _suspend() {
    printFromThread("self was suspended.");
    threadSuspended = true;
  }

  public synchronized void _resume() {
    printFromThread("self was resumed");
    threadSuspended = false;
    notify();
  }

  public synchronized void _stop() {
    self = null;
    printFromThread("self was \"nulled\". So let's notify to break wait");
    notify();
  }
}

четверг, 25 июня 2015 г.

Thread Scheduler

В мультипроцессорной/ядерной системе thread scheduler отвечает за распределении процессорного времени между процессами.
JVM по-умолчанию пользуется услугами системного TS, линукс и виндоус работают по схеме преемптив мультизадачности.
У каждого процессора/ядра, есть свой планировщик, но для обобщения обычно говорят об одном.
Преемптив планировщик, карантирует, каким бы низким приоритетом не обладал поток и какие высокоприоритетные потоки не ожидали рядом с ним, он гарантированно получит доступ к выполнению.
Да, первыми идут на выполнение высокоприоритетные процессы, но все они работают не больше определенного в системе количества квантов времени - квант равняется нескольким тикам часов процессора/ядра. Если процесс отработал свой лимит он будет сохранен со своим контекстом(состоянием регистров и текущей точкой выполнения в стеке) и отправлен в конец(или в какое-то место другое - наверняка есть алгорит вычисления места в очереди на основании состояния ждущий и их приоритетов) очереди процессов на выполнение. И следующий процесс из очереди получит доступ к процессору/ядру.

У процессов есть неслько состояний: раннабл, вейтинг, нью - вейтинг процесс не получит доступ даже если подошла его очередь - он ожидает какое-то событие и это будет холостое переключение контекста.
События могут менять приоритет в очереди - если например пришло I/O событие, что запрошенные байты были прочитаны с переферии и могут быть получена с этого момент с памяти в регистры - так процесс пробудится в ближайший момент, но его приоритет резко увеличивается и он уже находится среди высокоприоритетных тредов.

Переключение контекста очень дорогая штука(в сравнение с временем в котором живет процессор) - поэтому нужно акуратно проектировать взаимодействие между процессами и уходить от частого переключение контекста. 

среда, 24 июня 2015 г.

Subroutine, Coroutine, Generator

Subroutine - блок инструкций, решающие определенную задачу, запакованы в некую пакетную единицу. Примеры: процедуры, функции, методы, рутины. Субрутины могут располагать только одним програмным стеком при старте приложения.
Coroutine - сабрутина для кооперативной многопоточности, которая передает управляемость другому потоку для выполнения своих инструкций, при этом поточный поток подвисает. Тут мы уже не можем обойтись одним стеком приходится использовать continuations , которые могут выделять в памяти дополнительные стеки, и обычно реализованы в высокоуровневых языках программирования со сборщиком муссора.
Generator - также как и корутина передает управление другому потоку, только вот с того потока программа обречена вернуться обратно, откуда его вызвали через генератор, механизм корутины позволяет перенаправить программу в любом направлении.

Fibers

Fibers - это вид потоков, которые работают в юзерспейсе виртуальной памяти, разделяя одну и туже память между собой, но они не работают паралельно.
Их основное отличие от зеленых потоков в том, что они сами "уступают" процессорный ресурс другим "страждущим" потокам.
При этом они работают в одном системном треде, по этому здесь нет реальной параллельности(да и предаставлены они в языках программирования как последовательные структуры в более читабельном виде).
Но они очень легковесные и переключение между ними быстрее чем у зеленых потоков. Это представители кооперативной многопоточности.

Файберы реализуют ту самую концепцию, что и корутины. Единственное, что можно назвать отличием между ними - это то, что корутины конструкция уровня языка, а файберы - системы.

Плюс такого подхода, что мы уходим от проблем синхронизации.
Минус - что мы не пользуемся на полную мощь ресурсом параллельной системы.
Но подход маппинга платформенной многопоточности на системную N:M с использование файберов пока зывает себя очень хорошо в сравнении с чистый упред

четверг, 18 июня 2015 г.

Программная транзакционная память(SТМ)

Программная транзакционная память(software transactional memory, SТМ) - концепция, которая переносит идею транзакции в межпоточное взаимодействие.
Потоки работают с копией данных из памяти, не блокирую таким образом друг друга, при завершении транзакции происходит коммит, что означает, что все изменения вносятся в журнал виртуальной машины, если не происходит кофликтов в имененном состоянии памяти, то изменения фиксируются в памяти. В случае конфликта изменения откатывается и виртуальная машина выполняет снова траназкционный код с новым состоянием памяти и опять пытается сделать коммит.

На практике такой подход намного концептуально сильнее подхода локов, но приносит дополнительные расходы на виртуальную машину - журнал, повторные попытки и т.д.

Часто в небольших приложениях программы с локами работают быстрее.

Но SТМ решает некоторые задачи, которые не может решить лок, а также программы в такой конецпции намного проще и понятние.

В принцпипе найхудшая оценка алгоритмики подхода = O(n) при n - количество потоков.


SТМ реализован в ядре Clojure.

Модели паралельного программирования в разных языках

C/C++ - нативный подход, используются механизмы многопоточности предоставляемые ядром.

JDK     - по-умолчанию потоки виртуальной машины мапятся  на нативные потоки ОС 1:1, но можно переключить JVM на зеленые потоки - реализацию JDK, которая работатет по факту только в одном системном потоке по-очередно переключаясь между потоками виртуальной машины, симулируя многопоточность, иногда за счет економии на переключениях на уровне ядра, это может быть оправдано.

Python - (CPython) хоть и используется манипулирование нативными потоками, но в этот момент происходит GIL(Global Interpreter Lock), что означает, что в одно и тоже время, только один поток может использовать интерпретатор питона для выполнения его команд, при истечении лимита команд на один поток, происходит переключение на другой. Поэтому паралельной работы в одно и тоже время не происходит. В питон подключаются библиотеки Си, они чаще всего потоконебезопасны, поэтому подход с GIL пытается это обезопасить. В рамках проекта PyPy (в котором компилятор питона пишется на самом питоне) пытаются реализовать модель "Транзакционной памяти"(Software Transactional Memory, SТМ). На данный момент даже в многопоточных вычислениях интерпретатор с STM работает в разы медленней, чем с GIL. Но за счёт JIT PyPy-STM всё равно быстрее, чем CPython.

Erlang - реализут в многопоточности модель "ничего общего"(shared nothing), используются микропроцессы(юзерспес виртуальной памяти), а не потоки, что очень быстро.


четверг, 11 июня 2015 г.

Виртуальная память

В современных ОС конецепция, при которой каждому процессу память компьютера представляется полностью доступной для его нужд (как будто других процессов, ядра и вообще ОС не существует) и ее размер равен максимум для данной архитектуры(реальный физический размер не обязан быть максимуммом).

При этом память разбита постранично(определенными секторами), ОС ведет статистику использования каждой страницы(это мое виденье, точно я в этом не уверен) и в случае нехватки физической памяти, сливает страницы разных процессов с низкой статистикой на сторидж-устройство.


"Общая виртуальная память" компьютера разделяется на kernel space и user space(userland). В первой части находятся ядро, его расширения и некоторые драйверы устройств, в юзерспейсе находятся библиотеки, приложения и некоторые драйверы. Это повышает отказоустойчивость и безопасность ОС.

Процессы приложений видят свою виртуальную память и не имеют доступа к чужой( при этом, если позволяют права, некоторые процессы могут запросить у ядра доступ к участку памяти другого(чужого) процесса - например debuggers). Также они могут запросить разделенную память с другими процессами у ядра, хотя можно использовать и другие техники межпроцессного взаимодействия - пайпы и сокеты.




суббота, 6 июня 2015 г.

Чему равен размер ссылки в языке программирования?

Размер ссылки всегда равен раздядности регистров процессора (размеру машинного слова).

Это потому, что процессору может адрессоваться к памяти не выше этого числа.
В 32битной архитектуре машинное слово = 4 байта( 8 х 4 = 32).
В 64битной - 8 байт (8 х 8 = 64).


В C/C++ есть тип с названием слово - WORD - его размер 2 байта, это потому что язык был реализован на момент, когда технологический уровень был на уровне 16битной архитектуры, поэтому этот тип в C/C++ исторически сохраняет размерность 2-х байт.