Изучаем процессы в linux

Атрибуты нитей

Атрибут Значение по умолчанию Объяснение
scope PTHREAD_SCOPE_PROCESS Нить использует процессорное время, выделяемое процессу. В Solaris 9 и последующих версиях Solaris этот параметр не имеет практического значения
detachstate PTHREAD_CREATE_JOINABLE Нити создаются присоединенными (для освобождения ресурсов после завершения нити необходимо вызвать pthread_join(3C)).
stackaddr NULL Стек нити размещается системой
stacksize Стек нити имеет размер, определяемый системой
priority Нить имеет приоритет 0.
inheritsched PTHREAD_EXPLICIT_SCHED Нить не наследует приоритет у родителя
schedpolicy SCHED_OTHER Нить использует фиксированные приоритеты, задаваемые ОС. Используется вытесняющая многозадачность (нить исполняется, пока не будет вытеснена другой нитью или не заблокируется на примитиве синхронизации)

Завершение нити

pthread_exit(3C)pthread_create(3C)pthread_exit(3C)
Примечание
При компиляции программ на С++ некоторыми версиями компиляторов (наиболее известны проблемы такого рода с GCC, но проблемы есть и у некоторых коммерческих компиляторов), при завершении нити вызовом pthread_exit(3C) не вызываются деструкторы локальных переменных. Информацию об этом не всегда удается найти в документации. В действительности это определяется не столько версией компилятора, сколько опциями при сборке компилятора и особенностями C runtime/libpthread.
Так, компилятор Sun Studio 11 вызывает деструкторы, а GCC 3.4.3, входящий в поставку Solaris 10 – не вызывает.
GCC 2.95.4 в Debian Woody не вызывает деструкторы, GCC 3.3.5 в Debian Sarge – вызывает.
Intel C++ Compiler 7.1 при работе на Debian Woody не вызывает деструкторы.
#include <pthread.h>
#include <unistd.h>
#include <iostream>

class TestClass {
public:
int value;
TestClass(int parameter) {
std::cout << «Constructed » << parameter << «\n»;
value=parameter;
}
~TestClass() {
std::cout << «destructed » << value << «\n»;
}
};

extern «C» {
void *body_forexit(void * param) {
TestClass a(1);

sleep(1);
pthread_exit((void *)a.value);
return (void *)a.value;
}

void *body_forreturn(void * param) {
TestClass b(2);

sleep(2);
return (void *)b.value;
}
} // extern «C»

int main() {

pthread_t thread1, thread2;

pthread_create(&thread1, NULL, body_forexit, NULL);
pthread_detach(thread1);
pthread_create(&thread2, NULL, body_forreturn, NULL);
pthread_detach(thread2);
pthread_exit(NULL);
return 0;
}
Примечание 2
Завершение процесса системным вызовом exit(2) или возвратом из функции main приводит к завершению всех нитей процесса. Это поведение описано в стандарте POSIX, поэтому ОС, которые ведут себя иначе (например, старые версии Linux), не соответствуют стандарту. Если вы хотите, чтобы нити вашей программы продолжали исполнение после завершения main, следует завершать main при помощи вызова pthread_exit(3C).
exit(2)exit(2)_exit(2)BOOST threadspthread_join(3C)malloc(3C)malloc(3C)pthread_cancel(3C)pthread_exit(3C)

Другие решения

Вот что я выкопал из ASIO, и позвольте мне сначала показать простой пример. В 32-разрядной версии CentOS 6.5 и в Eclipse с использованием g ++ 4.9.3 следующий код создается только с -std = c ++ 11, не нужно указывать -lpthread

