|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
0. Создаем основной каталог mkdir -p /opt/docker-3xui cd /opt/docker-3xui 1. Создаем файл копоза cat > docker-compose.yml << "EOF" services: 3xui: image: ghcr.io/mhsanaei/3x-ui:latest container_name: 3xui_app network_mode: host cap_add: - NET_ADMIN - NET_RAW volumes: - ./db/:/etc/x-ui/ - ./cert/:/root/cert/ environment: XRAY_VMESS_AEAD_FORCED: "false" XUI_ENABLE_FAIL2BAN: "true" XUI_PORT: "8080" # web interface tty: true restart: unless-stopped EOF Где: XUI_PORT: "8080" # web interface - если не задать то будет порт 2053 network_mode: host: Контейнер будет использовать сетевой стек сервера напрямую. cap_add: Права NET_ADMIN и NET_RAW необходимы для работы встроенного в 3x-ui Fail2ban. volumes: Мы монтируем папки db и cert из текущего каталога в контейнер. 2. Запускаем docker compose up -d 3. После запуска панель будет доступна по адресу http://<IP-твоего-сервера>:2053 Смотрим id: docker ps Смотрим в логи: docker compose logs docker logs "id_3xui" # Замени my_new_admin и MyStr0ngP@ssw0rd на свои значения docker exec -it 3xui_app x-ui setting -username my_new_admin -password "MyStr0ngP@ssw0rd" # Перезапусти панель, чтобы изменения применились docker exec -it 3xui_app x-ui restart # Всегда можно вызвать скрипт настройки docker exec -it 3xui_app x-ui |
Рубрика: devops
info
|
1 |
Суть создать контейнер и в нем собрать образ любой фигни из гита. |
Полная очистка (если пересобираем с нуля)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Выйти из контейнера, если сидишь в нём exit cd /opt/build-linux-system docker compose down docker rmi my-builder docker rm -f armbian-ophub #docker image prune -a #удаляет вообще все образы #docker container prune Выходим из каталога: cd Удаляем все связанное с нашей сборкой: sudo rm -rf /opt/build-linux-system |
Пример:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
0. Создаем каталог mkdir /opt/build-linux-system cd /opt/build-linux-system 1. Создаем файлы конфигов докеров и композа cat > Dockerfile << "EOF" FROM debian:bookworm ENV DEBIAN_FRONTEND=noninteractive # 1. Сначала ставим ca-certificates, чтобы работали защищенные соединения RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/* # 2. Добавляем КОРРЕКТНЫЙ репозиторий testing и ставим свежий qemu-user-static RUN echo "deb http://deb.debian.org/debian testing main" > /etc/apt/sources.list.d/testing.list && \ apt-get update && apt-get install -y --no-install-recommends -t testing qemu-user-static && \ rm /etc/apt/sources.list.d/testing.list && \ rm -rf /var/lib/apt/lists/* # 3. Устанавливаем все остальные пакеты из bookworm RUN apt-get update && apt-get install -y --no-install-recommends \ git curl wget sudo bc build-essential bison flex \ libssl-dev libncurses-dev device-tree-compiler \ python3 python3-pip python3-setuptools swig libpython3-dev \ rsync cpio kmod fakeroot debootstrap binfmt-support \ dosfstools parted gdisk util-linux e2fsprogs mtools u-boot-tools \ nano vim less tree htop procps uuid-runtime udev \ gcc-11 g++-11 \ && rm -rf /var/lib/apt/lists/* RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100 && \ update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 100 WORKDIR /work CMD ["bash"] EOF cat > docker-compose.yaml << "EOF" services: builder: build: . image: my-builder:latest container_name: builder privileged: true volumes: - ./work:/work - /dev:/dev working_dir: /work stdin_open: true tty: true command: bash EOF cat > .dockerignore << 'EOF' work/ output/ cache/ *.log EOF 2. Создаем директорию mkdir work 3. Включить qemu для ARM (один раз на хосте): docker run --privileged --rm tonistiigi/binfmt:latest --install all 3.1 Добавить луп устройства sudo modprobe loop max_loop=64 проверка: cat /proc/sys/fs/binfmt_misc/qemu-aarch64 # в строке flags должен быть F 4. Собрать образ (один раз и после правок Dockerfile) #docker compose build docker compose build --no-cache docker compose up -d 5. Зайти в контейнер (войти в VM): docker compose exec builder bash pwd # ожидаем /work ps aux | grep udev # udevd должен быть запущен ls -la /dev/loop* # loop-устройства видны В контейнере проверяем: cat /proc/sys/fs/binfmt_misc/qemu-aarch64 Если пусто то выполняем: mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc 6. Качаем репозиторий cd /work git clone -b napi-boards https://github.com/QwaSeeK/build.git cd build 7. Сборка: для напи2 (первый раз): ./compile.sh BOARD=napi2 BRANCH=current RELEASE=bookworm EXPERT=yes CPUTHREADS=2 #./compile.sh BOARD=napi2 BRANCH=current BUILD_DESKTOP=no BUILD_MINIMAL=no CPUTHREADS=2 EXPERT=yes KERNEL_CONFIGURE=no RELEASE=bookworm для napi2 (первый раз после сбоев): ./compile.sh BOARD=napi2 BRANCH=current RELEASE=bookworm EXPERT=yes CLEAN_LEVEL=make,dkms CPUTHREADS=2 для napi2 (последующие): ./compile.sh BOARD=napi2 BRANCH=current RELEASE=bookworm EXPERT=yes для napi-c: ./compile.sh BOARD=napi-c BRANCH=current RELEASE=bookworm EXPERT=yes 8. Для выхода на хост написать: exit 9. Готовый образ окажется на хосте в: ./work/build/output/ P.S. Проблема при зборке: # Сначала полностью сбросьте старые регистрации docker run --rm --privileged multiarch/qemu-user-static --reset -p yes # Попробуйте версию 7.0.0, которая считается стабильной (Если 7.0.0 не поможет, попробуйте 8.1.5) docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install arm64 docker run --privileged --rm tonistiigi/binfmt:qemu-v8.1.5 --install arm64 Проверка и уборка лишнего: docker compose ps # что запущено docker compose down # остановить docker system df # сколько места занято docker compose build --no-cache # Принудительно пересобрать без кэша Команды для изучения что можно собрать ядра/софт: Доступные борды napi: grep -r "KERNEL_TARGET" config/boards/ | grep -i napi | grep -v bana Что собирается: cat config/boards/napi-c.csc cat config/boards/napi2.csc Еще информации: cat config/sources/families/rk35xx.conf Очистка: cd /work/build rm -rf output/logs output/images cache/armbian # или CLEAN_LEVEL=make,dkms в команде |
Сборка с ядром 5.10.160
|
1 |
./compile.sh BOARD=rock-3a BRANCH=vendor RELEASE=bookworm EXPERT=yes CLEAN_LEVEL=make,dkms CPUTHREADS=2 USE_CCACHE=yes |
Docker wireguard
Установка Docker debian13:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
sudo apt-get update sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release Импортируем GPG-ключ для репозитория docker: curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg Добавим новый репозиторий в список apt: echo \ "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \ $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null Установим докер: apt-get update apt-get install docker-ce docker-ce-cli containerd.io Проверяем: docker run hello-world |
Пример установки wireguard
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# AmneziaWG 0. Созадем каталог и переходим в него mkdir /opt/docker-amneziawg cd /opt/docker-amneziawg 1. Создание композ файла IP_V4_ADDRESS_VPS=YOU_HOST_IP YOU_PASSWORD_WEB=enter_you_password PORT_WEB=50000 #UDP PORT_WG=60000 #TCP cat > docker-compose.yml << EOF services: amnezia-wg-easy: image: ghcr.io/spcfox/amnezia-wg-easy:latest container_name: amnezia-wg-easy restart: unless-stopped environment: - WG_HOST=$IP_V4_ADDRESS_VPS - PASSWORD=$YOU_PASSWORD_WEB - LANG=ru - PORT=$PORT_WEB - WG_PORT=$PORT_WG volumes: - ./wireguard:/etc/wireguard - /lib/modules:/lib/modules:ro ports: - "${PORT_WEB}:${PORT_WEB}/tcp" - "${PORT_WG}:${PORT_WG}/udp" cap_add: - NET_ADMIN - SYS_MODULE sysctls: - net.ipv4.conf.all.src_valid_mark=1 - net.ipv4.ip_forward=1 devices: - /dev/net/tun:/dev/net/tun EOF 2. Запуск docker compose up -d P.s Возможные проблемы: sudo modprobe iptable_nat |
Все тоже самое но без ната а сеть хоста напрямую:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#env IP_V4_ADDRESS_VPS=YOU_HOST_IP YOU_PASSWORD_WEB=enter_you_password PORT_WEB=50000 #UDP PORT_WG=60000 #TCP WG_INTERFACE_INET=ens1 cat > docker-compose.yml << EOF services: amnezia-wg-easy: image: ghcr.io/spcfox/amnezia-wg-easy:latest container_name: amnezia-wg-easy restart: unless-stopped network_mode: host environment: - WG_HOST=${IP_V4_ADDRESS_VPS} - PASSWORD=${YOU_PASSWORD_WEB} - LANGUAGE=ru - PORT=${PORT_WEB} - WG_PORT=${PORT_WG} - WG_INTERFACE=${WG_INTERFACE_INET} volumes: - ./wireguard:/etc/wireguard - /lib/modules:/lib/modules:ro cap_add: - NET_ADMIN - SYS_MODULE devices: - /dev/net/tun:/dev/net/tun EOF |
скриптец 0 bash / go
Пример на bash:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
#!!! Что делает. # Запускаеn run_command раз в секунду пока значения run_command_befor > run_command_befor_stop # Далее спим и ничего не делаем sleep_time # Когда проснулись начинаем выполнять команду run_command_2 пока значение run_command_after > run_command_after_stop # Тут происходит сбрасывание счетчиков и возращение к выполнению run_command # см переменные cat > 0.txt << "EOF" #!/bin/bash #LOG #set -xv #VAR export run_command_befor=0 export run_command_befor_stop=5 export sleep_time=2 export cicle_run=0 export run_command_after=0 export run_command_after_stop=5 export run_command="echo bu bu bu" export run_command_2=$run_command while : do $run_command sleep 1 ((run_command_befor++)) echo before sleep = $run_command_befor if ((run_command_befor > $run_command_befor_stop )); then ((cicle_run++)) echo ================================================================ me sleep $sleep_time sec sleep $sleep_time while : do $run_command_2 sleep 1 ((run_command_after++)) echo after sleep = $run_command_after if ((run_command_after > $run_command_after_stop)); then #exit 0 run_command_befor=0 run_command_after=0 echo =========================================================== run cicle command $cicle_run N break fi done fi done EOF bash 0.txt |
Сделать почти тоже самое на golang:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
0. Создаем папку: mkdir xrn cd xrn 1. создаем конфиг для xrn cat > config.conf << "EOF" # комментарии начинаются с решётки run_command=snmpwalk -n ctx_hades -v3 -a SHA1 -A authpassword -x AES128 -X privpassword -l authPriv -u snmpv3user 192.168.16.164 sysname run_command2= run_command_before_stop=5 run_command_after_stop=5 sleep_time_sec=2 step_sleep_sec=1 EOF 2. Создаем код программы: cat > main.go << EOF package main import ( "bufio" "flag" "fmt" "os" "os/exec" "strconv" "strings" "time" ) type Config struct { RunCommand string RunCommand2 string RunCommandBeforeStop int RunCommandAfterStop int SleepTimeSec int StepSleepSec int } func loadConfig(path string) (*Config, error) { f, err := os.Open(path) if err != nil { return nil, fmt.Errorf("open config: %w", err) } defer f.Close() cfg := &Config{ // значения по умолчанию RunCommandBeforeStop: 5, RunCommandAfterStop: 5, SleepTimeSec: 2, StepSleepSec: 1, } sc := bufio.NewScanner(f) for sc.Scan() { line := strings.TrimSpace(sc.Text()) if line == "" || strings.HasPrefix(line, "#") { continue // пропускаем пустые и комментарии } key, val, ok := strings.Cut(line, "=") if !ok { continue // строка без "=" — игнор } key = strings.TrimSpace(key) val = strings.TrimSpace(val) switch key { case "run_command": cfg.RunCommand = val case "run_command2": cfg.RunCommand2 = val case "run_command_before_stop": cfg.RunCommandBeforeStop, _ = strconv.Atoi(val) case "run_command_after_stop": cfg.RunCommandAfterStop, _ = strconv.Atoi(val) case "sleep_time_sec": cfg.SleepTimeSec, _ = strconv.Atoi(val) case "step_sleep_sec": cfg.StepSleepSec, _ = strconv.Atoi(val) } } if err := sc.Err(); err != nil { return nil, fmt.Errorf("scan config: %w", err) } if cfg.RunCommand2 == "" { cfg.RunCommand2 = cfg.RunCommand } return cfg, nil } // run и main — такие же, как в JSON-варианте, меняется только loadConfig. func run(cmdline string) { cmd := exec.Command("sh", "-c", cmdline) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin if err := cmd.Run(); err != nil { fmt.Fprintf(os.Stderr, "command error: %v\n", err) } } func main() { configPath := flag.String("config", "config.conf", "путь к конфиг-файлу") flag.Parse() cfg, err := loadConfig(*configPath) if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } stepSleep := time.Duration(cfg.StepSleepSec) * time.Second sleepTime := time.Duration(cfg.SleepTimeSec) * time.Second var ( runCommandBefore = 0 runCommandAfter = 0 cycleRun = 0 ) for { run(cfg.RunCommand) time.Sleep(stepSleep) runCommandBefore++ fmt.Printf("before sleep = %d\n", runCommandBefore) if runCommandBefore > cfg.RunCommandBeforeStop { cycleRun++ fmt.Printf("================================================================ me sleep %v sec\n", sleepTime) time.Sleep(sleepTime) for { run(cfg.RunCommand2) time.Sleep(stepSleep) runCommandAfter++ fmt.Printf("after sleep = %d\n", runCommandAfter) if runCommandAfter > cfg.RunCommandAfterStop { runCommandBefore = 0 runCommandAfter = 0 fmt.Printf("=========================================================== run cicle command %d N\n", cycleRun) break } } } } } EOF Делаем сборку: go mod init xrn #go get gopkg.in/yaml.v3 go build -o xrn . ./xrn -config config.yaml |
pyhton env / modbus
Установка на debian:
|
1 |
apt update && apt install python3-venv python3-full -y |
Создание и использование виртуального окружения:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Создайте виртуальное окружение (например, в папке myenv): python3 -m venv myenv Активируйте его: source myenv/bin/activate Установите библиотеку: pip install minimalmodbus Пере использование окружения: cd /root/myenv source /root/myenv/bin/activate Запуск скриптов: python read_modbus.py python 1.txt python 2.txt |
Примеры скриптов для modbus:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
cat > 1.txt << "EOF" import minimalmodbus import serial # Настройка порта instrument = minimalmodbus.Instrument('/dev/ttyUSB0', slaveaddress=1) instrument.serial.baudrate = 9600 instrument.serial.bytesize = 8 instrument.serial.parity = serial.PARITY_NONE instrument.serial.stopbits = 1 instrument.serial.timeout = 1.0 # Самая важная строчка для FTDI под Linux: # Драйвер очистит входной буфер от эха сразу после отправки запроса instrument.serial.flushInput() try: # Читаем 1 регистр (holding) с адреса 769 (в mbpoll это -r 769 -t 4) # minimalmodbus использует адресацию от 0, поэтому проверяем 769 или 768 в зависимости от карты регистров устройства value = instrument.read_register(769, functioncode=3) print(f"Значение регистра: {value}") except Exception as e: print(f"Ошибка чтения: {e}") EOF cat > 2.txt << "EOF" import serial import time # Настройки порта ser = serial.Serial( port='/dev/ttyUSB0', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=1.0 ) # Формируем запрос Modbus RTU (тот самый, что слал mbpoll) # Чтение 1 регистра с адреса 769 (0x0301) для устройства 1 request = b'\x01\x03\x03\x00\x00\x01\x84\x4E' # Очищаем буферы ser.reset_input_buffer() ser.reset_output_buffer() # Отправляем запрос ser.write(request) ser.flush() # Ждем ответа и вычитываем порцию (запрос занял время, плюс ответ) time.sleep(0.1) # Читаем сырые данные raw_response = ser.read(ser.in_waiting) print(f"Сырой ответ из порта: {raw_response}") # Отсекаем эхо, если оно есть (ищем начало нашего ответа с адреса \x01) # Если устройство вернуло 7 байт, как в ошибке: if len(raw_response) >= 7: # Если в начале висит эхо запроса (8 байт), отрезаем его if raw_response.startswith(request): response = raw_response[len(request):] else: response = raw_response print(f"Пакет после отсечения эха: {response}") if len(response) >= 7: # Извлекаем данные из 4-го и 5-го байта (индексы 3 и 4) high_byte = response[3] low_byte = response[4] value = (high_byte << 8) + low_byte print(f" Успешно! Значение регистра: {value}") else: print("Не удалось получить полный ответ от устройства.") ser.close() EOF |
Ставим Docker на debian 13
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
sudo apt-get update sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release Импортируем GPG-ключ для репозитория docker: curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg Добавим новый репозиторий в список apt: echo \ "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \ $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null Установим докер: apt-get update apt-get install docker-ce docker-ce-cli containerd.io Проверяем: docker run hello-world |
Готовим контейнер:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
cd /opt mkdir freeipa-setup && cd freeipa-setup cat > docker-compose.yml << 'EOF' services: freeipa: image: freeipa/freeipa-server:fedora-42 container_name: freeipa-server hostname: ipa.example.com ####--#fix this string privileged: true restart: unless-stopped ports: - "443:443" - "80:80" - "53:53/tcp" - "53:53/udp" - "88:88/tcp" - "88:88/udp" - "464:464/tcp" - "464:464/udp" - "389:389" - "636:636" - "749:749" volumes: - ./ipa-data:/data:Z command: - --unattended - --realm=EXAMPLE.COM ####--#fix this string ###CAPS STRING - --domain=example.com ####--#fix this string - --admin-password=YourStrongPass123 ####--#fix this string - --ds-password=YourStrongPass123 ####--#fix this string - --setup-dns - --auto-forwarders - --no-ntp - --no-host-dns - --allow-zone-overlap - --no-ssh - --no-sshd EOF |
Запуск и логи:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Запуск: docker compose up -d Логи: docker compose logs -f Подключение к консоли: docker exec -it freeipa-server /bin/bash # Проверяем, запущен ли контейнер docker ps -a | grep freeipa # Смотрим логи контейнера (особенно важно - последние 100 строк) docker logs --tail 100 freeipa-server # Проверьте, есть ли процессы внутри контейнера docker exec freeipa-server ps aux # Проверьте, запущен ли httpd (веб-сервер) docker exec freeipa-server systemctl status httpd |
Перенастройка:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 1. Остановите текущий контейнер docker compose down # 2. Удалите старые данные (ВАЖНО! это удалит предыдущие настройки) rm -rf ./ipa-data # 3. Отредактируйте docker-compose.yml nano docker-compose.yml # Измените строку с образом на: freeipa/freeipa-server:centos-8 # 4. Запустите заново docker compose up -d # 5. Следите за логами docker compose logs -f |
Официальная инструкция из github:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
git clone https://github.com/freeipa/freeipa-container.git cd freeipa-container !!! Точка в конце наада точно ^_^ !!! -f имя файла докер !!! -t как обозвать ваш образ docker build -f Dockerfile.rocky-9 -t freeipa:latest . docker build -f Dockerfile.rhel-10 -t freeipa:latest . docker build -f Dockerfile.centos-9-stream -t freeipa-co9 . Проверяем образы: docker images |
|
1 2 3 |
Идея просто локально собрать ansible в doker нужной версии. Использовать свои локальные фалы при запуске контейнера. в дистрибутиве apline версия ansible на 20260505 [core 2.16.1] |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
cat > docker-compose.yml << "EOF" services: ansible: build: . volumes: - ./:/ansible - ~/.ssh/id_rsa:/root/.ssh/id_rsa environment: - ANSIBLE_HOST_KEY_CHECKING=False stdin_open: true tty: true ansible-playbook: build: . volumes: - ./:/ansible - ~/.ssh/id_rsa:/root/.ssh/id_rsa environment: - ANSIBLE_HOST_KEY_CHECKING=False entrypoint: ansible-playbook EOF cat Dockerfile << "EOF" FROM alpine:latest RUN apk add --no-cache \ ansible \ openssh-client \ rsync WORKDIR /ansible ENTRYPOINT ["ansible"] CMD ["--help"] EOF docker-compose run --rm ansible-playbook ping.yaml docker-compose run --rm ansible --version |
kubernetes / minikube
ссылки:
|
1 2 3 4 5 6 7 8 |
www.youtube.com/watch?v=TwyhnBDOHPw https://minikube.sigs.k8s.io/docs/handbook/ https://helm.sh/ru/docs/intro/install/ Примеры от selectel: https://docs.selectel.ru/managed-kubernetes/clusters/connect-to-cluster/ https://docs.selectel.ru/managed-kubernetes/networks/set-up-ingress/ https://docs.selectel.ru/managed-kubernetes/networks/set-up-ingress/?install-ingress-controller=with-configuring-loadbalancer |
Задачи Kubernetes:
|
1 2 3 4 5 |
0. Деплоймент 1. Самовосстановление 2. Управление 3. Масштабирование 4. Баланс трафика |
Не задачи kubernetes:
|
1 2 3 4 |
Хранение данных Хранение образов (registry) Сборка образов Сбор журналов и метрик |
Общие понятия kubernetes:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
master node - сервер или вм машина - головной центр управления - центр принятия решений worker node - сервера или машины на которых запущены приложения, которыми управляет master noda master master master | | | --------------------------|-------------------------- | | | worker worker worker В свою очередь в master node: kubectl | api server --|-------------> scheduler | | -----> etcd | controller manager api server - именно с этим сервером абсолютно все общаются (you, etcd, scheduler, controller manager, node, pode, все и все) - etcd - это база данных, ключ значение, в этой базе хранится все значения что мы делаем с kubernetes scheduler - планировщик, тут принимается решения что куда деплоить controller manager - запущенный процесс, который следит за тем что бы kubernetes это сделал kubectl - через эту программу мы общаемся с "api server" (все управление kubernetes делается через нее) controle plane - это общее название api server + etcd + scheduler + control manager Deployment - это yaml файл в котором мы описывает что нам требуется от kubernetes ReplacaSet - подымает, следит за Pod (количество экземпляров, самовосстановление, желаемое состояние и т.д.) Pod - собственно наши приложение (doker контейнер или контейнеры) Трафик -> ingress controller -> node -> kube-proxy (service.yaml) / kubelet -> pod ingress - внешний трафик ingress controller - чаще всего это nginx, но также это может быть и haproxy и другое похожее ПО service.yaml - собственно управляет как подключится к pod (настройки для kube-proxy) Просто файлы настроек: deployment.yaml - для вашего приложения которое выбудете запускать в kubernetes service.yaml - файл с настройками для вашего приложения которое задает настройки для вашего приложения в kube-proxy kubelet - программа которая отвечает за разворачивание pod StatefulSet - это приложения, которые не живут в контейнерах, например базы данных (postgres, kafka, redis, etc..) DaemonSet - это приложения которое будет жить на каждой nod (например fluentd - сбор логов) |
minikube
|
1 2 3 4 5 6 7 |
https://minikube.sigs.k8s.io/docs/start/?arch=%2Flinux%2Fx86-64%2Fstable%2Fbinary+download curl -LO https://github.com/kubernetes/minikube/releases/latest/download/minikube-linux-amd64 sudo install minikube-linux-amd64 /usr/local/bin/minikube && rm minikube-linux-amd64 minikube start - запуск kubernetes minikube minikube dashboard - открыть дашборд minikube |
kubectl
|
1 2 3 |
kubectl get nodes - показать доступные nod kubectl get pods - показать запущенные pod kubectl apply -f deployment.yaml - пример запуска приложения (в файле deployment.yaml настройки для нашего приложения для kubernetes) |
Внешний kubernets:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
0. Для внешних подключений к kubernetes: export KUBECONFIG=$HOME/путь_до_конфига.yaml 1. helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm inspect values ingress-nginx/ingress-nginx > values.yaml 2. Фрагмент манифеста с блоком annotations: metadata: name: loadbalancer-name labels: app: nginx annotations: service.beta.kubernetes.io/openstack-internal-load-balancer: "true" 3. helm install ingress-nginx/ingress-nginx --generate-name -f values.yaml |
Примеры файлов:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
0. Создаем файл dokerfile, index.html, deployment.yaml, service.yaml: cat > dockerfile << "EOF" FROM nginx:alpine # Копируем простую HTML страницу COPY index.html /usr/share/nginx/html/ EXPOSE 80 EOF cat > index.html << "EOF" <!DOCTYPE html> <html> <head> <title>Hello World</title> </head> <body> <h1>Hello World from Kubernetes!</h1> <p>This is a simple web server running in a Docker container</p> <p>Server: <span id="server-info">Nginx</span></p> <p>Port: 80</p> </body> </html> EOF cat > deployment.yaml << "EOF" apiVersion: apps/v1 kind: Deployment metadata: name: hello-world-app labels: app: hello-world spec: replicas: 3 selector: matchLabels: app: hello-world template: metadata: labels: app: hello-world spec: containers: - name: hello-world-container image: hello-world-app:latest ports: - containerPort: 80 resources: requests: memory: "64Mi" cpu: "50m" limits: memory: "128Mi" cpu: "100m" EOF cat > service.yaml << "EOF" apiVersion: v1 kind: Service metadata: name: hello-world-service spec: selector: app: hello-world ports: - protocol: TCP port: 8080 targetPort: 80 type: LoadBalancer EOF Запуску: docker build -t hello-world-app:latest . kubectl apply -f deployment.yaml kubectl apply -f service.yaml kubectl get pods |
ls1046ardb / buildroot
Собираем собираем:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
0. Качаем и выбираем версию: git clone https://github.com/buildroot/buildroot cd buildroot git tag git checkout 2025.08.1 1. Показать доступные и создать конфиг для нужной платформы: make list-defconfigs | grep ls1046 make ls1046a-rdb_defconfig 2. Посмотреть что там можно качнуть из ядер: git ls-remote --tags https://github.com/nxp-qoriq/linux.git | grep 6 3. Сборка и настройка make linux-source make linux-menuconfig make menuconfig #make nconfig - альтернатива menuconfig #make xconfig - альтернатива menuconfig |
P.S Иногда полезно кое что :
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
В поисках: grep "BR2_LINUX_KERNEL_VERSION" .config grep "BR2_LINUX_KERNEL_CUSTOM_VERSION" .config В поисках ядер: find /opt/buildroot/output/build/linux-* -name "*lsdk*" -o -name "*ls1046*" -o -name "*defconfig*" 2>/dev/null | grep 1046 # Посмотрим какие defconfig есть в скачанном ядре ( сработает после make linux-source) ls /opt/buildroot/output/build/linux-v5.10.100/arch/arm64/configs/ Проверим, что создался правильный device tree: ls /opt/buildroot/output/build/linux-v5.10.100/arch/arm64/boot/dts/freescale/ | grep ls1046 # В menuconfig можно указать кастомный tarball Kernel -> Kernel version (Custom git repository) -> URL of custom kernel repository = https://github.com/nxp-qoriq/linux.git -> Custom kernel version = LSDK-xx.xx (или конкретный тег) #Иногда после изменения конфига(make menuconfig) помогает: make savedefconfig make linux-dirclean make |
P.S По поводу хешей
|
1 2 3 4 5 6 7 8 |
# Скачаем файл и вычислим хэш mkdir -p dl/linux echo "sha256 8e6e5c3e5f2b5a5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c5c linux-5.10.72.tar.xz" > dl/linux/linux.hash # Скачаем файл и вычислим хэш cd /opt/buildroot/output/build/ wget -q https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.72.tar.xz sha256sum linux-5.10.72.tar.xz > /opt/buildroot/dl/linux/linux.hash |
P.S включить английскую локаль
|
1 2 |
export LANG=C.UTF-8 export LC_ALL=C.UTF-8 |
P.S. Возможная проблема загрузки и неверная dtb:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
0. Ищем источник extlinux для 1046 find /opt/buildroot -name "*extlinux*" -type f | grep 1046 1. В текущем билдрут это файл: cat /opt/buildroot/board/freescale/ls1046a-rdb/rootfs_overlay/boot/extlinux/extlinux.conf ----------------------------------------------------------------------------------------- label ls1046ardb-buildroot kernel /boot/Image devicetree /boot/fsl-ls1046a-rdb-sdk.dtb append console=ttyS0,115200 root=/dev/mmcblk0p1 earlycon=uart8250,mmio,0x21c0500 rootwait ----------------------------------------------------------------------------------------- 2. Вот пример правильной так как я менял кое что при сборке то файл dtb у меня другой: cat /opt/buildroot/board/freescale/ls1046a-rdb/rootfs_overlay/boot/extlinux/extlinux.conf ------------------------------------------------------------ label ls1046ardb-buildroot kernel /boot/Image devicetree /boot/fsl-ls1046a-rdb.dtb append console=ttyS0,115200 root=/dev/mmcblk0p1 earlycon=uart8250,mmio,0x21c0500 rootwait ------------------------------------------------------------ 3. Производим переcборку образа: make target-post-image 4. Убеждаемся что все исправлено: cat /opt/buildroot/output/target/boot/extlinux/extlinux.conf 5. Записываем sd карту: cd output/images/ dd if=sdcard.img of=/dev/sde status=progress sync |
Важные параметры для сборки 5.10 ядра файл .config
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
BR2_LINUX_KERNEL=y BR2_LINUX_KERNEL_CUSTOM_GIT=y BR2_LINUX_KERNEL_CUSTOM_REPO_URL="https://github.com/nxp-qoriq/linux.git" BR2_LINUX_KERNEL_CUSTOM_REPO_VERSION="v5.10.100" BR2_LINUX_KERNEL_VERSION="v5.10.100" BR2_LINUX_KERNEL_USE_ARCH_DEFAULT_CONFIG=y BR2_LINUX_KERNEL_INTREE_DTS_NAME="freescale/fsl-ls1046a-rdb" BR2_LINUX_KERNEL_NEEDS_HOST_OPENSSL=y BR2_KERNEL_HEADERS_CUSTOM_GIT=y BR2_KERNEL_HEADERS_CUSTOM_REPO_URL="https://github.com/nxp-qoriq/linux.git" BR2_KERNEL_HEADERS_CUSTOM_REPO_VERSION="v5.10.100" BR2_DEFAULT_KERNEL_HEADERS="v5.10.100" BR2_PACKAGE_HOST_LINUX_HEADERS_CUSTOM_5_10=y BR2_aarch64=y BR2_cortex_a72=y BR2_ARM_FPU_FP_ARMV8=y BR2_TOOLCHAIN_BUILDROOT_GLIBC=y BR2_GCC_VERSION_14_X=y BR2_BINUTILS_VERSION_2_43_X=y BR2_PACKAGE_FMC=n BR2_PACKAGE_FMLIB=n |
Вот сам файл .config, собрано только ядро:
doc_2025-11-19_14-35-03.config_5_10_100_no_soft
Пример создания проекта golang
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
0. Создаем директорию для проекта mkdir ИМЯ_ПРОЕКТА cd ИМЯ_ПРОЕКТА touch main.go 1. Первичная иницилизация go mod init ИМЯ_ПРОЕКТА 2. Докача модулей из интернета go get путь_до_модуля go get путь_до_модуля go get путь_до_модуля go get путь_до_модуля Сборка: go build -o ИМЯ_ПРОЕКТА_ЖЕЛАЕМОЕ_ИМЯ_БИНАРНИКА_Х86 GOOS=linux GOARCH=arm64 go build -o ИМЯ_ПРОЕКТА_АРМ64 P.S. ИМЯ_ПРОЕКТА/ │── go.mod # Файл с информацией о модулях │── go.sum # Контрольные суммы зависимостей │── main.go # Исходный код └── ИМЯ_ПРОЕКТА # Скомпилированный бинарник (после go build) |
apt
|
1 2 3 4 |
apt install apt-file apt-file search bin/7z apt-file update apt-file show |
strace
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
strace witch -a date dpkg -S /bin/date dpkg -L coreutils dpkg -s coreutils Управляющие символы терминала: <pre class="lang:default decode:true " > нотация ввод вывод клавиши код_символа код_символа ^C intr - Ctrl+C 0x03 EXT ^ quit - Ctrl+ 0x1c FS ^ quit - Ctrl+4 0x1c FS ^Z susp - Ctrl+Z 0x1A SUB ^D eof - Ctrl+D 0x04 EOT ^? erase - Backspace 0x7F DEL ^? erase - Ctrl+? 0x7F DEL ^? erase - Ctrl+8 0x7F DEL ^H - backspace Ctrl+H 0x08 Del b - backspace Ctrl+H 0x08 Del ^W werase - Ctrl+W 0x17 ETB ^U kill - Ctrl+U 0x15 NAK ^l - tab TAB 0x09 HT t - tab Ctrl+I 0x09 HT ^M eol cr Enter 0x0D CR r eol cr Ctrl+M 0x0D CR ^j eol nl Ctrl+j 0x0A LF n eol nl Ctrl+j 0x0A LF ^S stop - Ctrl+S 0x13 DC3 ^Q start - Ctrl+Q 0x11 DC1 ^R rprnt - Ctrl+R 0x12 DC2 ^V lnext - Ctrl+V 0x16 SYN ^N - so Ctrl+N 0x0E SO ^O - si Ctrl+O 0x0f SI ^[ esc esc ESC 0x1B ESC e esc esc ESC 0x1B ESC ^[ esc esc Ctrl+[ 0x1B ESC e esc esc Ctrl+3 0x1B ESC !!! Обратите внимание на код символа / клавиши / ввод / вывод / натацию !!! !!! Многие действия можно сделать разными наборам нотация и клавиш !!! Показать доступные символы для терминала: stty -a whoami strace -fe uname,getcwd,geteuid,sethostname whoami hostname strace -fe uname,getcwd,geteuid,sethostname hostname pwd strace -fe uname,getcwd,geteuid,sethostname pwd err strace -fe uname,getcwd,geteuid,sethostname hostname springfield date ltrace -x *time*+fwrite date |
Стандарты
|
1 2 3 4 5 6 |
Организация IEEE IEEE 1003.1 POSIX.1 - интерфейс API операционной системы IEEE 1003.2 POSIX.2 - интерфейс командной строки ОС (CLI) Posix (Portable Operating System Interface) SUS (Single UNIX Specification) |
Телетайпы
|
1 2 3 4 5 6 7 8 9 10 11 12 |
ascii(7) charset(7) 7 - семибитная кодировка Управляющие символы: CR - (carriage return) - Возврат головки к началу строки BS - (back space) - Перемещение головки справа на лево LF - (line feed) - Прокрутка бумаги Начало новой строки LN - (line new) - На новую строку HT - (Horizantal tab) - На несколько символов вправо SO - (shift out) - Изменят шрифт на мелкий SI - (Shift in) - Изменяет шрифт на большой |
Виртуальные терминалы:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Когда-то терминалы подключались с помощью RS232 и драйвера ttyS(), сейчас это экзотика. Узнать к какому терминалу подключены вы используйте команду tty tty who users w Переключение между терминалами на лакальном ПК ALT + F1 ALT + F12 CTRL + ALT + F1 stty |
tee
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
Команда tee используется для того, чтобы одновременно вывести данные на экран и записать их в файл. Это особенно полезно для сохранения вывода команды в файл без потери возможности видеть вывод в реальном времени. Запись вывода команды в файл и отображение в терминале echo "Hello, World!" | tee output.txt Добавление данных в файл (с флагом -a) echo "New line" | tee -a output.txt Запись вывода команды в несколько файлов echo "Multi-output" | tee file1.txt file2.txt Использование tee с пайплайнами ls -l | tee filelist.txt | grep ".txt" echo -e "onentwonthree" | tee output.txt | wc -l Использование с перенаправлением стандартного потока ошибок ls /nonexistent /tmp |& tee output.txt /pre> Управляющие последовательности: <pre class="lang:default decode:true " > Посмотреть все: infocmp Задать: tput smul tput rev tput srg0 tput smul | od -ac |
Видосики в консоле ASCIIANSI:
|
1 2 3 4 5 6 |
ASCII-графика ANSI-графигка библиотеки caca и aalib tty setfont Uni1Vga8 mpv --quiet -vo caca https://www.yotube.com/что_топосмотреть mplayer -quiet -vo aa:dim:bold:reverse $(youtube-dl -g https://www.yotube.com/что_топосмотреть) |
Определить откуда запускается программа
|
1 2 3 4 5 6 |
whereis date which date type date type -a date type -a ls |
Трассировка выполнения команд в bash
|
1 2 3 4 5 |
Для влечения достаточно выполнить: set -x Для отключения: set +x |
Собственно чем отличается «-» «—»
|
1 2 3 4 5 |
- опция -- специальная опция сигнализирует о конце списка опций, за которыми следуют лексемы, расцениваемые как аргументы вне зависимости от их написания позволяют навести некий порядок |
Справочные системы / MAN
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
whatis intro whatis whatis whatis apropos whatis man man -w man man -w passwd /usr/share/man/man1/asn1parse.1ssl.gz /usr/share/man/man1/passwd.1.gz /usr/share/man/man5/passwd.5.gz man 5 passwd man 1 passwd man asn1parse Клавиши в man: q - выход h - справка стрелки - переходы / - поиск по регулярному выражению вперед ? - поиск назад n - поиск вперед N - повторить поиск в обратном направлении G - в конец страницы g - в начало страницы help help -d date help -d cd help -d ls help -d unset |
Пользователи и группы:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
UID - User Identifier GID - Group Identifier Узнать свой UID GID id Передача полномочий: su - switch user - переключится на пользователя sudo - switch user do - контролируемая передача полномочий su - реализует повторную регистрацию в системе sudo - реализует явные правила передачи полномочий описанных в файле "/etc/sudoers" и подтвердить передачу полномочий с помощью пароля пользователя Пароли и пользователи группы в файле: /etc/passwd /etc/shadow /etc/group /etc/gshadow |
Переменные окружения:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
Посмотреть можно так: env printenv Вывод env: SHELL=/bin/bash - текущий шел HOME=/home/username - домашний каталог LANGUAGE= - язык LANG=en_US.UTF-8 - язык PWD=/home/username/1 - текущий каталог LOGNAME=username - пользователь USER=username - пользователь PATH=/usr/local/sbin:/usr/local/bin:/usr/bin - где искать программы LC_* - наборы переменных определяют другие языковые особенности LC_TIME - определяет формат вывода даты PAGER - указывает на программу для постраничного листания например на less / more EDITOR - имя текущего редактора vim / nano VISUAL - имя текущего редактора vim / nano BROWSER - имя html просмоторщика TERM - устанавливает имя терминала по которому программа использует управляющие символы ESC locale - список доступных локалей locale -a Пример: export LC_TIME=ru_RU.utf8 date export LC_TIME=en_US.utf8 date Переменная PS1 (.bashrc) Хранит настройки приглашения shell alias ls='ls --color=auto' PS1='[u@h W]$ ' u - имя зарегистрированного пользователя в системе h - короткое собственное имя компьютера w - имя текущего каталога $ - символ приглашения ($ обычный # root) t - время e - символ управления ESC и т.д. см "man 5 terminfo" printenv printenv TERM infocmp Файлы и каталоги: .profile .bashrc .config .ssh .nanorc |
Файлы и каталоги:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
ls -l /bin/ls /dev/sda /dev/tty /sbin/halt -rwxr-xr-x 1 root root 137920 Aug 30 14:57 /bin/ls brw-rw---- 1 root disk 8, 0 Sep 19 11:53 /dev/sda crw-rw-rw- 1 root tty 5, 0 Sep 25 11:28 /dev/tty lrwxrwxrwx 1 root root 9 Sep 10 18:06 /sbin/halt -> systemctl ls -la /run/systemd/notify srwxrwxrwx 1 root root 0 Sep 4 11:39 /run/systemd/notify ls -la /run/systemd/inaccessible/fifo p--------- 1 root root 0 Sep 4 11:39 /run/systemd/inaccessible/fifo - обычный файл b - блочное устройство (block) c - символьное устройство (character) p - именованный канал (pipe) s - сокет(socket) l - символьная ссылка (link) Вот так их можно найти файлы сокетов и именованных каналов find / -type p find / -type file file_name - программа может показать что перед вами за файл, так как файл может быть текстовым или бинарным stat file_name - подробная статистика о файле Специальные файловые устройства: /dev/sd* /dev/input/mouse* /dev/video* /dev/snd/pcm* /dev/dri/card* crw-rw----+ 1 root video 226, 1 Sep 19 11:53 /dev/dri/card1 crw-rw---- 1 root input 13, 32 Sep 19 11:53 /dev/input/mouse0 brw-rw---- 1 root disk 8, 0 Sep 19 11:53 /dev/sda brw-rw---- 1 root disk 8, 1 Sep 19 11:53 /dev/sda1 brw-rw---- 1 root disk 8, 2 Sep 19 11:53 /dev/sda2 brw-rw---- 1 root disk 8, 3 Sep 19 11:53 /dev/sda3 brw-rw---- 1 root disk 8, 16 Sep 19 11:53 /dev/sdb brw-rw---- 1 root disk 8, 17 Sep 19 11:53 /dev/sdb1 brw-rw---- 1 root disk 8, 32 Sep 19 11:53 /dev/sdc brw-rw---- 1 root disk 8, 33 Sep 19 11:53 /dev/sdc1 brw-rw---- 1 root disk 8, 48 Sep 23 15:34 /dev/sdd brw-rw---- 1 root disk 8, 64 Sep 23 15:56 /dev/sde crw-rw----+ 1 root audio 116, 3 Sep 19 16:59 /dev/snd/pcmC0D0c crw-rw----+ 1 root audio 116, 2 Sep 24 18:10 /dev/snd/pcmC0D0p crw-rw----+ 1 root audio 116, 4 Sep 19 11:53 /dev/snd/pcmC0D2c crw-rw----+ 1 root audio 116, 7 Sep 25 12:56 /dev/snd/pcmC1D3p crw-rw----+ 1 root audio 116, 8 Sep 25 12:56 /dev/snd/pcmC1D7p crw-rw----+ 1 root audio 116, 9 Sep 25 12:56 /dev/snd/pcmC1D8p Устройства бывают символьные c и блочные b Рассмотрим строки: brw-rw---- 1 root disk 8, 0 Sep 19 11:53 /dev/sda brw-rw---- 1 root disk 8, 1 Sep 19 11:53 /dev/sda1 brw-rw---- 1 root disk 8, 2 Sep 19 11:53 /dev/sda2 brw-rw---- 1 root disk 8, 3 Sep 19 11:53 /dev/sda3 brw-rw---- 1 root disk 8, 16 Sep 19 11:53 /dev/sdb brw-rw---- 1 root disk 8, 17 Sep 19 11:53 /dev/sdb1 brw-rw---- 1 root disk 8, 32 Sep 19 11:53 /dev/sdc brw-rw---- 1 root disk 8, 33 Sep 19 11:53 /dev/sdc1 brw-rw---- 1 root disk 8, 48 Sep 23 15:34 /dev/sdd brw-rw---- 1 root disk 8, 64 Sep 23 15:56 /dev/sde Все драйверы ядра пронумерованы главными (major) числами (цифра 8), а под их управлением дополнительные(minor) числа (0,1,2,3,16,17,32,33,48,64) Еще в linux есть специальные устройства: /dev/null - всегда пустое /dev/full - всегда полное /dev/zero - всегда бесконечно нулевое /dev/random - генератор псевдо случайных чисел /dev/urandom - генератор псевдо случайных чисел |
Именованные каналы и файловые сокеты:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
IPC (InterProcess Communication) Служат для обмена процессов и программ между собой. Таким устройством могли бы стать и обычные файлы, но файлы служат для сохранения информации. pipe они де FIFO-файлы(first in first out) / named pipe socket Основное отличие именованного канала от сокета состоит в особенности передачи данных. Через именованный канал организуется однонаправленная (симплексная) передача без мультиплексирования, а через сокет - двунаправленная (дуплексная) мультиплексированная передача. Именованный канал обычно работает по схеме "поставщик - потребитель" (producer-consumer) Один потребитель принимает информацию от одного поставщика (на самом деле от многих но в разное время) telinit init halt reboot shutdown poweoff /dev/initctl /run/initctl Сокет, схема работы "клиент - сервер" (client-server) Один сервер принимает и отправляет информацию от многих ко многим (одновременно) клиентам. cron cupsd logger /dev/log /run/systemd/journal/dev-log systemd-journald |
Файловые дескрипторы:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
Системные вызовы: open - открытие файла read - чтение файла write - запись файла close - закрытие файла ioctl - используется для управления драйвером устройств ioctl(input output control), применяется в основном для специальных файлов устройств При запросе на открытие файла системным вызовом open производится его однократный поиск (относительно медленный поиск) имени файла в дереве каталогов и для запросившего процесса создается так называемый файловый дескриптор(descriptor). Файловый дескриптор содержит информацию, описывающую файл, например: индексный дескриптор inode файла на файловой системе, номера major и minor устройства, на котором располагается файловая система файла, режим открытия файла и прочую служебную информацию. При последующих операция read и write доступ к самим данным файла происходит с использованием файлового дескриптора(что исключает медленный поиск файла). Файловые дескрипторы пронумерованы и содержатся в таблице открытых процессом файлов, которую можно получить при помощи диагностической программы lsof. Получить список процессов, открывающих файл, можно при помощи lsof и fuser lsof fuser lsof -p $$ ls -la /dev/log sudo lsof /run/systemd/journal/dev-log sudo fuser /run/systemd/journal/dev-log ps p 317 sudo lsof /var/log/syslog ps up 354 Пример с локалью: strace -fe open,openat,close,read,write,ioctl date file /etc/localtime file /usr/share/zoneinfo/Europe/Moscow Пример с сидюком, где только его взять в 2024году.... ls -la /dev/cdrom strace -fe open,openat,close,read,write,ioctl eject Пример с клавишей на клаве и ее светодиодом: strace -fe open,openat,read,write,close,ioctl setleds -L +num +scroll |
Файловые системы и процедура монтирования:
|
1 2 3 4 |
mount mount /dev/cdrom ~/mnt/cdrom mount -t iso9660 lsblk -f /dev/cdrom |
Сетевые файловые системы:
|
1 2 3 4 5 6 7 8 9 10 |
Файловый сетевой сервер NAS Network Attached Storage Протоколы: NFS(Network File System) CIFS/SMB (Common Internet file System и Server Message Block mount -t nfs 10.1.1.1:/share/video /mnt/nas/video mount -t -cifs -o username=guest //10.1.1.1/share/photos /mnt/nas/photos |
Специальные файловые системы:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
proc - информация о процессах, нитях и прочих сущностях ядра операционной системы и используемых ими ресурсах предоставляет программам в виде файлов псевдо файловая система proc sysfs - информация об аппаратных устройствах, обнаруженных ядром операционной системы на шинах PCI, USB, SCSI и прочих, предоставляет псевдо файловая система sysfs. Пример программ которые используют псевдо файловые системы proc и sysfs: uptime ps lsmod lspci lsusb lsscsi Проверка: strace -fe open,openat uptime cat /proc/uptime cat /proc/loadavg strace -fe open,openat lspci -nn cat /sys/bus/pci/devices/0000:00:14.0/config cat /sys/bus/pci/devices/0000:00:14.0/vendor cat /sys/bus/pci/devices/0000:00:14.0/device cat /sys/bus/pci/devices/0000:00:14.0/class cat /sys/bus/pci/devices/0000:00:14.0/revision |
Внеядерные файловые системы:
|
1 2 3 4 5 6 7 8 |
FUSE (Filesystem in userspace) Это: archivemount sshfs encfs curlftpfs fusermount |
Прова доступа:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
stat filename id chown chgrp usermod базовые права: read - 4 write - 2 execute - 1 (execve) Пример как это работает: stat 1 Access: (0644/-rw-r--r--) chmod 421 1 stat 1 Access: (0421/-r---w---x) chmod 777 1 stat 1 Access: (0777/-rwxrwxrwx) Семантика режима доступа для разных типов файлов: файлы = r - читать файл w - изменять файл x - исполнять файл каталоги = w - это значит что из каталога можно стирать файлы из списка r - право просмотра списка файлов имен файлов x - право прохода в каталог chmod ugo + rwx filename chmod ugo - rwx filename user group owner chmod a= filename защитить файл от записи Дополнительные атрибуты файлов: s - Set user/group ID (SUID Set User ID или Set Group ID) - атрибут не явного делегирования полномочий Типичной задачей, требующей неявного делегирования полномочий, является проблема невозможности изменения пользователями своих учетных записей, которые хранятся вдух файлах таблицах - passwd и shadow, доступных на чтение и запись только пользователем root, однако пользователь может воспользоваться программой passwd для изменения своей учетной записи. passwd chfn chsh ping traceroute at crontab t - sTicky - липучка, атрибут ограниченного удаления Служит для базового ограничения записи(w) в каталоге, но только своих файлов. Например каталог tmp Кому то этого было не достаточно и были придуманы еще ACL для файлов. POSIX.1e ACL - access control lists getfacl setfacl lsattr chattr a - append only (только добавление в файл) i - immutalbe (не прикосаемый файл) s - secure deletion S - synchronous update Прова по умолчанию umask Мандатные права пользователя: DAC - (discretionary access control) Модуль принудительного разграничения доступа AppArmor: apparmor-utils aa-status Модуль принудительного разграничения доступа SELinux SElinux (Security Enhanced Linux) sestatus Что ты такое модуль управления принудительного разграничения доступа Astra Linux PARSEC - обеспечение безопасности информационных потоков pdp-id pdp-ls -M |
Программы и библиотеки
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
Программа представляет собой алгоритм, записанный на определенном языке, понятном исполнителю программы. Различают машинный язык. понятный центральному процессору, и языки более высоких уровней (алгоритмические), понятные составителю программы - программисту. Алгоритмы - некий набор инструкций, выполнение которых приводит к решению конкретной задачи. Согласно hier, откомпилированные до машинного языка программы размещаются в каталогах: /bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin А библиотеки в каталогах /lib /usr/lib /usr/local/lib Программы имеют специальный запускаемый формат ELF executalbe ldd (Loader dependencies) (SONAME. shared object name) file /usr/bin/ls ldd /usr/bin/ls Стоит заметить: Файла библиотеки linux-vdso.so.1 (реализующий интерфейс системных вызовов к ядру) не существует, так как он является виртуальной(VDSO, virtual dynamic shared object) Для большинства библиотек зависимости устанавливаются с помощью SONAME вида libNAME.so.X lib - стандартный префикс(library, библиотека) .so - суффикс(shared object, разделяемый объект) NAME - имя собственное .X - номер версии ее интерфейса ld.so ldconfig Библиотеки имеют специальный запускаемый формат ELF shared object Не стоит забывать что самой главной программой является Ядро linux uname -r file /boot/vmlinuz Модули ядра: insmod modprobe rmmod modinfo lsmod init_module delete_module /proc/modules lspci -k lsusb -t |
Процессы и нити:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
fork - системный вызов порождение нового процесса COPY - создает копию родительского процесса exit - уничтожение процесса и завершение процесса SIGCHILD - сообщение родительскому(parent процессу о завершении дочернего (child) status - статус завершения от дочернего процесса wait - родительский процесс ждет завершения дочернего zombie - состояние дочернего процесса если он вдруг стал сиротой clone - универсальный вызов позволяющий при порождении процесса, задать и указать общие ресурсы процессов, указать частные ресурсы и т.д. Примеры: dd if=/dev/cdrom of=plan9.iso & ps f find /usr/share/doc -type f -name '*.html' | xargs -n1 wx -l | sort -k 1 -nr | head 1 & ps fj wait ps f -C postgres ssh server ps -f -C sshd ps f -C apache2 ps fo pid,nlwp,cmd -C apache2 ps -fLc rsyslog ps -fLC systemd ls -lh plan9.iso time bzip plan9.iso & ps f ps -fLp 5546 fg ls -lh plan9.iso.bz2 time pbzip2 -d plan9.iso.bz2 time pbzip2 plan9.iso & ps f ps -fLp 5572 fg tar czf docs.tgz /usr/share/doc & ps f fg strace -fe clone,clone3,fork,vfork,execve tar czf docs.tgz /usr/share/doc pbzip2 plan9.iso & strace -q -fe clone,clone3,fork,vfork,execve pbzip2 plan9.iso |
Дерево процессов:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
Процессы операционной системы принято классифицировать на системные (ядерные), демоны и прикладные, исходя из их назначения и свойств. Прикладные процессы выполняют обычные пользовательские программы(man, ping, etc), для чего им выделяется индивидуальная память, объём которой указан в столбце VSZ вывода команды ps. Такие процессы обычно интерактивно взаимодействуют с пользователем посредством управляющего терминала(исключение графические программы), указанного в столбце TTY вывода программы ps. Демоны, процессы класса демоны(daemons) выполняются системные программы, реализующие те или иные службы операционной системы. Например: cron, sshd, rsyslogd, systemd-udevd. У них отсутствует управляющий терминал в столбце TTY вывода программы ps. Зачастую демоны имеют суффикс d в конце названия. Ядерные процессы, процессы ядра, выполняются параллельные части ядра операционной системы. У них нету индивидуальной виртуальной памяти VSZ, ни управляющего терминала TTY в выводе программы ps. Более того ядерные процессы не выполняют отдельную программу, загружаемую из ELF файла, поэтому их имена COMMAND являются условными и изображаются в квадратных скобках, и имеют особое состояние I в столбе STAT вывода программы ps. Ядерные нити выполняются в общей памяти ОС. ps faxu ps -uaxf pstree -cnAhT |
Маркеры доступа:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
Возможность процесса по отношению к объектам, доступ к которым разграничивается при помощи дискреционных механизмов, определяются значение его атрибутов, формирующих его DAC-маркер доступа. Атрибуты: RUID, RGID, EUID, EGID. man 7 credentials man -k credentials id ps fo euid,ruid,egid,rgid,user,group,tty,cmd who ls -la /etc/shadow /dev/tty2 /dev/tty3 /dev/pts/ptmx ls -la /usr/bin/passwd /usr/bin/wall ls -ln /usr/bin/passwd /usr/bin/wall ps ft pts/1,pts/2 o pid,ruid,rgid,euid,egid,tty,cmd Для selinux id -Z ps Zf sesearch -T -t dhcpc_exec_t -c process ls -Z /usr/sbin/dhclient ps -ZC dhclient cababilities CAP_SYS_PTRACE - эта привилегия позволяет процессам трассировщикам strace и ltrace, использующих системный вызов ptrace, трассировать программы CAP_SYS_NICE - привилегия позволяющая менять приоритет процесса (nice) CAP_KILL - привилегия позволяет посылать сигналы процессам CAP_FOWNER - привилегия позволяет процессам изменить режим доступа, мандатную ветку, флаги, атрибуты CAP_LINUX_IMMUTABLE - управления флагами i (immutable) и a(append) CAP_SETFCAP - устанавливать флаговые привилегии CAP_NET_RAW - создание необработанных (raw) и пакетных (packet) сокетов ps fo user,pid,cmd -C NetworkManager,postgres,apache2,systemd getpcaps 1221 getcap /bin/ping setcap cap_net_raw+ep /bin/ping Пример как дать права для шарка: tshark strace -fe execve tshark getcap /usr/bin/dumpcap setcap cap_net_raw+ep /usr/bin/dumpcap tshark -i eth0 Другие атребуты: ps fe ps fx pwdx ID pwdx 1235 |
Распределение процессора между процессами:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
Переключением центрального процесса между задачами (процессами и нитями) выполняет специальная компонента подсистемы управления процессами называемая планировщиком (scheduler) Именно планировщик определенным образом выбирает из множества неспящих, готовых к выполнению(runable) задач одну, которую переводит в состояние выполнения (running). Выбор задачи происходит естественным образом, когда текущая выполнявшиеся задача переходит в состояние сна(sleep) Ещё существуют вытесняющие алгоритмы планирования, которые ограничивают непрерывное выполнения задач, принудительно прерывают ее выполнение по исчерпании выданного ей кванта времени(timeslice) и вытесняется она во множество готовых, после чего производит выбор новой задачи, подлежащей выполнению. Для пользователей применяется алгоритм CFS(completely fair scheduler) Согласно которому процессорное время распределяется между неспящими задачами справедливым (fair) образом. Для двух задач с равным приоритетом должно быть выделено 50%. приоритеты называется любезностью:) NICE -20 ... +19 -20 сделать немедленно, сейчас же (выдается больше всего процессорного времени) +19 делать неспеша При отсутствии конкуренции за процессорное время, приоритет не будет играть никакой роли. Инфа о cpu: nproc lsclpu Пример управления приоритетом: bzip2 --best -kf plan9.iso & bzip2 --best -kf plan9.iso & ps fo pid,pcpu,pri,ni,psr,cmd renice +10 ID_process renice +10 1234 ps fo pid,pcpu,pri,ni,psr,cmd taskdet -p 3 1222 nice -n 5 time bzip2 --best -kf plan9.iso & nice -n 15 time bzip2 --best -kf plan9.iso & ps fo pid,pcpu,pri,ni,psr,cmd wait Другие планировщики: FIFO (First in First out)- выполняет задачи пока не завершит RR (Round Robin) - выполняет задачи по очереди чуть одна чуть другая PQ (Priority Queue) EDF ( Earliest Deadline First) - Алгоритм гарантирует выполнение задачи и не позволяет ее вытеснить пока она выполняется. Политики планирования: SCHED_OTHER, SCHED_BATHC, SCHED_IDLE - CFS SCHED_FIFO, SCHED_RR - FIFO, RR SCHED_DEADLINE - EDF taskset - позволяет привязать процесс к одному потоку процессора chrt - смена алгоритма шедулера Пример смены планировщика: ps -f chrt -b 0 time bzip2 --best -kf plan9.iso & chrt -o 0 time bzip2 --best -kf plan9.iso & chrt -i 0 time bzip2 --best -kf plan9.iso & ps fo pid,pcpu,class,pri,ni,psr,cmd ps -f chrt -pr 99 ID_PROCESS ps fo pid,psr,cls,ni.pri,pcpu,comm chrt -r 1 taskset -c 2 bzip2 --best -kf plan9.iso & chrt -r 1 taskset -c 2 bzip2 --best -kf plan9.iso & ps fo pid,psr,cls,ni,ori,pcpu,comm chrt -r 2 taskset -c 2 bzip2 --best -kf plan9.iso & ps fo pid,psr,cls,ni.pri,pcpu,comm top -b -n1 -p ID,ID,ID top -b -n1 -p 123,321,555 |
Ввод и вывод
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
Планировщики ввода и вывода SCAN, C-SCAN, LOOK, C-LOOK.... I/O scheduler Сортировка(sorting) Слияние(mergiring) Планировщик deadline один из первых для работами с операциями ввода и вывода. mq-dedline CFQ BFQ kyber - современный Посмотреть какой у вас планировщик для дисков: cat /sys/block/{sda,sdb,sr0}/queue/scheduler cat /sys/block/sda/queue/scheduler Пример: findmnt -T cat /sys/block/sda/queue/scheduler dd if=/dev/urandom of=big1 bs=16384 count=1024 dd if=/dev/urandom of=big2 bs=16384 count=1024 sync dd if=big1 of=/dev/null iflag=direct & dd if=big2 of=/dev/null iflag=direct & wait ionice -c best-effort -n 7 dd if=big1 of=/dev/null iflag=direct & ionice -c best-effort -n 0 dd if=big1 of=/dev/null iflag=direct & wait -n wait -n |
Память:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
Управление памятью - механизм страничного отображения - MMU MMU - (Memory Management Unit, Блок управления памятью) Процессы работают с виртуальными адресами воображаемой памяти (virtual address), отображаемым устройством MMU на физические адреса (physical address) настоящей оперативной памяти. Для отображения оперативной памяти RAM(random access memory) условно разбивается на гранулы по 4Кбайт, которые выделяются процессам. Память процесса состоит из: страниц (page) специальных таблиц (page table) сопоставленные выделенные страничные кадры (page frame) ps fux вывод в килобайтах VSZ (virtual size) - суммарный объём всех страниц процесса (в том числе и выгруженных) RSS (resident set size) - суммарный объём всех его страничных кадров в оперативной памяти, т.е. реальное потребление системные вызовы mmap/munmap, mlock, mprotect, msync, madvise COW - cppy-on-write - любые попытки изменения (write) приводят к созданию их копии(copy), куда попадают изминения ps fux pmap -d PID pmap -d 775 pmap - покажет память процесса ldd /usr/bin/hostname strace hostname top для добавления столбика с SWAP нажмите "g 3" а после "f" top -p $$ free -mw - статистика использования памяти Пример: dd if=/dev/urandom of=big bs=4069 count 262144 ld -lh big free -mw vi big pd -f free -mw top -b -n1 -p enter_PID_vi kill enter_PID_vi free -mw |
Механизмы сигналов
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
man signal man -k signal man kill kill - команда отсылки сигналов Ctrl + C - вызывает kill для выполняемой команды в консоли (SIGINT) stty -a - покажет сигнал ^C и другие возможные сигналы в текущей консоли Пример dd if=/dev/zero of=/dev/null Ctrl+c ^C А если мы запустим это в фоне dd if=/dev/zero of=/dev/null & jobs jobs -l kill -SIGINT enter_PID_jobs Но такой сигнал убьет процесс без сохранения данных на диск А если хочется сохранить данные SIGQUIT №3 dd if=/dev/zero of=/dev/null & jobs -l kill -SIGQUIT enter_PID_jobs ^Quit Для некоторых процессов и демонов не возможно послать сигнал ^C и ^ но возможно послать SIGTERM №15 dd if=/dev/zero of=/dev/null & kill -SIGTERM enter_PID_jobs Есть специальные сигналы для приостановки(№19 SIGSTOP) работы и возобновления(SIGCOUNT №18): bzip2 big & top -b -n1 -p enter_PID_bzip2 kill -SIGSTOP enter_PID_bzip2 ps -f jobs -l top -b -n1 -p enter_PID_bzip2 kill -SIGCOUNT enter_PID_bzip2 top -b -n1 -p enter_PID_bzip2 Самый наверно страшный сигнал №9 SIGKILL Не желательно его использовать бездумно Чаще всего приводит к созданию зомби безусловное завершение процесса Есть еще сигнал безусловной приостановке №19 SIGSTOP kill -l покажит все возможные сигналы dc - стековый калькулятор поможет преобразовывать из dec в hex и двоичный dc -e 26i3o00484004p Также процесс можно приостановить клавишами Ctrl+s Возобновить Ctrl+q bg - такой командой продолжить выполнение в фоне fg - вытащить команду из фона |
Межпроцессорное взаимодействие
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
IPC (iter-process communication)- средства межпроцессорного взаимодействия Применяются каналы, сокеты, очереди сообщений и разделяемая память Синхронизация действий процессов над совместно используемыми объектами - семафоры. Неименованные каналы Самый простой обмен между информацией между родственными процессами (родитель и любой потомок) Два дескриптора: первый передача (записи) в канал второй прием (чтения) из канала Пример: strace -fe pipe,execve tar cjf /tmp/docs.tgz /usr/share/doc ^Z (Ctrl+z) ps -f lsof -p enter_PID_tar Именованные каналы Именованные каналы повторяют поведение неименованных каналов, но предназначены для обмена информацией между неродственными процессами. Именованные каналы используются крайне редко. Пример: ls -l /run/initctl /dev/initctl lsof /run/initctl Неименованные локальные сокеты Именованные каналы = поставщик-> потребитель (односторонний обмен) Сокет = клиент <-> сервер (двух сторонний обмен) Сокет - устоявшиеся русская калька с англ. socket, буквально означающая "разъём", например, 220-вольтовые розетку и вилку, или сетевую розетку и вилку RJ-45, или 3.5гнездо для наушников и соответствующий штекер. Пример: strace -fe socketpair, execve rsync -a /usr/share/doc /tmp/a ps f lsof -p enter_PID_rsync Именованные локальные сокеты Наличие имени канала и сокета Многие системные сервисы linux как раз используют именованные локальные сокеты. Пример: systemd, x-windows-system, wayland, d-bus, wi-fi Терминальные мультиплексоры screen и tmux tty screen tty Ctrl+A C tty ps fp enter_PID_screen t pts/1,pts/2 screen ls screen -r Разделяемая память, семафоры и очереди сообщений ipcs ipcs -m -p dc -e enter_ipcs_string ps up PID pmap PID fuser -v /var/cache/nscd/hosts pmap PID findmnt /dev/shm fuser -v /dev/shm/* ps p PID,PID,PID pmsp -p PID Семафоры и очереди сообщений ipcs -q ipcs -s findmnt /dev/mqueue ls -l /dev/mqueue ls -l /dev/shm/sem.* |
Программирование:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
Интерпретаторы и их сценарии: ash, dash, ksh, bash, zsh, fish Языки: perl, python, tcl Универсальный комментарий в начале скрипта именованный shebang. #!/bin/bash #!/usr/bin/perl #!/usr/bin/python Примеры: file /bin/fgrep head -1 /bin/fgrep file /bin/gunzip head -1 /bin/gunzip file /bin/lsb_release head -1 /bin/lsb_release file /usr/bin/mimetype head -1 /usr/bin/mimetype file /usr/bin/netwag head -1 /usr/bin/netwag printenv PATH pwd Встроенные и внешние команды which -a cd type -a cd which -a pwd type -a pwd |
Перенаправление потоков ввода-вывода
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
0 - Стандартный ввод (stdin) — поток данных, который программа получает на вход (по умолчанию — клавиатура). 1 - Стандартный вывод (stdout) — поток, в который программа записывает результаты (по умолчанию — терминал). 2 - Стандартный поток ошибок (stderr) — поток, куда выводятся сообщения об ошибках (по умолчанию — терминал). Перенаправление stdout: Оператор > позволяет записать вывод команды в файл. Если файл существует, он будет перезаписан. ls > output.txt Оператор >> добавляет вывод в конец файла, не перезаписывая его. echo "Hello" >> output.txt Чтобы перенаправить поток ошибок в файл, используется 2>. ls /nonexistent 2> errors.txt 2>> добавляет ошибки в конец файла: ls /nonexistent 2>> errors.txt Перенаправление stdout и stderr вместе Можно объединить выводы стандартного вывода и ошибок в один файл с помощью &>. ls /nonexistent &> all_output.txt Также можно использовать > file 2>&1, где 2>&1 указывает, что stderr перенаправляется туда же, куда и stdout: ls /nonexistent > all_output.txt 2>&1 Перенаправление ввода (stdin) Оператор < позволяет передать данные в команду из файла. wc -l < input.txt Конвейеры (Pipelines) Конвейер | позволяет направить вывод одной команды на вход другой. ls / | grep "home" Специальные случаи перенаправления Удаление вывода — отправка вывода в "черную дыру" (файл /dev/null), где он просто исчезнет: ls /nonexistent > /dev/null 2>&1 Перенаправление файловых дескрипторов: В Bash файловые дескрипторы можно перенаправлять с помощью конструкции n>file (где n — номер потока). Например, 3>file откроет дескриптор 3 для записи в файл file. Heredoc (встраивание многострочного текста) cat <<EOF Это текст с несколькими строками. EOF Перенаправление stdout и stderr в разные файлы: command > output.txt 2> errors.txt Чтение из файла и отправка результата в другой файл: grep "pattern" < input.txt > output.txt Игнорирование ошибок: command 2> /dev/null Запись вывода и ошибок в один файл: command > output.txt 2>&1 Раздельная запись stdout и stderr: command > output.txt 2> errors.txt Отправка всех данных в «черную дыру»: command > /dev/null 2>&1 |
Арифметические действия:
|
1 2 3 4 5 6 7 8 9 |
set -x RADIUS=10 CIRCLE=`expr 2 * $RADIUS * 355 / 113` set -x let CIRCLE=2*RADIUS*355/113; echo $CIRCLE CIRCLE=$((2 * RADIUS * 355/113)) |
Экранирование:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
ls -l file Рабочий стол file "Рабочий стол" file 'Рабочий стол' find . -name *.gz set -x find . -name "*.gz" " " - слабое игнорирование (не распространяется на метасимволы ` $ ) '' - сильное экранирование, экранирует любые символы |
Список команд:
|
1 2 3 4 5 |
command1 ; command2 ; command3 ; command4 - простой синхронный command1 & command2 & command3 & command4 - простой асинхронный command1 && command2 && command3 && command4 - условный список команд И (следующая команда выполняется если предыдущая выполнилась успешно) command1 || command2 || command3 || command4 - условный список ИЛИ (следующая команда выполняется если не выполнилась предыдущая) |
Составные списки / test / условия
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
whict test which [ type -a test этот тест возвращает 0 если нет ошибок test -f /etc/passwd echo $? этот тест возвращает 0 если нет ошибок [ -w /etc/passwd ] echo $? проверяем блочное устройство сдром. если диска нет , открываем лоток [ -b /dev/cdrom ] && eject /dev/cdrom Составной список if if [!] list; then list; [elif [!] list; then;] ... [else list;] fi if test -b /dev/csrom; then eject /dev/cdrom; fi if [[ "$some_variable" == "good input" ]]; then echo "You got the right input." elif [[ "$some_variable" == "ok input" ]]; then echo "Close enough" else echo "No way Jose." fi ! - нет/не && - и || - или -lt - меньше, применяется в квадратных скобках [] -gt - больше, применяется в квадратных скобках [] [[ Двойные квадратные скобки ]] работают в целом так же, как и [одинарные квадратные скобки], но имеют дополнительные возможности вроде лучшей поддержки регулярных выражений. (( Двойные круглые скобки )) это конструкция, позволяющая осуществлять арифметические вычисления внутри Bash. Пример: if [[ "$name" == "Ryan" ]] && ! [[ "$time" -lt 2000 ]]; then echo "Sleeping" elif [[ "$day" == "New Year's Eve" ]] || [[ "$coffee_intake" -gt 9000 ]]; then echo "Maybe awake" else echo "Probably sleeping" fi Порой вам встретятся двойные квадратные скобки, как в примере выше. А порой они будут одинарными: if [ "$age" -gt 30 ]; then echo "What an oldy." fi Иногда могут быть и круглыми: !!! обрати внимание на age, это переменная и оформлена без ${age} if (( age > 30 )); then echo "Hey, 30 is the new 20, right?" fi А может и не быть: if is_upper "$1"; then echo "Stop shouting at me." fi Что происходит на самом деле: if ANY_COMMAND_YOU_WANT_AT_ALL; then # ... stuff to do fi Пример 0, скрипта iftst_v1: #! /bin/sh # Пример iftst_v0 if test $# -ne 2; then echo "Команде должно быть передано ровно два параметра!" exit 1 else echo "Параметр 1: $1. Параметр 2: $2" fi Пример for в файле: for variable in list do commands done Пример for в командной строке: for variable in list ; do commands ; done Пример использования for в командной строке: $ ls filemgr.png terminal.png $ for f in *.png ; do mv $f screenshot-$f ; done $ ls screenshot-filemgr.png screenshot-terminal.png Пример: for i in a b с; do echo $i; done Пример: #! /bin/sh for i in a b c; do echo $i done |
case
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
case word in [ [(] pattern1 [| pattern2]... ) list ;;]... esac ключевые словами case, in, esac признак окончания ветви ;; #!/bin/bash # Скрипт, который использует конструкцию case # переменная, значение которой сравнивается с набором значений num=2 case $num in 1) echo "num = 1" ;; 2) echo "num = 2" ;; 3) echo "num = 3" ;; esac echo "Конец программы" exit 0 #!/bin/bash echo "Какой цвет вам нравится больше всего?" echo "1 - синий" echo "2 - Красный" echo "3 - Желтый" echo "4 - Зеленый" echo "5 - оранжевый" read color; case $color in 1) echo "Синий - основной цвет.";; 2) echo "Красный - основной цвет.";; 3) echo "Желтый - основной цвет.";; 4) echo "Зеленый - вторичный цвет.";; 5) echo "Оранжевый - вторичный цвет.";; *) echo "Этот цвет недоступен. Пожалуйста, выберите другой.";; esac #!/bin/bash # Скрипт, который использует конструкцию case # переменная, значение которой сравнивается с набором значений num=2 case $num in 1|2|3) echo "num равно или 1 или 2 или 3" ;; 4) echo "num равно 4" ;; *) echo "Неизвестное значение" ;; esac echo "Конец программы" exit 0 |
for / while / until
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
for name in [ words ...]; do list; done и циклы с условием "ПОКА" while [!] list; do list; done и "ДО" until [!] list do list; done с ключевыми словами for, in, while, until, do, done for node in $(seq 1 254); do ping -c 1 -W 1 192.168.1.$node; done while ! ping -c1 -w1 8.8.8.8 ; do sleep 1; date; done until ping -c1 -w1 8.8.8.8 ; do sleep 1; date; done |
Инструментальные средства обработки текста
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
Базовые регулярные выражения ? - Любой символ * - Любое количество любых символов (в том числе ни одного), но не *-файлы! ** - Любые файлы и каталоги, в том числе из всех подкаталогов (начиная с версии bash 4.0 — shopt -s globstar) [abc] - Один из символов, указанных в скобках [a-f] - Символ из указанного диапазона [!abc] - Любые символы, кроме тех, что указаны в скобках [^аЬс] - Аналогично предыдущему ~ - Сокращенное обозначение домашнего каталога . - Текущий каталог .. - Каталог на один уровень выше echo ab{l,2,3} - Возврат abl аЬ2 аЬЗ echo a{1..4} - Возврат al а2 аЗ а4 echo $[3*4] - Арифметические вычисления `команда` - Замена команды результатом ее выполнения $(команда) - Вариант, аналогичный предыдущему echo $? - вернет с каким результатом завершилась предыдущая команда (0 - нет ошибок) Команда "символ" - Отмена интерпретацию любых специальных символов, кроме $ Команда 'символ' - Похоже на предыдущий вариант, но с большими ограничениями (не допускает подстановки переменных) ? - любой одиночный символ в шаблоне * - любое количество любых символов в шаблоне . - любой одиночный символ в регулярном выражении .* - любое количество любых символов в регулярном выражении grep '^[^#]' /etc/wgetrc grep [^0-9][0-9][0-9][^0-9] /etc/services |
Сетевая подсистема:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
lspci lspci -ks 02:00.1 modinfo iwlwifi e1000 ifconfig -a ip link show ip addr show dev wlp2s0 lsmod Просмотр статистики по соединениям IPv4: netstat -4autpn netstat -4atupn ss -4atupn ss -4atplnu Назначение IP адреса: ifconfig eno1 10.0.10.10 up ifconfig eno1 ping -c 1 10.0.0.10 ipaddress add 172.16.16.172/16 dev eno1 ipaddress show dev eno1 ping -c1 172.16.16.172 Маршрутизация: Добавление маршрута по умолчанию: ip route add 0.0.0.0/0 via 10.0.0.1 Показать таблицу: route -n ip route show Диагностика: traceroute -m 50 bad.horse Автоматическая настройка сети: pgrep NetworkManager lsof -w +E -p ID_NetworkManager -a -U strace -fe connect nmcli general Проводная сеть: nmcli dev nmcki dev show enp0s25 nmcli con nmcli conn add type ethernet ifname enp0s25 nmcli conn nmcli conn up ethernet-enp0s25 nmcli dev show enp0s25 ip a show dev enp0s25 ip route show Беспроводная сеть: nmcli dev wifi rescan nmcli dev wifi list nmcli con add type wifi ifname wlp2s0 ssid SSID_NAME nmcli dev show wlp2s0 ip a show dev wlp2s0 nmcli --ask dev wifi connect SSID_NAME nmcli conn show WPA cli: pgrep wpa_supplicant lsof +E -p ID_Wpa_supplicant -a -U strace -fe connect wpa_cli -i wlp2s0 status wpa_cli -i wlp2s0 status wpa_cli -i wlp2s0 scan wpa_cli -i wlp2s0 scan_results wpa_cli -i wlp2s0 list_networks wpa_cli -i wlp2s0 add_network wpa_cli -i wlp2s0 select_network 1 wpa_cli -i wlp2s0 list_networks VPN: nmcli con edit type vpn |
Служба имен и DNS/mDNS-резолверы
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
Отображение имен Name service switch configuration /etc/nsswitch.conf Соответствующие модули libnss_*.so. Номера портов /etc/services libnss_db.so.2 Примеры: grep hosts /etc/nsswitch.conf grep services /etc/nsswitch.conf find /lib/ -name 'libnss_*' Файловые таблицы имен: /etc/hosts /etc/services Примеры: cat /etc/hosts grep http /etc/services getent hosts ubuntu getent services 53/udp Резолверы: sytemd-resolved cat /etc/resolv.conf Примеры: ss -4autpn sport = :53 getent hosts ya.ru host ya.ru host 8.8.8.8 resolvectl dns ss -4autpn spot = :5353 avahi-browse -arcl avahi-resolve --name h1.local geten hosts h2.local |
Сетевые службы ssh
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
https://www.youtube.com/watch?app=desktop&v=f3KZ86l1p5w Для удаленного доступа были программы rlogin, rsh, telnet, и данные передавались в открытом виде, не было шифрования. Служба ssh предназначена для организации безопасного (secure) доступа к сеансу командного интерпретатора (shell) удаленных сетевых узлов. SSH Конфиденциальность (плюс целостность) передаваемых данных. Обеспечивается при помощи симметрического шифрования с помощью общего сеансового ключа. Аутентичность (подлинность) взаимодействующих сторон. Сеансовый ключ устанавливается обеими взаимодействующими сторонами при помощи асимметричного алгоритма открытого согласования ключей протокола Диффи-Хелмана. MITM (man in the middle) - атака Пример: ssh user@namehost.local ssh user@192.168.0.124 uname -a whoami tty logout ssh user@192.168.0.124 uptime Входы без пароля ssh-keygen ssh-copy-id user@192.168.0.124 Копирование: scp file_name user@192.168.0.124: sftp user@192.168.0.124 rsync -avzr user@192.168.0.124:/home/user/file.txt . sshfs |
LDAP
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
https://pro-ldap.ru Системные вызовы open, read, write, close LDAP (Lightweight Directory Access Protocol) DIT (Directory Information Tree) LDAP состоит в использовании принципа ООП (объектно-ориентированного проектирования), согласно которому индивидуальные записи каталога (entries) являются объектами, т.е. экземплярами классов (class), в свою очередь описывающих обязательными (must) и возможные (may) атрибуты (свойства) объектов. Так, класс для описания персональных данных (objectClass: person) требует, что бы его конечный экземпляр обязательно содержит атрибут sn (surname, фамилия) и cn (common name), и позволяет содержать атрибут telephoneNumber. А класс для описания свойств учетных записей (ojectClass: posixAccaunt) требуют наличия атрибутов uid (user identifier), что на деле является "именем" учетной записи, uidNumber, gidNumber (UID и GID учетные записи) и homeDirectory (домашний каталог пользователя) и позволяет (если нужно) содержать атрибуты, такие как userPassword и loginShell. Надо заметить, что принадлежность объекта к тому или иному классу тоже является атрибутом с именем ojectClass, принадлежащему классу с названием top. Само дерево записей LDAP-каталога организуется универсальным образом. В LDAP при построении дерева на любом уровне для различия (distinguish) сущностей (записей) может быть произвольно любой из атрибутов любых объектов, названных выше, например uid. Относительно отличимые имена RDN (realtive distignuished name) состоящих из имен атребута, uid=testla или uid=enstein Аналогично FQDN в системе DNS записываются справа налево. Для разделения имен в DNS используется точка "." www.google.com Почти также делается в LDAP но для разделения сущностей используется запятая objectClass: dcObject dc (Domain commponent) dc=www, dc=google, dc=com ldapsearch -LL -H ldap://ldap.forums.com -x -b "" -s base ldapsearch -LL -H ldap://ldap.forums.com -x -b "" -s base mamingContexts -b "" - пустой фильтр -s base - запрос адресуется только к этому имени (-s scope) ldapsearch -LLL -H ldap://ldap.forumsys.com -x -b dc=example,dc=com -s sub dn ldapsearch -LLL -H ldap://ldap.forumsys.com -x -b dc=example,dc=com objectClass=posixAccaunt dn ldapserach -LL -H ldap://ldap.forumsys.com -x -b uid=tesla,dc=example,dc=com -s base |
Сетевые утилиты
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
tcpdump -i wlp2s0 port 53 hots ya.ru tshark -i wlp2s0 -V -O http,data-text-lines -Y http port 80 curl https://ya.ru nmap -n -vvv -reason 192.168.0.1 pgrep lftp lsof -i 4 -a -p 6056 ss -p dport = :ftp strace -f trace=network curl http://www.gnu.org/graphics/agnuheadterm-xterm.txt cd /usr/share/doc strace -fe trace=network python2 -m SimpleHTTPServer wget http://localhost:8000 |
X windows system
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
X-server и X-client - рисует окна Как любая другая сетевых служба, оконная система X состоит из X-сервера и X-клиентов, взаимодействующих между собой посредством "X протокола" X-сервер Основная задача которого заключается в управлении оборудованием графического вывода и ввода. Под управлением X-сервером находится графические дисплеи (видеоадаптеры и подключенные к ним мониторы), устройства ввода и вывода. Именно X-сервер принимает подключения от X-клиентов. Смотрим: pgrep -l Xorg ps o pid,tty,cmd p PID_ID_process lsof -p PID_ID_process -a /dev lsof -p PID_ID_process -a -U lsof -p PID_ID_process -a -i xdpyinfo | grep -A 4 screen |
X-клиенты и X-протоколы
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
X-клиенты всегда создают хотя бы одно окно xdpyinfo xrandr - поворот экрана glxifo xlsclients xwininfo - список созданных окон xprop Xnest Xephyr Примеры: xwininfo -tree -root | grep gnome-terminal xwininfo id 0x2aaasdasda | grep Map xwininfo id 0x2aaasdasda | egrep 'Map|Width|Height' xprop -id 0x2a00001 | grep ^WM_ WM_NAME - текст заголовка отображаемых окон WM_CLIETN_MACHINE - имя узла сетевого узла X-клиента WM_COMMAND - отображает команду, при помощи которой был запущен клиент xlsclients -l | grep -C 3 gnome-terminal Взаимодействие X-клиентов и X-сервера происходит при помощи локальных или сетевых сокетов в зависимости от их взаимного местоположения и согласно адресу подключения, указываемому на стороне X-клиентов при помощи переменой окружения DISPLAY в host:number В случае сетевого: DISPLAY=ubuntu.local:0 DISPLAY=192.168.0.100 В случае локального: DISPLAY=:0 Примеры: Xnest :0 Xnest :listen tcp & lsof -p 10950 -a -i lsof -p 10950 -a -U xeyes DISPLAY=:1 strace -fe connect xeyes DISPLAY=ubuntu.local:1 strace -fe connect xeyes |
Оконные менеджеры:
|
1 2 3 4 5 6 |
Оконные менеджеры - позволяют манипулировать окнами. Примеры оконных менеджеров: twm - один из самых первых olvm mwm IceWM |
Настольные пользовательские окружения:
|
1 2 3 4 5 6 7 |
Манипулируют и рисуют и есть свои программы. GNOME - gnome-session(менеджер сеансов) и gnome-shell(оконный менеджер в свою очередь запускает его dbus-launch) KDE - startkde(сценарий), ksmserver(менеджер сеансов), kwin(оконный менеджер) XFCE LXDE В современных версиях практически безальтернативно используется D-Bus для взаимодействия с окнами и между окнами. |
Библиотеки интерфейсных элементов:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
сам X-server умеет рисовать примитивы, точки круги и т.д. для более сложных используются специальные библиотеки widget Xaw Xview Motif Tk Qt Xrender Xlib Современные это Qt и Gtk, на основе их разрабатывают KDE и GNOME Пример как посмотреть доступные библиотеки: ldd $(which xeyes) | grep -i libX ldd $(which mwm) | grep -i libXm ldd $(which wish) | grep -i libtk ldd $(which gnome-shell) | grep -i libgtk ldd $(which kcacl) | grep -i libqt.gui |
Wayland
|
1 |
замена X серверу |
Контейнеры и виртуальные машины:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
Чрутизация: Самым древним средством изоляции, известным со времен классической ОС UNIX, является системный вызов chroot, позволяющий назначить процессам корень дерева каталогов, от которого вычисляются все абсолютные путевые имена. Каждый процесс имеет атрибут cwd (current working directory) и атрибут rtd(root directory). pgrep -l avahi-daemon sudo lsof -p ID_avahi-daemon | grep rtd Пример Чрутизации: не правильно но показательно: mkdir c-137 chroot c-137 sh sudo chroot c-137 sh sudo strace -fe chroot,chdir,execve chroot c-137 sh более менее правильно: mkdir c-137/bin cp /bin/sh c-137/bin sudo chroot c-137 sh ldd /bin/sh Пространства имен: namespaces ps up $$ ls -la /proc/$$/ns ps o pid,netns,mntns,pidns,comm p $$ stat -L /proc/$$/ns/net Пример создания контейнера в пространстве имен с использованием unshare: ps o pid,netns,mntns,pidns,comm p $$ sudo unshare -mnp -f -R c-137 --mount-proc busybox sh Контейнеризация: runnc и docker Docker(software) LXC OpenVZ OCI (Open Container Ininitiative) Помогает взаимодействовать с контейнерами программа runc Пример runc: runc spec ls config.json sed -n '/root.*{/,/}/p' config.json sudo debbootstrap --variant=minbase --include iproute2 jammy rootfs Docker: сайт docker.io - образы docker image ls ls -l /var/run/docker.sok docker image pull ubuntu:23.04 docker run -ti --hostname c-123 ubuntu:23.04 docker attach c-123 Группы управления cgroups Управление ресурсами контейнеров |
Загрузка linux
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
Процесс загрузки абсолютно любой операционной системы начинается с той компоненты, которая собственно операционной системой и является - с ядра. Все остальное это дополнительные компоненты, и нужны по месту, десктоп, сервер, роутер и т.д. Главной задачей загрузки является размещение ядра ос в оперативной памяти и передача ей управления. Эти задачи выполняет загрузчик ОС, который принципе не является ее частью. Чаще всего выступает частью аппаратной платформы на которой выполняется ОС. Загрузчик сильно зависит от аппаратной платформы, также должен соответствовать спецификациям платформы, таким как способы разделения носителей на разделы, собственное размещение на этих носителях, порядок передачи ему управления при старте платформы и т.д. Платформа персонального компьютера она же "IBM PC Compatible" BIOS - набор базовых услуг Согласно BIOS носитель должны содержать таблицу разделов определенного вида в своем первом блоке "boot sector", так называемой загрузочной записи MBR, анализировала таблицу разделов, выбирала активный раздел, считывала его PBR(он же VBR (volume boot record) и передавала ему управление. Установленная на этот активный раздел ОС обычно размещала свой загрузчик и выполнялась дальнейшая загрузка. С развитием платформы BIOS развился в EFI, позднее в UEFI. А MRR развился "GUID Partition table" Процедура загрузки теперь предполагает наличие наличие специального системного раздела ESP (EFI System Partition), которая содержит файловую систему FAT (File Allocation Table), на которой в виде текущей (current) передается управление по умолчанию. При любом виде загрузке BIOS/UEFI, загрузчик получив управление умеет правильно считать в оперативную память ядро ОС и передать ему управление. Самым популярным загрузчиком linux является GRUB. efibootmgr -v В современных linux каталог ESP смонтирован в /boot/efi А файлы grub чаще всего в корневой файловой системе в каталоге /etc и файлах grub.cfg Само ядро располагается в каталоге /boot EFI - каталог grub - каталог grub initrd.img - архив модулей (микро версия операционной системы, busybox) vmlinuz-linux - основной файл, ядро linux Загрузчик загружает обе части, а затем передает управление. lsinitramfs /boot/initrd.img-generic | grep.ko$ | wc -l lsinitramfs /boot/initrd.img-generic | grep bin/ lsinitramfs - показ содержимого mkinitramfs - запаковка unmkinitramfs - распаковка |
Драйверы
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
вендор устройства(vendor) идентификатор модели устройства(device) find /lib/modules/`uname -r`/kernel/drivers -name '*.ko' | wc -l lspci -d ::0401 -k lspci -d ::0401 -n modinfo snd_intel8x0 | grep alias lspci lscsi lsusb udevadm monitor -k mount -t sysfs cat /sys/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1:1.0/uevent |
Ссылки:
|
1 |
https://zip.bhv.ru/9785977518437.zip |
Пример:
|
1 2 3 4 5 6 7 8 |
sysctl -w fs.file-max=100000 sysctl -w net.core.somaxconn=65535 sysctl -w net.core.netdev_max_blacklog=65536 sysctl -w net.ipv4.tcp_fin_timeout=15 sysctl -w net.ipv4.tcp_tw_reuse=1 sysctl -w net.ipv4.tcp_tw_recycle=1 sysctl -w net.ipv4.tcp_max_tw_buckets=65536 sysctl -w net.ipv4.tcp_window_scalling=1 |
Для любознательных что есть что:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
net.core.somaxconn=65535 Задает максимальное кол-во соединений, которые могут ожидать обработки в очереди. Важно для приложений. net.core.netdev_max_blacklog=65536 Определяет максимальный размер очереди сетевых устройств обрабатывающих трафик net.ipv4.tcp_fin_timeout=15 Время до ожидания завершения соединения (флаг fin) net.ipv4.tcp_tw_reuse=1 Разрешаем использования сокетов TIME-WAIT для новых соединений. По факту разрешает использования портов в статусе TIME-WAIT для новых соединений. net.ipv4.tcp_tw_recycle=1 Активирует режим повторного использования портов в статусе TIME-WAIT. net.ipv4.tcp_max_tw_buckets=65536 Увеличиваем кол-во сокетов time-wait net.ipv4.tcp_window_scalling=1 Управляет масштабированием окна TCP. Увеличиваем размер TCP-окна если это возможно |
файлик /etc/sysctl.conf
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# custom net.ipv4.ip_forward=1 net.ipv4.tcp_max_tw_buckets = 65536 net.ipv4.tcp_tw_reuse = 0 net.ipv4.tcp_max_syn_backlog = 131072 net.ipv4.tcp_syn_retries = 3 net.ipv4.tcp_synack_retries = 3 net.ipv4.tcp_retries1 = 3 net.ipv4.tcp_retries2 = 8 net.ipv4.tcp_rmem = 16384 174760 349520 net.ipv4.tcp_wmem = 16384 131072 262144 net.ipv4.tcp_mem = 262144 524288 1048576 net.ipv4.tcp_max_orphans = 65536 net.ipv4.tcp_fin_timeout = 10 net.ipv4.tcp_low_latency = 1 net.ipv4.tcp_syncookies = 0 net.netfilter.nf_conntrack_max = 1048576 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216 |
ссылка с одной шляпой….
|
1 |
https://zip.bhv.ru/9785977519953.zip |
Сегменты
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
Сегменты — разновидность коллекций, которые могут расширяться для хранения дополнительных элементов; а это как раз то, что нужно! Оказывается, в Go существует структура данных, в которую можно добавлять новые значения, — она называется сегментом. Как и массив, сегмент состоит из нескольких элементов, относящихся к одному типу. В отличие от массивов, существуют функции, позволяющие добавлять новые элементы в конец сегмента. Отличие сегмента от массива: Фактически это уже знакомый синтаксис объявления массива, только без указания размера. var myArray [5]int // Массив — обратите внимание на размер. var mySlice []int // Сегмент — размер не задан В отличие от переменных для массивов, объявление переменной для сегмента не приводит к автоматическому созданию сегмента. Для этого следует вызвать встроенную функцию make. Функции передается тип создаваемого сегмента (он должен соответствовать типу переменной, которой вы собираетесь присвоить сегмент) и длина сегмента при создании. Пример: var notes []string notes = make([]string, 7) notes[0] = "do" notes[1] = "re" notes[2] = "mi" fmt.Println(notes[2]) fmt.Println(notes[0]) Пример: pr := make([]int, 5) pr[0] = 99 pr[4] = 200 fmt.Println("pr[4] - pr[0] = ", pr[4]-pr[0]) Встроенная функция len для сегментов работает так же, как и для массивов. Передайте len сегмент, и функция вернет его длину в виде целого числа. Пример: notes := make([]string, 7) primers := make([]int, 5) fmt.Println(len(notes)) fmt.Println(len(primers)) Циклы "for" и "for...range" работают с сегментами точно так же, как и с массивами: Пример: letters := []string{"a", "b", "c"} for i := 0; i < len(letters); i++ { fmt.Println(letters[i]) } fmt.Println(" ") for _, letter := range letters { fmt.Println(letter) } |
Литералы сегментов
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
Как и с массивами, если вы заранее знаете, какими значениями должен быть заполнен сегмент в исходном состоянии, то можете инициализировать сегмент этими значениями при помощи литерала сегмента. Литерал сегмента очень похож на литерал массива, но если литерал массива содержит длину массива в квадратных скобках, у литерала сегмента квадратные скобки пусты. За пустыми скобками следует тип элементов, которые будут храниться в сегменте, и список исходных значений всех элементов, заключенный в фигурные скобки. Вызывать функцию make необязательно, при использовании литерала сегмента ваш код создаст сегмент и заполнит его. Сегмент: []int{9, 18, 27} | | | | | Список значений, разделенных запятыми. | Тип элементов в сегменте Пустая пара квадратных скобок Пример: notes := []string{"do", "re", "mi", "fa", "so", "la", "ti"} //Значения присваиваются с помощью литерала сегмента. fmt.Println(notes[3], notes[6], notes[0]) primes := []int{ // Многострочный литерал сегмента. 2, 3, 5, } fmt.Println(primes[0], primes[1], primes[2]) Погодите! Похоже, что сегменты могут делать все, что делают массивы, и в них можно добавлять элементы. Тогда почему бы не ограничиться сегментами и не забыть про эту ерунду с массивами? !!! Потому что сегменты построены на основе массивов. !!! И вы не сможете понять, как работают сегменты, не понимая массивы. Каждый массив существует на основе базового массива. Данные сегмента на самом деле хранятся в базовом массиве, а сегмент всего лишь предоставляет «окно» для работы с некоторыми (или всеми) элементами массива. Когда вы используете функцию make или литерал сегмента для создания сегмента, базовый массив при этом создается автоматически (и вы не можете обратиться к нему иначе как через сегмент). Но вы также можете создать массив самостоятельно, а затем создать сегмент на основе этого массива при помощи оператора сегмента. Пример: underlyingArray := [5]string{"a", "b", "c", "d", "e"} slice1 := underlyingArray[0:3] fmt.Println(slice1) Пример: underlyingArray := [5]string{"a", "b", "c", "d", "e"} i, j := 1, 4 slice2 := underlyingArray[i:j] fmt.Println(slice2) У оператора сегмента предусмотрены значения по умолчанию как для начального, так и для конечного индексов. Если начальный индекс не указан, будет использовано значение 0 позиции. Пример: underlyingArray := [5]string{"a", "b", "c", "d", "e"} slice4 := underlyingArray[:3] fmt.Println(slice4) А если не указан конечный индекс, то в сегмент включаются элементы от начального индекса и до конца базового завершается. Пример: underlyingArray := [5]string{"a", "b", "c", "d", "e"} slice5 := underlyingArray[1:] fmt.Println(slice5) |
Базовые массивы
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Как упоминалось ранее, сам сегмент не содержит данных, это всего лишь «окно» для просмотра элементов базового массива. Сегмент можно представить себе как микроскоп, направленный на определенную часть предметного стекла (базовый массив). Когда вы берете сегмент базового массива, то «видите» только ту часть элементов массива, которая видна через этот сегмент. Несколько сегментов могут существовать на основе одного базового массива. В этом случае каждый сегмент становится «окном» для отдельного подмножества элементов массива. Сегменты даже могут перекрываться! Присваивание нового значения элементу сегмента приводит к изменению соответствующего элемента в базовом массиве. Если на один и тот же базовый массив указывают несколько сегментов, то и изменения элементов массива будут видны во всех сегментах. Из-за этих потенциальных проблем обычно рекомендуется создавать сегменты с использованием make или литерала сегмента (вместо того, чтобы создать массив и применять к нему оператор сегмента). С make и литералами сегментов вам никогда не приходится иметь дела с базовым массивом. |
Расширение сегментов функцией «append»
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
В Go существует встроенная функция append, которая получает сегмент и одно или несколько значений, которые присоединяются в конец сегмента. Функция возвращает но вый, расширенный сегмент со всеми элементами исходного сегмента и новыми элементами, добавленными в его конец. Пример: slice := []string{"a", "b"} fmt.Println(slice, len(slice)) slice = append(slice, "c") fmt.Println(slice, len(slice)) slice = append(slice, "d", "e") fmt.Println(slice, len(slice)) Вам не нужно следить за тем, по какому индексу присваиваются новые значения, или за чем-нибудь еще! Просто вызовите функцию append и передайте ей сегмент со значениями, которые добавляются в конец сегмента, и вы получите новый расширенный сегмент. Да, так просто! Обратите внимание: возвращаемое значение append во всех случаях присваивается той же переменной сегмента, которая передается append. Таким образом предотвращается возможность непоследовательного поведения сегментов, возвращаемых append. Базовый массив сегмента не может увеличиваться в размерах. Если в массиве не остается места для добавления элементов, все элементы копируются в новый, больший массив, а сегмент обновляется, чтобы он базировался на новом массиве. Но поскольку все это происходит где-то за кулисами внутри функции append, невозможно простым способом определить, имеет ли возвращенный сегмент тот же базовый массив, как и переданный сегмент, или другой. Если в программе будут оставаться оба сегмента, это может привести к непредсказуемому поведению. Пример: s1 := []string{"s1", "s1"} s2 := append(s1, "s2", "s2") s3 := append(s2, "s3", "s3") s4 := append(s3, "s4", "s4") fmt.Println(s1, s2, s3, s4) s4[0] = "XX" fmt.Println(s1, s2, s3, s4) По этой причине при вызове append возвращаемое значение обычно присваивается той же переменной сегмента, которая была передана append. Если в программе хранится только один сегмент, то вам не придется беспокоиться о том, используют ли два сегмента один базовый массив! Пример: s1 := []string{"s1", "s1"} s1 = append(s1, "s2", "s2") s1 = append(s1, "s3", "s3") s1 = append(s1, "s4", "s4") fmt.Println(s1) |
Сегменты и нулевые значения
|
1 2 3 4 5 6 7 8 9 10 11 |
Как и в случае с массивами, при обращении к элементу сегмента, которому не было присвоено значение, вы получите нулевое значение для этого типа. Пример: floatSlice := make([]float64, 10) boolSlice := make([]bool, 10) fmt.Println(floatSlice[9], boolSlice[5]) Пример: var intSlice []int var stringSlice []string fmt.Printf("intSlice: %#v, stringSlice: %#vn", intSlice, stringSlice) |
016 / go работа с файлами
Чтение текстового файла
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
Ранее мы использовали пакеты os и bufio стандартной библиотеки для чтения данных по строкам с клавиатуры. Те же пакеты могут использоваться и для построчного чтения данных из текстовых файлов. В своем любимом текстовом редакторе создайте новый файл с именем data.txt. Запишите в файле три наших значения с плавающей точкой, по одному числу в строке. cat > data.txt << "EOF" 71.8 56.2 89.5 EOF Пример программы: package main import ( "bufio" "fmt" "log" "os" ) func main() { file, err := os.Open("data.txt") //Файл данных открыва if err != nil { log.Fatal(err) } scanner := bufio.NewScanner(file) //Цикл выполняется до того, как будет достигнут конец файла, а scanner.Scan вернет false for scanner.Scan() { //Читает строку из файла fmt.Println(scanner.Text()) //Выводит строку } // Если при закрытии файла произошла ошибка то сообщить о ней и завершить работу err = file.Close() //Закрывает файл для освобождения ресурсов. if err != nil { log.Fatal(err) } if scanner.Err() != nil { log.Fatal(scanner.Err()) } } Наша тестовая программа readfile.go успешно читает данные из файла data.txt и выводит их. А теперь разберемся, как же она работает. Сначала строка с именем открываемого файла передается функции os.Open. Эта функция возвращает два значения: указатель os.File, представляющий открытый файл, и значение ошибки. Как и в случае с другими функциями, если значение ошибки равно nil, это означает, что файл был открыт успешно, но любое другое значение указывает на то, что произошла ошибка (например, если файл отсутствует или не читается). В таком случае программа выводит сообщение об ошибке и завершается. |
Чтение текстового файла в массив
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
Пример кода: // Пакет datafile предназначен для чтения данных из файлов. //package datafile package main import ( "fmt" "bufio" "os" "strconv" ) // GetFloats читает значение float64 из каждой строки файла. ошибку. func GetFloats(fileName string) ([3]float64, error) { var numbers [3]float64 // Объявление возвращаемого массива. file, err := os.Open(fileName) //Открывает файл с переданным именем. if err != nil { return numbers, err } i := 0 //Переменная для хранения индекса, по которому должно выполняться присваивание scanner := bufio.NewScanner(file) for scanner.Scan() { numbers[i], err = strconv.ParseFloat(scanner.Text(), 64) if err != nil { return numbers, err } i++ //Переход к следующему индексу массива. } err = file.Close() if err != nil { return numbers, err } if scanner.Err() != nil { return numbers, scanner.Err() } return numbers, nil //Если выполнение дошло до этой точки, значит, ошибок не было, поэтому программа возвращает массив чисел и значение ошибки «nil ». } func main() { fileName := "data.txt" fmt.Println("Hello readfile") fmt.Println(GetFloats(fileName)) } |
Чтение файла и выполнение программы average. Еще один пример:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
//average вычисляет среднее значение package main import ( "bufio" "fmt" "os" "strconv" "log" ) // GetFloats читает значение float64 из каждой строки файла. ошибку. func GetFloats(fileName string) ([3]float64, error) { var numbers [3]float64 // Объявление возвращаемого массива. file, err := os.Open(fileName) //Открывает файл с переданным именем. if err != nil { return numbers, err } i := 0 //Переменная для хранения индекса, по которому должно выполняться присваивание scanner := bufio.NewScanner(file) for scanner.Scan() { numbers[i], err = strconv.ParseFloat(scanner.Text(), 64) if err != nil { return numbers, err } i++ //Переход к следующему индексу массива. } err = file.Close() if err != nil { return numbers, err } if scanner.Err() != nil { return numbers, scanner.Err() } return numbers, nil //Если выполнение дошло до этой точки, значит, ошибок не было, поэтому программа возвращает массив чисел и значение ошибки «nil». } func main() { filename := "data.txt" numbers, err := GetFloats(filename) if err != nil { log.Fatal(err) } var sum float64 = 0 for _, number := range numbers { sum += number } sampleCount := float64(len(numbers)) fmt.Printf("Average: %0.2fn", sum/sampleCount) } Наша программа может обрабатывать только три значения! cat > data1.txt << "EOF" 71.8 56.2 89.5 99.3 EOF Пример ошибки: panic: runtime error: index out of range [3] with length 3 goroutine 1 [running]: main.GetFloats(0xd5676, 0x9, 0x0, 0x0, 0x0, 0x4d458, 0x162060) /home/pi/githabmegafolder/c-test/02_lesson_golang/25_file/02_averange.go:25 +0x2e4 main.main() /home/pi/githabmegafolder/c-test/02_lesson_golang/25_file/02_averange.go:46 +0x34 exit status 2 |
014 / go doc
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
Введите команду "go doc", чтобы вывести документацию по любому пакету или функции. Чтобы вывести документацию по пакету, передайте его путь импорта команде "go doc". Например, информация о пакете "strconv" выводится командой "go doc strconv". go doc -h go doc -u http go doc strconv go doc strconv ParseFloat go doc github.com/headfirstgo/keyboard Команда go doc старается получить полезную информацию на основании анализа кода. Имена пакетов и пути импорта включаются автоматически, как и имена функций, параметры и возвращаемые типы. И все же команда go doc не умеет творить чудеса. Если вы хотите, чтобы пользователи смогли узнать из документации о предназначении пакета или функции, придется добавить эту информацию самостоятельно. К счастью, это делается просто: нужно добавить в код документирующие комментарии. Обычные комментарии Go, размещенные непосредственно перед директивой package или объявлением функции, считаются документирующими комментариями и включаются в вывод go doc. При добавлении документирующих комментариев следует соблюдать ряд правил: - Комментарии должны состоять из полноценных предложений. - Комментарии для пакетов должны начинаться со слова "Package", за которым следует имя пакета: // Package mypackage enables widget management. - Комментарии для функций должны начинаться с имени функции, которую они описывают: // MyFunction converts widgets to gizmos. - В комментарии также можно включать примеры кода, которые должны снабжаться отступами. - Не включайте дополнительные символы для выразительности или форматирования (кроме отступов в примерах кода). Документирующие комментарии будут выводиться в виде простого текста и должны форматироваться соответствующим образом. go help go help gopath go help list |
015 / go arrays / Массивы
Массивы
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
Многие программы работают со списками. Списки адресов. Списки телефонных номеров. Списки товаров. Массив представляет собой набор значений, относящихся к одному типу. Представьте себе таблетницу — вы можете класть и доставать таблетки в любое отделение, но при этом ее удобно переносить как единое целое. Значения, хранящиеся в массиве, называются элементами. Вы можете создать массив строк, массив логических значений или массив любого другого типа Go (даже массив массивов). Весь массив можно сохранить в одной переменной, а затем обратиться к любому нужному элементу. В массивах хранятся наборы значений. Массив содержит заранее заданное количество элементов, а его размер не может увеличиваться или уменьшаться. Чтобы объявить переменную для хранения массива, следует указать количество хранящихся в нем элементов в квадратных скобках ([]), а затем тип элементов в массиве. Чтобы присвоить значения элементам массива или прочитать их позднее, необходимо каким-то образом указать, какой элемент вам нужен. Элементы в массиве нумеруются, начиная с 0. Номер массива называется его индексом. Пример массив нот: var notes [7]string notes[0] = "do" notes[1] = "re" notes[2] = "mi" fmt.Println(notes[0]) fmt.Println(notes[1]) Массив целых чисел: var primes [5]int primes[0] = 2 primes[1] = 3 fmt.Println(primes[0]) Массив значений time.Time: var dates [3]time.Time dates[0] = time.Unix(1257894000, 0) dates[1] = time.Unix(1447920000, 0) dates[2] = time.Unix(1508632200, 0) fmt.Println(dates[1]) Нулевые значения в массивах Как и в случае с переменными, при создании массивов все содержащиеся в них значения инициализируются нулевым значением для типа, содержащегося в массиве. Так массив значений int по умолчанию заполняется нулями. С другой стороны, нулевым значением для строк является пустая строка, так что массив строковых значений по умолчанию заполняется пустыми строками. Нулевые значения позволяют безопасно выполнять операции с элементами массивов, даже если им не были присвоены значения. Например, в следующем массиве хранятся целочисленные счетчики. Любой элемент можно увеличить на 1 даже без предварительного присваивания значения, потому что мы знаем, что все значения счетчиков начинаются с 0. var counters [3]int counters[0]++ // Первый элемент увеличивается с 0 до 1. counters[0]++ // Первый элемент увеличивается с 1 до 2. counters[2]++ // Третий элемент увеличивается с 0 до 1. fmt.Println(counters[0], counters[1], counters[2]) Вывод: 2 0 1 При создании массива все содержащиеся в нем элементы инициализируются нулевым значением для типа, хранящегося в массиве. |
Литералы массивов
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
Если вам заранее известны значения, которые должны храниться в массиве, вы можете инициализировать массив этими значениями в форме литерала массива. Литерал массива начинается как тип массива — с количества элементов в квадратных скобках, за которым следует тип элементов. Далее в фигурных скобках идет список исходных значений элементов массива. Значения элементов должны разделяться запятыми. [3]int{9, 18, 27} | | | | | Список значений, разделенных запятыми. | Тип элементов в массиве. | Количество элементов в массиве. var notes [7]string = [7]string{"do", "re", "mi", "fa", "so", "la", "ti"} fmt.Println(notes[3], notes[6], notes[0]) var primes [5]int = [5]int{2, 3, 5, 7, 11} fmt.Println(primes[0], primes[2], primes[4]) Литералы массивов также позволяют использовать короткие объявления переменных с помощью :=. Короткое объявление переменной. notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"} primes := [5]int{2, 3, 5, 7, 11} Литералы массивов могут распространяться на несколько строк, но перед каждым переносом строки в коде должна стоять запятая. Запятая даже должна стоять после последнего элемента в литерале массива, если за ним следует перенос строки. (На первый взгляд этот синтаксис выглядит неуклюже, но он упрощает последующее добавление новых элементов в коде.) text := [3]string{ // Все это один массив. "This is a series of long strings", "which would be awkward to place", "together on a single line", // Запятая в конце обязательна. } Когда вы занимаетесь отладкой кода, вам не нужно передавать элементы массивов Println и другим функциям пакета fmt один за одним. Просто передайте весь массив. Пакет fmt содержит логику форматирования и вывода массивов. (Пакет fmt также умеет работать с сегментами, картами и другими структурами данных, которые будут описаны позднее.) var notes [3]string = [3]string{"do", "re", "mi"} var primes [5]int = [5]int{2, 3, 5, 7, 11} fmt.Println(notes) fmt.Println(primes) Возможно, вы также помните глагол "%#v", используемый функциями Printf и Sprintf, — он форматирует значения так, как они отображаются в коде Go. При форматировании с "%#v" массивы отображаются в форме литералов массивов Go. fmt.Printf("%#vn", notes) fmt.Printf("%#vn", primes) |
Обращение к элементам массива в цикле
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Вы не обязаны явно записывать целочисленные индексы элементов массивов, к которым обращаетесь в своем коде. В качестве индекса также можно использовать значение целочисленной переменной. notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"} index := 1 fmt.Println(index, notes[index]) // Выводит элемент массива с индексом 1. index = 3 fmt.Println(index, notes[index]) // Выводит элемент массива с индексом 3. for i := 0; i <= 2; i++ { fmt.Println(i, notes[i]) } При обращении к элементам массивов через переменную необходимо действовать внимательно и следить за тем, какие значения индексов используются в программе. Как упоминалось ранее, массивы содержат конкретное число элементов. Попытка обратиться к индексу за пределами массива приводит к панике — ошибке, происходящей во время выполнения программы (а не на стадии компиляции). |
Проверка длины массива функцией «len»
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Написание циклов, которые ограничиваются только правильными индексами, сопряжено с определенным риском ошибок. К счастью, есть пара приемов, которые упрощают этот процесс. Во-первых, вы можете проверить фактическое количество элементов в массиве перед обращением к элементу. Для этого можно воспользоваться встроенной функцией len, которая возвращает длину массива (количество содержащихся в нем элементов). notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"} for i := 0; i < len(notes); i++ { fmt.Println(i, notes[i]) } Впрочем, и здесь существует некоторый риск ошибок. Хотя len(notes) возвращает наибольший индекс, к которому вы можете обращаться, равен 6 (потому что индексирование массивов начинается с 0, а не с 1). При попытке обратиться по индексу 7 возникнет ситуация паники. |
Безопасный перебор массивов в цикле «for…range»
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
В другом, еще более безопасном способе обработки всех элементов массива используется специальный цикл for...range. В форме с range указывается переменная для хранения целочисленного индекса каждого элемента, другая переменная для хранения значения самого элемента и перебираемый массив. Цикл выполняется по одному разу для каждого элемента в массиве; индекс элемента присваивается первой переменной, а значение элемента — второй переменной. В блок цикла включается код для обработки этих значений. for index, value := range myArray { // Блок цикла. } Эта форма цикла for не содержит запутанных выражений инициализации, условия и завершения. А поскольку значение элемента автоматически присваивается переменной, риск обращения к недействительному индексу массива исключен. Форма цикла for с range читается безопаснее и проще, поэтому именно она чаще всего встречается при работе с массивами и другими коллекциями. notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"} for index, note := range notes { fmt.Println(index, note) } Цикл выполняется семь раз, по одному разу для каждого элемента в массиве notes. Для каждого элемента переменной index присваивается индекс элемента, а переменной note присваивается значение элемента. После этого мы выводим индекс и значение. Помните, как при вызове функции с несколькими возвращаемыми значениями мы хотели проигнорировать одно из них? Это значение присваивалось пустому идентификатору ( _ ), чтобы компилятор Go просто отбросил это значение без выдачи сообщения об ошибке... То же самое можно проделать со значениями из циклов "for...range". Если вам не нужен индекс каждого элемента массива, присвойте его пустому идентификатору: notes := [7]string{"do", "re", "mi", "fa", "so", "la", "ti"} for index, note := range notes { fmt.Println(index, note) } А если вам не нужна переменная для значения, замените ее пустым идентификатором |
013 / go Указатели
Указатели
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
Оператор & (амперсанд) используется в Go для получения адреса переменной. Например, следующий код инициализирует переменную, сначала выводит ее значение, а затем адрес переменной... Пример кода: package main import "fmt" func main() { amount := 6 fmt.Println(amount) //Выводит значение переменной fmt.Println(&amount) //Выводит адрес переменной } вывод кода: 6 //Значение переменной. 0x400011c010 //Адрес переменной. Адрес можно получить для переменной любого типа. Обратите внимание, все переменные имеют разные адреса. Еще пример кода: package main import "fmt" func main() { var myInt int fmt.Println(&myInt) var myFloat float64 fmt.Println(&myFloat) var myBool bool fmt.Println(&myBool) } Вывод кода: 0x40000ba010 0x40000ba018 0x40000ba030 И что же собой представляют эти «адреса»? Чтобы найти конкретный дом в плотно застроенном городе, вам нужно знать его адрес... Память, выделяемая компьютером программе, так же переполнена, как и городские улицы. Она забита значениями переменных: логическими значениями, целыми числами, строками и т. д. Зная адрес переменной, вы сможете воспользоваться им для получения значения, хранящегося в переменной. Адрес | 0x1040a100 0x1040a108 0x1040a110 0x1040a118 0x1040a120 0x1040a128 true 6 ... ... ... 3.1415 | значение, хранящееся по этому адресу! !!! Значения, представляющие адреса переменных, называются указателями, потому что они указывают на область памяти, в которой хранится переменная. |
Типы указателей
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
Тип указателя состоит из знака * и типа переменной, на которую ссылается указатель. Например, тип указателя на переменную int записывается в виде *int (читается «указатель на int»). С помощью функции reflect.TypeOf можно вывести типы указателей из приведенной ранее программы: package main import ( "fmt" "reflect" ) func main() { var myInt int fmt.Println(reflect.TypeOf(&myInt)) //Получает указатель на myInt и выводит тип указателя. var myFloat float64 fmt.Println(reflect.TypeOf(&myFloat)) //Получает указатель на myFloat и выводит тип указателя. var myBool bool fmt.Println(reflect.TypeOf(&myBool)) //Получает указатель на myBool и выводит тип указателя.* } В программе можно объявлять переменные, содержащие указатели. В таких переменных могут храниться только указатели на один конкретный тип переменной, так что переменная может содержать только указатели *int, только указатели *float64 и т. д. package main import "fmt" func main() { myInt := 4 //Значение myIntPointer := &myInt //Указатель fmt.Println(myIntPointer) //Выводится сам указатель. fmt.Println(*myIntPointer) //Выводится значение, на которое ссылается указатель. myFloat := 98.6 //Значение myFloatPointer := &myFloat //Указатель fmt.Println(myFloatPointer) //Выводит сам указатель fmt.Println(*myFloatPointer) //Выводится значение, на которое ссылается указатель. myBool := true //Значение myBoolPointer := &myBool ////Указатель fmt.Println(myBoolPointer) //Выводит сам указатель fmt.Println(*myBoolPointer) //Выводится значение, на которое ссылается указатель. Как и с другими типами, при немедленном присваивании исходного значения переменной-указателю можно воспользоваться коротким объявлением переменной. package main import "fmt" func main() { var myBool bool myBoolPointer := &myBool //Короткое объявление переменной-указателя. fmt.Println(myBoolPointer) } |
Чтение или изменение значения по указателю
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
!!! Оператор * может использоваться для обновления значения по указателю. package main import "fmt" func main() { myInt := 4 //Значение fmt.Println(myInt) //Вывести значение myIntPointer := &myInt //Создаем указатель *myIntPointer = 8 //Новое значение присваивается переменной, на которую ссылается указатель (myInt). fmt.Println(*myIntPointer) //Выводится значение переменной, на которую ссылается указатель. fmt.Println(myInt) //Значение переменной выводится напрямую. } /* 4 Исходное значение myInt. 8 Результат обновления *myIntPointer. 8 Обновление значения myInt (то же, что *myIntPointer). */ В приведенном коде команда *myIntPointer = 8 обращается к переменной, на которую ссылается указатель myIntPointer (то есть переменной myInt) и присваивает ей новое значение. Таким образом, обновляется не только значение *myIntPointer, но и myInt |
Использование указателей с функциями
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
Указатели также можно возвращать из функций; просто объявите возвращаемый тип функции как тип указателя. Пример кода: package main import "fmt" func createInt() *int { var myInt = 100 return &myInt } func createPointer() *float64 { var myFloat = 98.5 return &myFloat } func crS() *string { var xX = "hello all" return &xX } func main() { var myFloatPointer *float64 = createPointer() fmt.Println(*myFloatPointer) var x *int = createInt() fmt.Println(*x) var x1 *string = crS() fmt.Println(*x1) } Кстати говоря, в отличие от некоторых других языков, в Go можно вернуть указатель на переменную, локальную по отношению к функции. И хотя эта переменная уже не находится в области видимости, пока у вас есть указатель, Go предоставит вам доступ к значению этой переменной. package main import "fmt" func printPointer(myBoolPointer *bool) { fmt.Println(*myBoolPointer) } func main() { var myBool bool = true printPointer(&myBool) } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
Пакет math/rand содержит функцию Intn, которая сгенерирует случайное число за нас, поэтому в программу следует импортировать math/rand. После этого можно будет вызвать функцию rand.Intn для генерирования случайного числа Пример кода: package main import ( "fmt" "math/rand" ) func main() { target := rand.Intn(100) + 1 fmt.Println(target) } Передайте число функции rand.Intn, и функция вернет случайное число в диапазоне от 0 до переданного числа. Другими словами, если передать аргумент 100, будет получено случайное число в диапазоне 0–99. Так как нам требуется число в диапазоне 1–100, остается прибавить 1 к полученному случайному значению. Результат сохраняется в переменной target. Пока мы ограничимся простым выводом переменной target. Попытавшись запустить программу в таком виде, вы получите случайное число. Однако вы будете получать одно и то же случайное число раз за разом! Чтобы получать разные случайные числа, необходимо передать значение функции rand.Seed. Тем самым вы «инициализируйте» генератор случайных чисел, то есть предоставляете значение, которое будет использоваться для генерирования других случайных чисел. Но если передавать одно и то же значение инициализации, то и случайные значения будут теми же и мы снова вернемся к тому, с чего начинали. Ранее было показано, что функция time.Now выдает значение Time, представляющее текущую дату и время. Его можно использовать для того, чтобы получать разное значение инициализации при каждом запуске программы. Пример кода: package main import ( "fmt" "math/rand" "time" ) func main() { seconds := time.Now().Unix() rand.Seed(seconds) target := rand.Intn(100) + 1 fmt.Println(target) } Функция rand.Seed ожидает получить целое число, поэтому передать ей значение Time напрямую не удастся. Вместо этого для Time следует вызвать метод Unix, который преобразует его в целое число. (А конкретно значение будет преобразовано в формат времени Unix — целое количество секунд, прошедших с 1 января 1970 года. Впрочем, запоминать это не нужно.) Это число передается rand.Seed. |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
Выражение вычисляется, и если полученный результат равен true, то выполняется код в теле условного блока. Если же результат равен false, условный блок пропускается. if true { fmt.Println("I'll be printed!") } if false { fmt.Println("I won't!") } Как и многие другие языки, Go поддерживает множественное ветвление в условных командах. Такие команды записываются в форме if...else if...else. if grade == 100 { fmt.Println("Perfect!") } else if grade >= 60 { fmt.Println("You pass.") } else { fmt.Println("You fail!") } Условные команды используют логическое выражение (результат которого равен true или false), чтобы определить, должен ли выполняться содержащийся в них код. if 1 == 1 { fmt.Println("I'll be printed!") } if 1 >= 2 { fmt.Println("I won't!") } if 1 > 2 { fmt.Println("I won't!") } if 2 <= 2 { fmt.Println("I'll be printed!") } if 1 < 2 { fmt.Println("I'll be printed!") } if 2 != 2 { fmt.Println("I won't!") } Если код должен выполняться только в том случае, когда условие дает результат false, используйте ! — оператор логического отрицания. Этот оператор берет значение true и превращает его в false или же берет значение false и превращает его в true. if !true { fmt.Println("I won't be printed!") } if !false { fmt.Println("I will!") } Если код должен выполняться только в том случае, когда истинны оба условия, используйте оператор && («и»). А если он должен выполняться лишь тогда, когда истинно хотя бы одно из двух условий, используйте оператор || («или»). if true && true { fmt.Println("I'll be printed!") } if false || true { fmt.Println("I'll be printed!") } if true && false { fmt.Println("I won't!") } if false || false { fmt.Println("I won't!") } В: Другой язык программирования требует, чтобы условие команды if заключалось в круглые скобки. В Go это не обязательно? О: Нет. Более того, команда go fmt удалит все круглые скобки, добавленные вами, если только они не используются для определения порядка операций. |
Циклы / Цикл for
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
Пример кода: package main import "fmt" func main() { for x := 4; x <= 6; x++ { fmt.Println("x is now", x) } } Разберем: for x := 4; x <= 6; x++ { fmt.Println("x is now", x) } for - ключевое слово x:=4 - команда инициализации x < 6 - Условное выражение x++ - Операция приращения { - начало блока цикла }конец блока цикла fmt.Println("x is now", x) - тело блока цикла Циклы всегда начинаются с ключевого слова for. В одной из стандартных разновидностей циклов за for следуют три сегмента кода, которые управляют циклом: - Команда инициализации, обычно используемая для инициализации переменной. - Условное выражение, которое определяет, когда следует прервать выполнение цикла. - Оператор приращения, который выполняется после каждой итерации цикла. Команда инициализации часто используется для инициализации переменной; условное выражение обеспечивает выполнение цикла до того, как переменная достигнет определенного значения, и оператор приращения обновляет значение этой переменной. Например, в приведенном фрагменте переменная t инициализируется значением 3, условие обеспечивает выполнение цикла, пока t > 0, а оператор приращения уменьшает t на 1 при каждом выполнении цикла. В конечном итоге t уменьшается до 0 и цикл завершается. package main import "fmt" func main() { for t := 3; t > 0; t-- { fmt.Println(t) } fmt.Println("Blastoff!") } Операторы ++ и -- часто встречаются в командах приращения циклов. ++ увеличивает значение переменной на 1, а -- уменьшает его на 1. Примеры кода: x := 0 x++ fmt.Println(x) x++ fmt.Println(x) x-- fmt.Println(x) for x := 1; x <= 3; x++ { fmt.Println(x) } for x := 3; x >= 1; x-- { fmt.Println(x) } В языке Go также поддерживаются операторы присваивания += и -=. Они получают значение в переменной, добавляют или вычитают другое значение, а затем присваивают результат той же переменной. Примеры кода: x := 0 x += 2 fmt.Println(x) x += 5 fmt.Println(x) x -= 3 fmt.Println(x) Операторы += и -= также могут использоваться в циклах для изменения переменной на величину, отличную от 1. for x := 1; x <= 5; x += 2 { fmt.Println(x) } for x := 15; x >= 5; x -= 5 { fmt.Println(x) } Когда цикл завершается, выполнение программы продолжится с команды, следующей за блоком цикла. При этом цикл продолжает выполняться, пока условное выражение остается истинным. Этот факт может иметь нежелательные последствия; ниже приведены примеры циклов, которые выполняются бесконечно или не выполняются ни одного раза: бесконечный цикл: for x := 1; true; x++ { fmt.Println(x) } Цикл не выполнится никогда: for x := 1; false; x++ { fmt.Println(x) } Будьте осторожны! Цикл может выполняться бесконечно. В этом случае ваша программа никогда не остановится сама. Если это случится, в активном окне терминала нажмите клавишу Control одновременно с клавишей C, чтобы прервать выполнение программы. Операторы инициализации и приращения необязательны. При желании операторы инициализации и приращения в заголовке цикла for можно опустить, оставив только условное выражение (хотя вы должны проследить за тем, чтобы условие в какой-то момент становилось ложным, иначе в программе возникнет бесконечный цикл). x := 1 for x <= 3 { fmt.Println(x) x++ } x := 3 for x >= 1 { fmt.Println(x) x-- } |
Пропуск частей цикла командами continue и break
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
В Go предусмотрены два ключевых слова для управления циклом. Первое — continue — осуществляет немедленный переход к следующей итерации цикла; при этом дальнейший код текущей итерации в блоке цикла пропускается. for x := 1; x <= 3; x++ { fmt.Println("before continue") continue fmt.Println("after continue") } В приведенном выше примере строка "after continue" никогда не выводится, потому что ключевое слово continue всегда выполняет переход к началу цикла — до того, как отработает второй вызов Println. Второе ключевое слово break приводит к немедленному выходу из цикла. Дальнейший код в блоке цикла не отрабатывается, другие итерации цикла не выполняются. Управление передается первой команде, следующей за циклом. for x := 1; x <= 3; x++ { fmt.Println("before break") break fmt.Println("after break") } fmt.Println("after loop") Здесь при первой итерации цикла выводится сообщение "before break", после чего команда break немедленно прерывает цикл; сообщение "after break" не выводится, и цикл не выполняется повторно (хотя без break он бы выполнился еще два раза). Управление передается команде, следующей за циклом. |
010 / go Комментарии
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Самая распространенная форма комментариев обозначается двумя слешами (//). Все символы от // до конца строки рассматриваются как часть комментария. Комментарий // может занимать всю строку или следовать после кода. // Общее количество виджетов в системе. var TotalCount int // Должно быть целым числом. Более редкая форма комментариев занимает несколько строк. Блочные комментарии начинаются с /* и заканчиваются */, а весь текст между этими маркерами (включая символы новой строки) является частью комментария. /* Пакет widget включает все функции, используемые для работы с виджетами. */ |





