Linux threads explained

Parallel and concurrent difference

1. concurrent (Concurrency) : In the operating system , the time period refers to a number of programs have been started in the run-to-completion between, and these programs are on the same processor running. Two of concurrent relationships are synchronous and mutually exclusive. (Concurrency refers to the moment, only one instruction execution, but multiple processes executing instructions are fast rotation, making it the effect of multiple processes executing simultaneously on a macro - a macro parallel for single-core processor)

  • Mutually exclusive: interprocess mutually exclusive use of critical resources of the phenomenon, called mutually exclusive.
  • Synchronous (synchronous): The relationship between the process of critical resources are not mutually exclusive relationship, but a relationship of interdependence. Further explanation: After the output is used as a pre-process the input of a process, not a process when the first output of the second process must wait. Information about a set of concurrent processes with synchronous relationship with each other is called sending messages or events. ( Have to call each other dependencies should not occur simultaneously, and the synchronization is to prevent those "simultaneous" thing )
  • Wherein there are concurrent concurrent pseudo and true concurrency, concurrent pseudo refers concurrent single-core processor, means true concurrent concurrent multi-core processor.

2. Parallel (Parallelism) : in a uniprocessor multiprogramming system, processes are performed alternately, one kind of special external exhibit concurrent; in a multiprocessor system, the process can be performed alternately only, and may overlap execution . Program on a multiprocessor parallel processing is available. Thus it shows that the parallel is for the purposes of multi-processors. Parallel multiple concurrent events occurring simultaneously, have the meaning concurrent, but not necessarily concurrent parallel, it also does not have to mean the same moment between concurrent events. ( The same time, a number of instructions executed simultaneously on multiple processors - for multi-core processors )

The difference between synchronous and asynchronous

  • Synchronous (Synchronous) : The relationship between the process of critical resources are not mutually exclusive relationship, but a relationship of interdependence. Further explanation: After the output is used as a pre-process the input of a process, not a process when the first output of the second process must wait. Information about a set of concurrent processes with synchronous relationship with each other is called sending messages or events.
  • Asynchronous (Asynchronous) : asynchronous and synchronous are opposite, the synchronization sequence is performed, and then performs a next performed a complete, wait, coordinated operation. Asynchronous is independent of each other, continue to do their own thing while waiting for an event, you do not need to wait for this event after the completion of the work. Thread is to achieve a way asynchronous . Asynchronous method call is to let the main thread does not need to wait for the completion of another thread synchronization, which allows the main thread to do other things.

Multithreading Concepts

What is the thread

  • LWP: light weight process lightweight process , it is still essentially a process (in the Linux environment)
  • Process: independent address space, with PCB
  • Thread: There PCB, but there is no separate address space (shared)
  • Difference: whether a shared address space. Alone (process); shared (thread).
  • Under Linux: thread: the smallest unit of execution, the basic unit of scheduling.
  • Process: the smallest unit of resource allocation, can be regarded as only one thread of the process .

Linux kernel threads implementation principle

Unix-like systems, there is no early "thread" concept, introduced until the 1980s, with the process mechanism to achieve the concept of threads. Therefore, in such systems, processes and threads are closely related.

  • LWP (light-weight process), but also PCB, create the underlying function and process threads used, as are the clone.
  • Look processes and threads are the same, has a different PCB from the kernel, but the PCB pointing to the page table memory resources of the three is the same.
  • The process can be transformed into a thread
  • Thread can be seen as a set of registers and stack
  • In linux, most are small thread of execution units; the process is the smallest unit of resource allocation
  • View LWP Number: ps -Lf pid view lwp number specified thread.

Three maps: Process PCB -> page directory (can be regarded as an array, the first address in the PCB) -> Page Tables -> physical page -> Memory Unit - Reference: "Linux kernel source code Scenario Analysis" ---- Maud operation

  • For the process, the same address (the same virtual address) in a different process, the repeated use without conflict. The reason is that although they are the same virtual address, but, page directory, page tables, each physical page is not the same. The same virtual address mapped to different physical pages in memory unit, and ultimately access to different physical pages.
  • but! Different threads! Two threads have separate PCB, but share the same page directory, it will share the same physical page table and page. Therefore, two PCB share an address space.
  • In fact, both the process of creating a fork, or create a thread pthread_create, call the same underlying implementation is a core function clone.
  • If you copy each other's address space, then output a "process"; if the other party shared address space, it creates a "thread."
  • Therefore: Linux kernel does not distinguish between processes and threads. Only to distinguish on the user level. So, all operating functions pthread_ * thread is a library function, instead of system calls.

Threads share resources

  • 1. file descriptor table
  • 2. The signal processing mode of each
  • 3. The current working directory
  • 4. The user ID and group ID
  • The memory address space (.text / .data / .bss / heap / shared libraries)

Thread non-shared resources

  • 1. thread id
  • 2. The processor stack pointer and the scene (core stack)
  • 3. The independent stack space (user stack)
  • 4.errno variable
  • The mask character signal
  • 6. scheduling priority

Thread advantages and disadvantages

  • Advantages: 1. to improve program concurrency 2. 3. small overhead data communications, data can be easily shared
  • Disadvantages: 1. library function, unstable 2. debug, difficult to write, gdb does not support 3. Signal Support
  • Projecting relative advantages, disadvantages are not flawed. As the lead in Linux implementation processes, threads difference is not great .

Thread control primitives

pthread_self function to get the thread ID. Getpid corresponding to its role in the process () function.

  • pthread_t pthread_self (void); Return value: Success: 0; failed: No!
  • Thread ID: pthread_t type, essentially: at Linux as unsigned integers (% lu), other systems may be implemented structure
  • The thread ID is an internal process, identification. (Between two processes, allowing the same thread ID)
  • Note: You should not use global variables pthread_t tid, in the child thread by thread pthread_create outgoing parameters to get the ID, but you should use pthread_self.

pthread_create function creates a new thread. Its role in the process of the corresponding fork () function.

  • int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
  • Returns: Success: 0; failure: the error number ----- Linux environment, the characteristics of all the threads, the failure are directly returns the error number.

parameter:

  • pthread_t: the current Linux understood as: typedef unsigned long int pthread_t;
  • Parameter 1: Outgoing parameters, save the system assigned thread ID good for us
  • Parameter 2: usually pass NULL, default attributes indicate the use of threads. To use the specific attributes can also be modified parameter.
  • Parameter 3: the function pointer to a function of the primary thread (thread form), the function end of run, the thread ends.
  • Parameter 4: parameters used during the main thread execution function, such as to pass a plurality of parameters, structured packing may be used.
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void *fun(void *arg)
{
	printf("I'm thread, Thread ID = %lu\n", pthread_self());
	return NULL;
}

int main(void)
{
	pthread_t tid;

	pthread_create(&tid, NULL, fun, NULL);
	sleep(1);      // 在多线程环境中,父线程终止,全部子线程被迫终止
	printf("I am main, my pid = %d\n", getpid());

	return 0;
}

operation result

  • After you create a new thread in a thread call pthread_create (), from the current thread pthread_create () returns to continue down the code and the new thread executed by our decision start_routine function pointer passed to pthread_create. start_routine function receives a parameter passed to it by the pthread_create arg parameter, the parameter is the type void *, the pointer is interpreted by what their types are defined by the caller. start_routine return type is void *, the pointer of the same meaning as defined myself by the caller. When start_routine return, withdrew from this thread, other threads can call pthread_join start_routine get the return value, similar to the parent calls wait (2) the exit status of the child process, introduce pthread_join detail later.
  • After a successful return to pthread_create thread id of the newly created thread is filled to the memory unit parameter points. We know the process id of type pid_t, id of each process is unique in the entire system, calling getpid (2) id can get the current process, is a positive integer value. Type thread id is thread_t, it is only guaranteed to be unique in the current process, thread_t this type have different implementations on different systems, it may be an integer value, it could be a structure, it may be an address , it can not simply be treated as an integer with printf to print, call pthread_self (3) can get id of the current thread.

Exercise: circulating create multiple threads, each thread print your own is the first of several threads are created. (Similar to the process loop to create a child process)

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

void *tfn(void *arg)
{
	int i;

	i = (int)arg;
	//i = *((int *)arg);
	sleep(i);	 //通过i来区别每个线程
	printf("I'm %dth thread, Thread_ID = %lu\n", i+1, pthread_self());

	return NULL;
}

int main(int argc, char *argv[])
{
	int n, i;
	pthread_t tid;

	if (argc == 2)
		n = atoi(argv[1]);

	for (i = 0; i < n; i++) 
	{
		pthread_create(&tid, NULL, tfn, (void *)i);  //将i转换为指针,在tfn中再强转回整形。
	}
	sleep(n);
	printf("I am main, and I am not a process, I'm a thread!\n" 
			"main_thread_ID = %lu\n", pthread_self());

	return 0;
}

operation result

Thread shared  between threads share global variables !

  • [Bear in mind]: thread default shared data segment, code segment such as address space, commonly used global variables. The process is not shared global variables, only with mmap .

Program design, global data shared between threads verification.

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

int var = 100;

void *tfn(void *arg)
{
	var = 200;
	printf("thread\n");

	return NULL;
}

int main(void)
{
	printf("At first var = %d\n", var);

	pthread_t tid;
	pthread_create(&tid, NULL, tfn, NULL);
	sleep(1);

	printf("after pthread_create, var = %d\n", var);

	return 0;
}

operation result

pthread_exit function to a single thread exit

  • void pthread_exit (void * retval); Parameters: retval indicates that the thread exit status, usually pass NULL
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void *tfn(void *arg)
{
	int i;

	i = (int)arg; //强转。

	if (i == 2)
		pthread_exit(NULL);
	sleep(i);	 //通过i来区别每个线程

	printf("I'm %dth thread, Thread_ID = %lu\n", i+1, pthread_self());

	return NULL;
}

int main(int argc, char *argv[])
{
	int n , i;
	pthread_t tid;

	if (argc == 2)
		n = atoi(argv[1]);

	for (i = 0; i < n; i++)
	{
		pthread_create(&tid, NULL, tfn, (void *)i);  //将i转换为指针,在tfn中再强转回整形。
	}

	sleep(n);
	printf("I am main, I'm a thread!\n" "main_thread_ID = %lu\n", pthread_self());

	return 0;
}

operation result

Thinking: Using exit the specified thread exits, can it? Conclusion: thread, prohibit the use of exit function causes all threads in all processes exit.

  • In the sleep control without adding output order. pthread_create in the cycle, almost instantaneously create 5 threads, but only the first one thread has the opportunity to output (or the first two have, or may not, depending on the kernel scheduler) If the first three threads executing the exit, the entire process exit , so all the thread exits.
  • So, multi-threaded environment, should be used sparingly, or do not use the exit function, instead use pthread_exit function, a single thread exits. Any thread exit leading to withdraw from the process, not the end of another thread work, can not return or exit when the main thread exits.
  • Notice also that memory cell or return pthread_exit returned pointer points must be global or allocated with a malloc, the thread can not be allocated on the stack function, because when other threads get the function returns a pointer to the thread has been pulled out.

pthread_join waiting thread function exits, the acquiring thread exit status and its role, the corresponding process waitpid () function.

  • int pthread_join (pthread_t thread, void ** retval); Success: 0; failed: No.
  • Parameters: Thread: Thread ID ([Note]: not a pointer); retval: storing thread end state.
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>

typedef struct
{
	int a;
	int b;
} exit_t;

void *tfn(void *arg)
{
	exit_t *ret;
	ret = malloc(sizeof(exit_t)); // malloc分配

	ret->a = 100;
	ret->b = 300;

	sleep(3);
	//pthread_exit((void *)ret);
	return (void *)ret;
}

	int main(void)
{
	pthread_t tid;
	exit_t *retval;

	pthread_create(&tid, NULL, tfn, NULL);

	/*调用pthread_join可以获取线程的退出状态*/
	pthread_join(tid, (void **)&retval);      //wait(&status);
	printf("a = %d, b = %d \n", retval->a, retval->b);
	return 0;
}

Contrast Memory:

  1. Process: main return values, exit parameters -> int; wait for the child ended the wait function parameters -> int *
  2. Thread: thread main function return values, pthread_exit -> void *; waiting thread ends pthread_join function parameters -> void **

The use of non-null parameters retval

The function is called thread hangs wait until terminated as thread id thread. thread thread terminated in different ways, by terminating the state is different pthread_join obtained, summarized as follows:

  1. If the thread through the thread return return retval unit pointed in the return value is stored in thread thread function.
  2. If the thread is called a thread pthread_cancel another thread terminates abnormally out, retval unit is pointing in the store is constant PTHREAD_CANCELED.
  3. If the thread is a thread calls himself pthread_exit terminated, retval pointed unit is stored in the parameters passed to pthread_exit.
  4. If you are not interested in the state of thread to terminate the thread, you can pass NULL to retval parameter.

Exercise: Using multiple child threads pthread_join recovery function will cycle created.

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

int var = 100;

void *tfn(void *arg)
{
	int i;
	i = (int)arg;
    
	sleep(i);
	if (i == 1) 
	{
		var = 333;
		printf("var = %d\n", var);
		return (void *)var;

	}
	else if (i == 3) 
	{
		var = 777;
		printf("I'm %dth pthread, pthread_id = %lu\n var = %d\n", i+1, pthread_self(), var);
		pthread_exit((void *)var);

	} 
	else  
	{
		printf("I'm %dth pthread, pthread_id = %lu\n var = %d\n", i+1, pthread_self(), var);
		pthread_exit((void *)var);
	}

	return NULL;
}

int main(void)
{
	pthread_t tid[5];
	int i;
	int *ret[5];  

	for (i = 0; i < 5; i++)
		pthread_create(&tid[i], NULL, tfn, (void *)i);

	for (i = 0; i < 5; i++) 
	{
		pthread_join(tid[i], (void **)&ret[i]);
		printf("-------%d 's ret = %d\n", i, (int)ret[i]);
	}
        
	printf("I'm main pthread tid = %lu\t var = %d\n", pthread_self(), var);

	sleep(i);
	return 0;
}

operation result

pthread_cancel function kill (canceled) threads its role, the corresponding process kill () function.

  • int pthread_cancel (pthread_t thread); Success: 0; failed: No.
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>

void *tfn1(void *arg)
{
	printf("thread 1 returning\n");
	return (void *)111; 
}

void *tfn2(void *arg)
{
	printf("thread 2 exiting\n");
	pthread_exit((void *)222);
}

void *tfn3(void *arg)
{
	while (1) 
	{
		printf("thread 3: I'm going to die in 3 seconds ...\n");
		sleep(1);

		pthread_testcancel();	//自己添加取消点*/
	}

	return (void *)666;
}

int main(void)
{
	pthread_t tid;
	void *tret = NULL;

	pthread_create(&tid, NULL, tfn1, NULL);
	pthread_join(tid, &tret);
	printf("thread 1 exit code = %d\n\n", (int)tret);

	pthread_create(&tid, NULL, tfn2, NULL);                        
	pthread_join(tid, &tret);
	printf("thread 2 exit code = %d\n\n", (int)tret);

	pthread_create(&tid, NULL, tfn3, NULL);
	sleep(3);
	pthread_cancel(tid);
	pthread_join(tid, &tret);
	printf("thread 3 exit code = %d\n", (int)tret);

	return 0;
}

operation result

  • Cancel threads are not real-time, but there is a certain delay. We need to wait for thread reaches a cancellation point (checkpoint).
  • Archiving is similar to playing a game, you must arrive at the designated place (archive points, such as: the inn, warehouse, city, etc.) in order to store progress. Kill thread nor is it can be done immediately, it must reach the point of cancellation.
  • Cancellation points: whether the thread inspection was canceled, according to a location request action. Usually some system calls creat, open, pause, close, read, write ..... execute the command man 7 pthreads can view the system has canceled these points call list. See also APUE.12.7 cancel options section.
  • It can be roughly considered a system call (into the kernel) that is a cancellation point. As there is no thread cancellation point, you can set a cancellation point itself by calling pthreestcancel function.
  • 被取消的线程, 退出值定义在Linux的pthread库中。常数PTHREAD_CANCELED的值是-1。可在头文件pthread.h中找到它的定义:#define PTHREAD_CANCELED ((void *) -1)因此当我们对一个已经被取消的线程使用pthread_join回收时,得到的返回值为-1。

pthread_detach函数  实现线程分离

  • int pthread_detach(pthread_t thread); 成功:0;失败:错误号
  • 线程分离状态:指定该状态,线程主动与主控线程断开关系。线程结束后,其退出状态不由其他线程获取,而直接自己自动释放。网络、多线程服务器常用。
  • 在线程被分离后,不能使用pthread_join等待它的终止状态。
  • 进程若有该机制,将不会产生僵尸进程。僵尸进程的产生主要由于进程死后,大部分资源被释放,一点残留资源仍存于系统中,导致内核认为该进程仍存在。
  • 也可使用 pthread_create函数参2(线程属性)来设置线程分离。

使用pthread_detach函数实现线程分离 

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

void *tfn(void *arg)
{
	int n = 3;

	while (n--) 
	{
		printf("thread count %d\n", n);
		sleep(1);
}

	return (void *)1;
	//pthread_exit((void *)1);
}

int main(void)
{
	pthread_t tid;
	void *tret;
	int err;

#if 1

	pthread_attr_t attr;			/*通过线程属性来设置游离态*/
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr,	PTHREAD_CREATE_DETACHED);
	pthread_create(&tid, &attr, tfn, NULL);

#else

	pthread_create(&tid, NULL, tfn, NULL);
	pthread_detach(tid);         //让线程分离  ----自动退出,无系统残留资源

#endif

	while (1) 
	{
		err = pthread_join(tid, &tret);
		printf("thread exit code = %d\n", (int)tret);
		printf("-------------err= %d\n", err);
		if (err != 0)
			fprintf(stderr, "thread_join error: %s\n\n", strerror(err));
		else
			fprintf(stderr, "thread exit code %d\n\n", (int)tret);

		sleep(1);
	}

	return 0;
}

运行结果

  • 一般情况下,线程终止后,其终止状态一直保留到其它线程调用pthread_join获取它的状态为止。但是线程也可以被置为detach状态,这样的线程一旦终止就立刻回收它占用的所有资源,而不保留终止状态。不能对一个已经处于detach状态的线程调用pthread_join,这样的调用将返回EINVAL错误。也就是说,如果已经对一个线程调用了pthread_detach就不能再调用pthread_join了。

终止线程方式

总结:终止某个线程而不终止整个进程,有三种方法:

  1. 从线程主函数return。这种方法对主控线程不适用,从main函数return相当于调用exit。
  2. 一个线程可以调用pthread_cancel终止同一进程中的另一个线程。
  3. 线程可以调用pthread_exit终止自己。

控制原语对比

进程            线程

fork             pthread_create

exit             pthread_exit

wait            pthread_join

kill              pthread_cancel

getpid        pthread_self 命名空间

 

Guess you like

Origin blog.csdn.net/qq_22847457/article/details/89371217