Как только я добавляю tes2 () в main, происходит сбой неопределенная ссылка на `pthread_create ‘

Во-первых, в автономном ASIO есть код, который использует поток posix, но также может использовать std. Порядок должен быть переставлен так, чтобы сначала использовать std, когда оба определены. Пример кода из строки static_mutex.hpp 27

Во-вторых, есть еще два файла, которые нуждаются в posix: signal_blocker.hpp и tss_ptr.hpp. Make потерпит неудачу, потому что поддерживаются только Windows и POSIX

Я не знаю, возможно ли полностью переписать два файла для использования c ++ 11, но эти два файла являются источником зависимости от -lpthread, а не от std :: thread.

утверждает, что если определяется, это не нуждается в pthread. После того как я определил это в символах, ошибки make упали вдвое, указывая только на является источником этой зависимости.

влияет на signal_set_service.ipp

и select_reactor.ipp

2

Мониторинг и отладка

PID USERNAME SIZE RSS STATE PRI NICE TIME CPU PROCESS/NLWP
744 root 1924K 1264K sleep 59 0 0:00:00 0.0% snmpdx/1
646 root 3440K 1828K sleep 59 0 0:00:00 0.0% syslogd/13
713 root 1680K 796K sleep 59 0 0:00:00 0.0% smcboot/1
714 root 1680K 796K sleep 59 0 0:00:00 0.0% smcboot/1
641 daemon 4216K 3700K sleep 59 0 0:00:01 0.0% nfsmapid/4
635 root 1680K 1032K sleep 59 0 0:00:00 0.0% sac/1
637 root 1788K 1212K sleep 59 0 0:00:00 0.0% ttymon/1
634 root 1048K 720K sleep 59 0 0:00:00 0.0% utmpd/1
639 root 3584K 2576K sleep 59 0 0:00:00 0.0% inetd/3
619 daemon 2476K 1836K sleep 59 0 0:00:00 0.0% statd/1
627 daemon 2040K 1452K sleep 59 0 0:00:00 0.0% lockd/2
578 root 5248K 4068K sleep 59 0 0:00:43 0.0% nscd/33
524 root 6172K 5340K sleep 59 0 0:00:08 0.0% svc.configd/12
522 root 8684K 6448K sleep 59 0 0:00:30 0.0% svc.startd/12
554 daemon 3908K 2332K sleep 59 0 0:00:01 0.0% kcfd/4
625 root 2016K 1500K sleep 59 0 0:00:00 0.0% ypbind/1
520 root 1992K 1228K sleep 59 0 0:00:00 0.0% init/1
507 root 0K 0K sleep 60 – 0:00:00 0.0% zsched/1
5719 belenky 944K 600K sleep 59 0 0:00:00 0.0% a.out/1
PID TTY TIME CMD
Примечание

Отладка многопоточных программ в gdb

threadnothreadno –linespecthreadnoconditionlinespec threadno condition C / C?http://sourceware.org/gdb/documentation/

Отладка многопоточных программ в dbx

Sun Studio?Sun Studio?http://docs.sun.com/app/docs/doc/819-3683
t@1 a l@1 ?() running in main()
t@2 ?() asleep on 0xef751450 in_swtch()
t@3 b l@2 ?() running in sigwait()
t@4 consumer() asleep on 0x22bb0 in _lwp_sema_wait()
>t@5 b l@4 consumer() breakpoint in Queue_dequeue()
t@6 b l@5 producer() running in _thread_start()

l@1 running in main()
l@2 running in sigwait()
l@3 running in _lwp_sema_wait()
>l@4 breakpoint in Queue_dequeue()
l@5 running in _thread_start()

threadid.

«Сырое» создание потока

Мы умеем создавать процессы, используя только системный вызов fork. Спрашивается, можно ли создать вручную поток, не используя библиотеку POSIX Threads. Практической ценности этот навык не несёт.

clone()

#include <sched.h>
 
int clone(int (*fn)(void *), void *child_stack,
          int flags, void *arg, ...
          /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );

Как и fork(), он создаёт новый процесс. Но можно более тонко настроить, что будет скопировано, а что будет общим у дочернего и родительского процесса: адресное пространство, таблица дескрипторов, таблица обработчиков сигналов.

NPTL делает вызов clone() с параметрами

CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_SIGHAND |
CLONE_THREAD | CLONE_SETTLS | CLONE_PARENT_SETTID |
CLONE_CHILD_CLEARTID | CLONE_SYSVSEM

В прикладном коде почти никогда не нужно вызывать clone() вручную для создания потоков. Лучше использовать pthreads, это переносимо и гораздо удобнее.

Пример

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sched.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
 
volatile int g_counter = ;
 
#define STACK_SIZE 4096
 
int ThreadFunc(void* ptr) {
    volatile int* shouldTerminate = ptr;
    while (!*shouldTerminate) {
        printf(" = %d\n", g_counter);
        sleep(1);
    }
    printf(" Terminating...\n");
    return ;
}
 
int main() {
    printf("My pid: %d\n", getpid());
 
    void* stack = malloc(STACK_SIZE);
    if (stack == NULL) {
        perror("Unable to allocate thread stack");
        return 1;
    }
 
    int shouldTerminate = ;
 
    int flag = SIGCHLD | CLONE_SIGHAND | CLONE_FS | CLONE_VM | CLONE_FILES;
    pid_t tid = clone(ThreadFunc, stack + STACK_SIZE, flag, &shouldTerminate);
    if (tid == -1) {
        perror("clone()");
        return 1;
    }
 
    printf("Done! Thread pid: %d\n", tid);
 
    while (1) {
        if (getchar() == EOF) {
            shouldTerminate = 1;
            break;
        }
        g_counter++;
    }
 
    printf("Waiting for thread...\n");
    int status;
    if (waitpid(tid, &status, __WALL) == -1) {
        perror("waitpid()");
    }
 
    free(stack);
    printf("Done! Graceful shutdown.\n");
    return ;
}

В примере обратите внимание на то, что раз на архитектуре x86 стек растёт вниз, то в clone передаётся указатель на конец массива, используемого под стек, а не указатель на начало.

NOTES top

       See pthread_self(3) for further information on the thread ID
       returned in *thread by pthread_create().  Unless real-time
       scheduling policies are being employed, after a call to
       pthread_create(), it is indeterminate which thread—the caller or
       the new thread—will next execute.

       A thread may either be joinable or detached.  If a thread is
       joinable, then another thread can call pthread_join(3) to wait
       for the thread to terminate and fetch its exit status.  Only when
       a terminated joinable thread has been joined are the last of its
       resources released back to the system.  When a detached thread
       terminates, its resources are automatically released back to the
       system: it is not possible to join with the thread in order to
       obtain its exit status.  Making a thread detached is useful for
       some types of daemon threads whose exit status the application
       does not need to care about.  By default, a new thread is created
       in a joinable state, unless attr was set to create the thread in
       a detached state (using pthread_attr_setdetachstate(3)).

       Under the NPTL threading implementation, if the RLIMIT_STACK soft
       resource limit at the time the program started has any value
       other than "unlimited", then it determines the default stack size
       of new threads.  Using pthread_attr_setstacksize(3), the stack
       size attribute can be explicitly set in the attr argument used to
       create a thread, in order to obtain a stack size other than the
       default.  If the RLIMIT_STACK resource limit is set to
       "unlimited", a per-architecture value is used for the stack size.
       Here is the value for a few architectures:

              ┌─────────────┬────────────────────┐
              │Architecture Default stack size │
              ├─────────────┼────────────────────┤
              │i386         │               2 MB │
              ├─────────────┼────────────────────┤
              │IA-64        │              32 MB │
              ├─────────────┼────────────────────┤
              │PowerPC      │               4 MB │
              ├─────────────┼────────────────────┤
              │S/390        │               2 MB │
              ├─────────────┼────────────────────┤
              │Sparc-32     │               2 MB │
              ├─────────────┼────────────────────┤
              │Sparc-64     │               4 MB │
              ├─────────────┼────────────────────┤
              │x86_64       │               2 MB │
              └─────────────┴────────────────────┘

EXAMPLE

pthread_create

In the following run,
on a system providing the NPTL threading implementation,
the stack size defaults to the value given by the
«stack size» resource limit:

$ ulimit -s

8192 # The stack size limit is 8 MB (0x800000 bytes)
$ ./a.out hola salut servus

Thread 1: top of stack near 0xb7dd03b8; argv_string=hola
Thread 2: top of stack near 0xb75cf3b8; argv_string=salut
Thread 3: top of stack near 0xb6dce3b8; argv_string=servus
Joined with thread 1; returned value was HOLA
Joined with thread 2; returned value was SALUT
Joined with thread 3; returned value was SERVUS

In the next run, the program explicitly sets a stack size of 1 MB (using
pthread_attr_setstacksize(3))

for the created threads:

$ ./a.out -s 0x100000 hola salut servus

Thread 1: top of stack near 0xb7d723b8; argv_string=hola
Thread 2: top of stack near 0xb7c713b8; argv_string=salut
Thread 3: top of stack near 0xb7b703b8; argv_string=servus
Joined with thread 1; returned value was HOLA
Joined with thread 2; returned value was SALUT
Joined with thread 3; returned value was SERVUS

Program source

#define handle_error_en(en, msg) \
        do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

#define handle_error(msg) \
        do { perror(msg); exit(EXIT_FAILURE); } while (0)

struct thread_info { /* Used as argument to thread_start() */
    pthread_t thread_id;        /* ID returned by pthread_create() */
    int       thread_num;       /* Application-defined thread # */
    char     *argv_string;      /* From command-line argument */
};

/* Thread start function: display address near top of our stack,
   and return upper-cased copy of argv_string */

static void *
thread_start(void *arg)
{
    struct thread_info *tinfo = arg;
    char *uargv, *p;

    printf(«Thread %d: top of stack near %p; argv_string=%s\n»,
            tinfo->thread_num, &p, tinfo->argv_string);

    uargv = strdup(tinfo->argv_string);
    if (uargv == NULL)
        handle_error(«strdup»);

    for (p = uargv; *p != ‘\0’; p++)
        *p = toupper(*p);

    return uargv;
}

int
main(int argc, char *argv[])
{
    int s, tnum, opt, num_threads;
    struct thread_info *tinfo;
    pthread_attr_t attr;
    int stack_size;
    void *res;

    /* The «-s» option specifies a stack size for our threads */

    stack_size = -1;
    while ((opt = getopt(argc, argv, «s:»)) != -1) {
        switch (opt) {
        case ‘s’:
            stack_size = strtoul(optarg, NULL, 0);
            break;

        default:
            fprintf(stderr, «Usage: %s  arg…\n»,
                    argv);
            exit(EXIT_FAILURE);
        }
    }

    num_threads = argc — optind;

    /* Initialize thread creation attributes */

    s = pthread_attr_init(&attr);
    if (s != 0)
        handle_error_en(s, «pthread_attr_init»);

Создание и ожидание потока

Рассмотрим простой пример

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <conio.h>

#define ERROR_CREATE_THREAD -11
#define ERROR_JOIN_THREAD   -12
#define SUCCESS		   0

void* helloWorld(void *args) {
	printf("Hello from thread!\n");
	return SUCCESS;
}

int main() {
	pthread_t thread;
	int status;
	int status_addr;

	status = pthread_create(&thread, NULL, helloWorld, NULL);
	if (status != 0) {
		printf("main error: can't create thread, status = %d\n", status);
		exit(ERROR_CREATE_THREAD);
	}
	printf("Hello from main!\n");

	status = pthread_join(thread, (void**)&status_addr);
	if (status != SUCCESS) {
		printf("main error: can't join thread, status = %d\n", status);
		exit(ERROR_JOIN_THREAD);
	}

	printf("joined with address %d\n", status_addr);
	_getch();
	return 0;
}

В данном примере внутри основного потока, в котором работает функция main, создаётся новый поток, внутри которого вызывается функция helloWorld. Функция helloWorld выводит на
дисплей приветствие. Внутри основного потока также выводится приветствие. Далее потоки объединяются.

Новый поток создаётся с помощью функции pthread_create

int pthread_create(*ptherad_t, const pthread_attr_t *attr, void* (*start_routine)(void*), void *arg);

Функция получает в качестве аргументов указатель на поток, переменную типа pthread_t, в которую, в случае удачного завершения сохраняет id потока. pthread_attr_t – атрибуты потока.
В случае если используются атрибуты по умолчанию, то можно передавать NULL. start_routin – это непосредственно та функция, которая будет выполняться в новом потоке. arg – это
аргументы, которые будут переданы функции.

Поток может выполнять много разных дел и получать разные аргументы. Для этого функция, которая будет запущена в новом потоке, принимает аргумент типа void*. За счёт этого можно обернуть все передаваемые аргументы в структуру. Возвращать значение можно также через передаваемый аргумент.

В случае успешного выполнения функция возвращает 0. Если произошли ошибки, то могут быть возвращены следующие значения

  • EAGAIN – у системы нет ресурсов для создания нового потока, или система не может больше создавать потоков, так как количество потоков превысило значение
    PTHREAD_THREADS_MAX (например, на одной из машин, которые используются для тестирования, это магическое число равно 2019)
  • EINVAL – неправильные атрибуты потока (переданные аргументом attr)
  • EPERM – Вызывающий поток не имеет должных прав для того, чтобы задать нужные параметры или политики планировщика.

Пройдём по программе

#define ERROR_CREATE_THREAD -11
#define ERROR_JOIN_THREAD   -12
#define SUCCESS		   		  0

Здесь мы задаём набор значений, необходимый для обработки возможных ошибок.

void* helloWorld(void *args) {
	printf("Hello from thread!\n");
	return SUCCESS;
}

Это функция, которая будет работать в отдельном потоке. Она не будет получать никаких аргументов. По стандарту считается, что явный выход из функции вызывает функцию pthread_exit, а возвращаемое значение будет передано при вызове функции pthread_join, как статус.

status = pthread_create(&thread, NULL, helloWorld, NULL);
if (status != 0) {
	printf("main error: can't create thread, status = %d\n", status);
	exit(ERROR_CREATE_THREAD);
}

Здесь создаётся и сразу же исполняется новый поток. Поток не получает никаких атрибутов или аргументов. После создания потока происходит проверка на ошибку.

Вызов

status = pthread_join(thread, (void**)&status_addr);
if (status != SUCCESS) {
	printf("main error: can't join thread, status = %d\n", status);
	exit(ERROR_JOIN_THREAD);
}

Приводит к тому, что основной поток будет ждать завершения порождённого. Функция

int pthread_join(pthread_t thread, void **value_ptr);

Откладывает выполнение вызывающего (эту функцию) потока, до тех пор, пока не будет выполнен поток thread. Когда pthread_join выполнилась успешно, то она возвращает 0.
Если поток явно вернул значение (это то самое значение SUCCESS, из нашей функции), то оно будет помещено в переменную value_ptr.
Возможные ошибки, которые возвращает pthread_join

  • EINVAL – thread указывает на не объединяемый поток
  • ESRCH – не существует потока с таким идентификатором, который хранит переменная thread
  • EDEADLK – был обнаружен дедлок (взаимная блокировка), или же в качестве объединяемого потока указан сам вызывающий поток.

Обновить

Я обнаружил, что ASIO_HAS_PTHREADS определяется не мной, и поэтому ASIO где-то использует потоки POSIX, а компоновщику тогда нужна опция -pthread. Затем я проследил путь к asio / detail / signal_blocker.hpp с помощью директивы #error. Есть только два места, которые он определил, и они находятся в ASIO config.hpp

ASIO по-прежнему отвечает на POSIX THREADS или Windows для signal_blocker.hpp, показанного ниже. Вот почему ASIO все еще нуждается в pthread.

И _PTHREADS определяется из кросс-компилятора gnu (arm-xilinx-linux-gnueabi), включая файлы, такие как features.h, posix_opt.h и т. Д. Я не собираюсь отслеживать, кто действительно определил макрос, но ASIO — это источник, который использует _POSIX_THREADS и поэтому опция компоновщика -pthread должна быть там.

Опять же, не ASIO C ++ 11 поток не нуждается в -pthread для g ++ 4.9.2, но автономный ASIO нуждается в этом. Следующий код построен правильно без -pthread в g ++ 4.9.2 (Xilinx SDK, основанный на Eclipse):

5

В кодовом блоке появляется неопределенная ссылка на ошибку symbol’pthread_create @@ GLIBC_2.2.5.

Позже я вспомнил, что использовал в программе поток std :: thread на C ++ 11. Я лично предполагаю, что C ++ 11 должен использовать pthread при инкапсуляции потока std :: thread, поэтому std используется в программе под Linux. :: поток, следующая информация должна быть добавлена:

Поскольку библиотека pthread не является библиотекой по умолчанию в системе Linux, при подключении вам необходимо использовать библиотеку libpthread.a, поэтому при использовании pthread_create для создания потока необходимо добавить параметр -lpthread в компиляцию:

gcc   pthread.c -lpthread -o pthread

Ниже приведена модификация кодовых блоков:

Настройки >> Настройки компилятора >> Глобальные настройки компилятора >> Настройки компоновщика:

Добавьте библиотеку ссылок слева: /usr/lib/x86_64-linux-gnu/libpthread.a (черезнайдите libpthread.a определяется)

Добавьте другие параметры компоновщика справа:-lpthread

Если ваш libpthread.a не находится под lib64, вы можете использовать следующую команду, чтобы найти его:

$ locate libpthread.a

Интеллектуальная рекомендация

1. Для реальных сигналов (для понимания): A (ω) является соотношением амплитуды выходного сигнала и амплитуды входного сигнала, называемого частотой амплитуды. Φ (ω) — это разница межд…

Один. вести Многие люди задавали некоторые вопросы о создании проекта Flex + LCDS (FDS) в сообщениях и группах. Из-за операции ее трудно четко объяснить, поэтому я написал простой учебник (я обещал эт…

package com.example.phonehttp; import android.os.Bundle; import android.os.Handler; import android.app.Activity; import android.widget.ScrollView; import android.widget.TextView; public class MainActi…

Он предназначен для реализации подкласса того же родительского класса с родительским классом. Полиморфизм Один и тот же ссылочный тип использует разные экземпляры для выполнения разных операций; Идея …

тема: Объедините два упорядоченных слоя в новый заказанный список и возврат. Новый список состоит из всех узлов двух связанных списков, данных сплавным. Пример: Анализ: два связанных списка состоит в …

Вам также может понравиться

D. Самая ценная строка Пример ввода 2 2 aa aaa 2 b c Образец вывода aaa c На самом деле, будучи задетым этим вопросом, вы должны быть осторожны. После инвертирования строки, если две строки имеют один…

Given a 2D integer matrix M representing the gray scale of an image, you need to design a smoother to make the gray scale of each cell becomes the average gray scale (rounding down) of all the 8 surro…

calc () может быть очень незнакомым для всех, и трудно поверить, что calc () является частью CSS. Поскольку он выглядит как функция, почему он появляется в CSS, поскольку это функция? Этот момент такж…

Основываясь на дереве регрессии, сформированном CART, а также на предварительной и последующей обрезке дерева, код выглядит следующим образом:…

Откат Обновление в режиме онлайн с версии Centos (CentOS Linux версии 7.3.1611 (Core) до CentOS Linux версии 7.5.1804 (Core)) # ошибка соединения yum-ssh после обновления yexpected key exchange group …

Пример создания потоков с передачей им аргументов

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

Так как функция может получать только указатель типа void, то все аргументы следует упаковать в структуру. Определим новый тип структуру:

typedef struct someArgs_tag {
	int id;
	const char *msg;
	int out;
} someArgs_t;

Здесь id – это идентификатор потока (он в общем-то не нужен в нашем примере), второе поле это строка, а третье длина строки, которую мы будем
возвращать.

Внутри функции приводим аргумент к нужному типу, выводим на печать строку и засовываем в структуру обратно вычисленную длину строки.

void* helloWorld(void *args) {
	someArgs_t *arg = (someArgs_t*) args;
	int len;

	if (arg->msg == NULL) {
		return BAD_MESSAGE;
	}

	len = strlen(arg->msg);
	printf("%s\n", arg->msg);
	arg->out = len;

	return SUCCESS;
}

В том случае, если всё прошло удачно, то в качестве статуса возвращаем значение SUCCESS, а если была допущена ошибка (в нашем случае, если передана нулевая строка), то выходим со статусом BAD_MESSAGE.

В этом примере создадим 4 потока. Для 4-х потоков понадобятся массив типа pthread_t длинной 4, массив передаваемых аргументов и 4 строки, которые мы и будем передавать.

pthread_t threads;
int status;
int i;
int status_addr;
someArgs_t args;
const char *messages[] = {
	"First",
	NULL,
	"Third Message",
	"Fourth Message"
};

Первым делом заполняем значения аргументов.

for (i = 0; i < NUM_THREADS; i++) {
	args.id = i;
	args.msg = messages;
}

Далее создаём в цикле новые потоки

for (i = 0; i < NUM_THREADS; i++) {
	status = pthread_create(&threads, NULL, helloWorld, (void*) &args);
	if (status != 0) {
		printf("main error: can't create thread, status = %d\n", status);
		exit(ERROR_CREATE_THREAD);
	}
}

Затем ждём завершения

for (i = 0; i < NUM_THREADS; i++) {
	status = pthread_join(threads, (void**)&status_addr);
	if (status != SUCCESS) {
		printf("main error: can't join thread, status = %d\n", status);
		exit(ERROR_JOIN_THREAD);
	}
	printf("joined with address %d\n", status_addr);
}

Под конец ещё выводим аргументы, которые теперь хранят возвращённые значения. Заметьте, что один из аргументов «плохой» (строка равна NULL). Вот полный код

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <conio.h>
#include <string.h>

#define ERROR_CREATE_THREAD -11
#define ERROR_JOIN_THREAD   -12
#define BAD_MESSAGE			-13
#define SUCCESS				  0

typedef struct someArgs_tag {
	int id;
	const char *msg;
	int out;
} someArgs_t;

void* helloWorld(void *args) {
	someArgs_t *arg = (someArgs_t*) args;
	int len;

	if (arg->msg == NULL) {
		return BAD_MESSAGE;
	}

	len = strlen(arg->msg);
	printf("%s\n", arg->msg);
	arg->out = len;

	return SUCCESS;
}

#define NUM_THREADS 4

int main() {
	pthread_t threads;
	int status;
	int i;
	int status_addr;
	someArgs_t args;
	const char *messages[] = {
		"First",
		NULL,
		"Third Message",
		"Fourth Message"
	};

	for (i = 0; i < NUM_THREADS; i++) {
		args.id = i;
		args.msg = messages;
	}

	for (i = 0; i < NUM_THREADS; i++) {
		status = pthread_create(&threads, NULL, helloWorld, (void*) &args);
		if (status != 0) {
			printf("main error: can't create thread, status = %d\n", status);
			exit(ERROR_CREATE_THREAD);
		}
	}

	printf("Main Message\n");

	for (i = 0; i < NUM_THREADS; i++) {
		status = pthread_join(threads, (void**)&status_addr);
		if (status != SUCCESS) {
			printf("main error: can't join thread, status = %d\n", status);
			exit(ERROR_JOIN_THREAD);
		}
		printf("joined with address %d\n", status_addr);
	}

	for (i = 0; i < NUM_THREADS; i++) {
		printf("thread %d arg.out = %d\n", i, args.out);
	}

	_getch();
	return 0;
}

Выполните его несколько раз. Заметьте, что порядок выполнения потоков не детерминирован. Запуская программу, можно каждый раз получить другой порядок выполнения.

Q&A

Всё ещё не понятно? – пиши вопросы на ящик

Основы Pthreads

Библиотека pthreads определяет набор типов, функций, констант для языка C. Заголовочный файл называется pthread.h.

Там около сотни функций, все начинаются на pthread_, могут быть отнесены к четырём группам:

  • Thread management — creating, joining threads etc.
  • Mutexes
  • Condition variables
  • Synchronization between threads using read/write locks and barriers

Важно: функции не выставляют значение errno. Большинство функций библиотеки спроектированы так, что возвращают 0 в случае успеха и код ошибки (положительное число) в случае неудачи.. Само по себе errno реализуется не просто как глобальная переменная, а как макрос, который раскрывается в thread-specific-переменную

Поэтому обрабатывать ошибки POSIX-функций через errno в разных потоках можно безопасно, состояния гонки при этом не возникает.

Само по себе errno реализуется не просто как глобальная переменная, а как макрос, который раскрывается в thread-specific-переменную. Поэтому обрабатывать ошибки POSIX-функций через errno в разных потоках можно безопасно, состояния гонки при этом не возникает.

Пример

Из википедии.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
 
#define NUM_THREADS 5
 
void* perform_work(void* argument) {
  int passed_in_value;
 
  passed_in_value = *((int*) argument);
  printf("Hello World! It's me, thread with argument %d!\n", passed_in_value);
 
  return NULL;
}
 
int main(int argc, char** argv) {
  pthread_t threadsNUM_THREADS;
  int thread_argsNUM_THREADS;
  int result_code;
  unsigned index;
 
  // create all threads one by one
  for (index = ; index < NUM_THREADS; ++index) {
    thread_args index  = index;
    printf("In main: creating thread %d\n", index);
    result_code = pthread_create(&threadsindex, NULL, perform_work, &thread_argsindex);
    assert(!result_code);
  }
 
  // wait for each thread to complete
  for (index = ; index < NUM_THREADS; ++index) {
    // block until thread 'index' completes
    result_code = pthread_join(threadsindex, NULL);
    assert(!result_code);
    printf("In main: thread %d has completed\n", index);
  }
 
  printf("In main: All threads completed successfully\n");
  exit(EXIT_SUCCESS);
}

Команда lshw

Команда lshw позволяет использовать утилиту с соответствующим именем для получения информации об аппаратном обеспечении компьютера. Данная утилита позволяет выводить информацию об отдельных классах устройств, использовать различное форматирование вывода, а также удалять из вывода информацию, позволяющую идентифицировать устройства (серийные номера и подобные идентификаторы), что делает ее идеальным вариантом для формирования списка устройств, который можно передавать третьим лицам. Аналогами данной утилиты являются hwinfo и inxi.

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

Перед тем, как говорить о параметрах утилиты, следует обратить внимание на тот факт, что ее интерфейс немного отличается от интерфейсов большинства других утилит командной строки для Linux: вам придется использовать один, а не два дефиса перед полными именами параметров. Утилита поддерживает несколько форматов вывода результатов: HTML (активируется с помощью параметра -html), XML (активируется с помощью параметра -xml), табличный с путями sysfs (активируется с помощью параметра -short) и табличный с адресами шин (активируется с помощью параметра -businfo)

Что касается параметров, то наиболее актуальными являются такие параметры, как -class для указания интересующего класса устройств, -enable и -disable для активации и деактивации определенных тестов, -sanitize для удаления идентификационных данных устройств, -numeric для вывода цифровых идентификаторов и -notime для удаления из вывода меток времени. Для ознакомления со списком классов устройств используемого компьютера следует использовать утилиту с параметром -short. Что касается тестов, то вы можете использовать следующие идентификаторы: dmi (доступ к расширениям DMI/SMBIOS), device-tree (доступ к интерфейсу OpenFirmware Device Tree), spd (доступ к интерфейсу Serial Presence Detect оперативной памяти), memory (эвристическое определение объема оперативной памяти), cpuinfo (доступ к информации о ЦП от ядра ОС), cpuid (доступ к информации от ЦП), pci (доступ к информации от устройств PCI/AGP), isapnp (доступ к расширениям ISA PnP), pcpcia (доступ к интерфейсу PCMCIA/PCCARD), ide (доступ к информации от устройств IDE/AT-API), usb (доступ к информации от устройств USB), scsi (доступ к информации от устройств SCSI), network (определение параметров сетевых интерфейсов).

Установка утилиты

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

Команда для Linux Mint, Ubuntu и Debian:

Команда для Fedora Workstation:

Вывод полной информации обо всех устройствах компьютера

Для вывода полной информации обо всех устройствах компьютера достаточно использовать утилиту lshw без каких-либо параметров:

Такой режим работы утилиты не всегда удобен, ведь вывод содержит огромное количество лишних деталей.

Вывод краткой информации обо всех устройствах компьютера

Для вывода краткой информации обо всех устройствах компьютера следует использовать лишь параметры -short или -businfo утилиты lshw:

Очевидно, что в данном случае вывод представлен в виде таблицы, содержащей пути в sysfs, классы устройств и их названия.

Вывод краткой информации об отдельных классах устройств

Для вывода информации об интересующих пользователя классах устройств достаточно указать класс этих устройств:

В данном случае была выведена информация лишь о центральном процессоре.

Сохранение журнала работы утилиты в файле

Для сохранения журнала работы утилиты в файле достаточно использовать параметр форматирования вывода и перенаправить вывод в файл:

Разумеется, третьим лицам лучше передавать файл в формате HTML, использовав при его формировании параметр -sanitize.

×

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Все про сервера
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: