Multi process and multi thread programming in Linux

Creation and application of thread in Linux

Multithreading programming technology in Linux is widely used. Multithreading can improve the running efficiency and convenience of programs. Multithreading programming technology is widely used in larger Linux programs. The communication problem between threads is how thread A passes messages to thread B. At present, the main technologies used in inter thread communication are message queue and shared memory. In the implementation of threading mechanism, it is divided into Solaris (earlier) and POSIX. The implementation of multithreading programming in Linux, POSIX specification, and the use of message queue for inter thread communication. Linux platform thread functions need to include header files: #include < pthread h> The header file used by Solaris is < thread h>
Threads can be divided into user level threads and core level threads according to their scheduler.

(1) User level thread

User level thread mainly solves the problem of context switching. Its scheduling algorithm and scheduling process are all selected and decided by the user. It does not need specific kernel support at runtime. Here, the operating system often provides a user space thread library, which provides the functions of thread creation, scheduling and revocation, while the kernel still only manages the process. If a thread in a process calls a blocked system call, the process, including all other threads in the process, is blocked at the same time. The main disadvantage of this user level thread is that it can not give full play to the advantages of multiprocessor in the scheduling of multiple threads in a process.

(2) Core level thread

This thread allows threads in different processes to be scheduled according to the same relative priority scheduling method, which can give full play to the concurrency advantage of multiprocessor.
Now most systems adopt the method of coexistence of user level threads and core level threads. A user level thread can correspond to one or several core level threads, that is, the "one-to-one" or "many to one" model. This can not only meet the needs of multiprocessor system, but also minimize the scheduling overhead.

Linux threads exist in the form of lightweight processes in the core, with independent process table entries, and all operations such as creation, synchronization and deletion are carried out in the pthread library outside the core. The pthread library uses a management thread (_pthread_manager(), which is independent and unique for each process) to manage the creation and termination of threads, assign thread ID s to threads and send thread related signals (such as Cancel), while the caller of the main thread (pthread_create()) transmits the request information to the management thread through the pipeline. For thread programming technology in Linux platform, please refer to the special explanation on Oracle website. The title is Multithreaded Progamming Guide. The link address is:
https://docs.oracle.com/cd/E19455-01/806-5257/index.html
A condition variable blocks the thread until the state of the variable changes.
Mutual exclusion locks are mutually exclusive locks of shared data.
The following is the thread library of POSIX.
pthread_create();// Create a thread using the default properties
pthread_arrt_init();// Create a property object to control the properties of the thread.
pthread_arrt_t tarrt;
pthread_join();// Wait for a thread to end and reclaim thread resources
pthread_detach();// When the thread ends, its resources are added
Single threaded C programs have two kinds of data: local data and global data. Multithreaded C programs add one: thread specific data (TSD).
pthread_key_create();// Create a key to identify thread specific data. Creation does not need to be synchronized, but access must be controlled synchronously.
pthread_key_t key;
pthread_key_delete();// When destroying a key, the programmer should be responsible for releasing the resources associated with the key before destroying it.
pthread_setspecific();// Bind the key.
pthread_getspecific();// Gets the value of the key binding.
pthread_self();// Get thread ID
sched_yield();// A thread of the same level or higher exits the current thread.
pthread_setschedparam();// Sets the priority of the current thread.
pthread_getschedparam();// Gets the priority of the current thread.
pthread_kill();// To send a signal to a thread, the thread must be in the same process.
int pthread_kill(thread_t tid, int sig);
#include<pthread.h>
#include<signal.h>
int sig;
pthread_t tid;
int ret;
ret =pthread_kill(tid, sig);
pthread_exit();// Terminate the current thread
pthread_cancel();// Cancels the execution of a thread.
pthread_setcancelstate();// Make a thread cancellable or non cancellable.
pthread_testcancel();// Create a cancel point.
pthread_arrt_init();// Set the value of the property variable to control the thread.

Creating a Detached Thread / / create a thread that shares the generics
#include <pthread.h>
pthread_attr_t tattr;
pthread_t tid;
void *start_routine;
void arg
int ret;

/* initialized with default attributes */
ret = pthread_attr_init()(&tattr);
ret = pthread_attr_setdetachstate()(&tattr,PTHREAD_CREATE_DETACHED);
ret = pthread_create()(&tid, &tattr, start_routine, arg);
pthread_attr_setstatcksize();// Sets the thread stack size.

On the Linux platform, functions related to thread operation include:

1. Thread creation function pthread_create
2. Wait for another thread to end the function pthread_join
3. Thread separation function pthread_detach
4. Thread self termination function pthread_exit
Multithreaded programs also need to execute necessary mutual exclusion when running, and semaphores can also be used for communication between threads.

Using pthread_create create thread

Creating threads in Linux using pthead_create function,
#inlcude<pthread.h>
int pthread_create();

/*<TCP/IP Network programming page287
* thread1.c
* 057.c Thread instance 1
*/
#include<stdio.h>
#include<pthread.h>
#include<unistd.h> 
void * thread_main(void *arg); 
int main(int argc,char *argv[])
{
	pthread_t t_id;
	int thread_param=5;
	if(pthread_create(&t_id,NULL,thread_main,(void*)&thread_param)!=0)
	{
		puts("pthread_create() error");
		return -1;
	}
	sleep(10); 
	puts("end of main"); 
	return 0;
}

void *thread_main(void *arg)
{
	int i;
	int cnt=*((int*)arg);
	for(i=0;i<cnt;i++)
	{
		sleep(1);
		puts("running thread"); 
	}
	return NULL;
}

"undefined reference to 'pthread_create'", all functions related to threads will have this error, resulting in failure to compile. Reason for the problem: pthread is not the default library under Linux, that is, when linking, the entry address of the brother function in the phread library cannot be found, so the link will fail.
Solution: add the - lpthread parameter when compiling gcc.
gcc -o pt pthread.c -lpthread

Using pthread_join waits for the thread to end

How to recycle resources after the created thread is executed? By default, the created thread is not separated from the original thread. When the original thread calls pthread_join() function to wait for the end of the created thread, and release the occupied resources after the execution of the created thread code. You can also set the created thread to be separate, so that the system will release its resources immediately after the newly created thread runs. Let's look at pthread_ Use of the join method.

/*<Linux Network programming page127
* pthread.c
* 058.c Thread instance 2
*/
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
static int run = 1;								/*Operating status parameters*/
static int retvalue ;							/*Thread return value*/
void *start_routine(void *arg)					/*Thread processing function*/
{
		int *running = arg;						/*Get running state pointer*/
		printf("The child thread is initialized, and the passed in parameter is:%d\n",*running); /*Print information*/
		while(*running)							/*When the running control parameter is valid*/
		{
			printf("The child thread is running\n");			/*Print operation information*/
			usleep(1);							/*wait for*/
		}
		printf("Child thread exit\n");					/*Print exit information*/
		
		retvalue = 8;							/*Set exit value*/
		pthread_exit( (void*)&retvalue);		/*Thread exits and sets the exit value*/
}
int main(void)
{
	pthread_t pt;
	int ret = -1;
	int times = 3;
	int i = 0; 
	int *ret_join = NULL;
	
	ret = pthread_create(&pt, NULL, (void*)start_routine, &run);													/*Establish thread*/
	if(ret != 0)							/*Thread creation failed*/
	{
		printf("Thread creation failed\n");				/*Print information*/
		return 1;							/*return*/
	}	
	usleep(1);								/*wait for*/
	for(;i<times;i++)						/*Print 3 times*/
	{
		printf("Main thread printing\n");				/*Print information*/
		usleep(1);							/*wait for*/
	}
	run = 0;								/*Set the thread exit control value to let the thread exit*/
	pthread_join(pt,(void*)&ret_join);		/*Wait for the thread to exit*/
	printf("The return value of the thread is:%d\n",*ret_join);	/*Exit value of print thread*/
	return 0;
}

Semaphores used in threads

Compiling code with semaphore functions also requires the - lpthread parameter

// 059.c use of semaphores between threads "TCP/IP network programming" page305
#include<stdio.h>
#include<pthread.h> 
#include<semaphore.h>


void * read(void *arg);
void * accu(void *arg);

static sem_t sem_one;
static sem_t sem_two;
static int num; 
int main(int argc,char *argv[])
{
	pthread_t id_t1,id_t2;
	sem_init(&sem_one,0,0);

	sem_init(&sem_two,0,1);
	
	pthread_create(&id_t1,NULL,read,NULL);
	pthread_create(&id_t2,NULL,accu,NULL);
	
	pthread_join(id_t1,NULL);
	pthread_join(id_t2,NULL);
	sem_destroy(&sem_one);
	sem_destroy(&sem_two);
	return 0;
}

void *read(void * arg)
{
	int i;
	for(i=0;i<5;i++)
	{
		fputs("Input num: ",stdout);
		sem_wait(&sem_two);
		scanf("%d",&num);
		sem_post(&sem_one);
	}
	return NULL;
}

void *accu(void * arg)
{
	int sum=0,i;
	for(i=0;i<5;i++)
	{ 
		sem_wait(&sem_one);
		sum+=num; 
		sem_post(&sem_two);
	}
	printf("Result: %d \n",sum);
	return NULL;
}

Network programs using threads

The Linux platform also supports socket communication tasks for each client by creating threads. Start the Ubuntu 64 system, and the actual installation is Ubuntu 12

If you directly use the gcc command, the compilation failure message will appear.

The chat client thread separates sending and receiving

// 060.c chat client using thread "TCP/IP network programming" page310 chat_ clnt. c
//Chat program client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>

#define BUF_SIZE 100
#define NAME_SIZE 20
void* recv_msg(void *arg);
void* send_msg(void *arg);
void error_handling(char *msg);
//global variable 
char name[NAME_SIZE]="[default]";
char msg[BUF_SIZE]; 
int main(int argc,char *argv[])
{	
	int sock;
	struct sockaddr_in serv_adr; 
	pthread_t snd_thread,rcv_thread;
	void * thread_return;
	if(argc!=4)
	{
		printf("Usage: %s <IP> <port> <name>\n",argv[0]);
		exit(1); 
	}
	sprintf(name,"[%s]",argv[3]); 
	//socket resources for connection and communication
	sock=socket(PF_INET,SOCK_STREAM,0);
	memset(&serv_adr,0,sizeof(serv_adr));
	serv_adr.sin_family=AF_INET;
	serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
	serv_adr.sin_port=htons(atoi(argv[2]));
	//Connect to target host 
	if(connect(sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1)
		error_handling("connect() error");
	
	//Create a thread to manage sending data
	pthread_create(&snd_thread,NULL,send_msg,(void *)&sock);
	//Create a thread to manage receiving data
	pthread_create(&rcv_thread,NULL,recv_msg,(void *)&sock);
	//Wait for the sending thread to end
	pthread_join(snd_thread,&thread_return);
	//Wait for the receiving thread to end
	pthread_join(rcv_thread,&thread_return);
	//Close socket file resource
	close(sock);
	return 0;
}

//Send thread code
void * send_msg(void * arg)
{
	int sock=*((int *)arg); 
	char name_msg[NAME_SIZE+BUF_SIZE];
	
	while(1)
	{
		fgets(msg,BUF_SIZE,stdin);
		if(!strcmp(msg,"q\n")||!strcmp(msg,"Q\n"))
		{
			close(sock);
			exit(0);
		}
		sprintf(name_msg,"%s %s",name,msg);
		write(sock,name_msg,strlen(name_msg)); 
	}		 
	return NULL;
}
//Receive message thread code
void *recv_msg(void *arg)
{
	int sock=*((int *)arg); 
	char name_msg[NAME_SIZE+BUF_SIZE];
	int str_len;
	while(1)
	{
		str_len=read(sock,name_msg,NAME_SIZE+BUF_SIZE-1);
		if(str_len==-1)
			return (void*)-1;
		name_msg[str_len]=0;
		fputs(name_msg,stdout);
	}
	return NULL;
} 
//End the process after outputting the error message
void error_handling(char * message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1); 
}

Implementation of multithreaded concurrent server

For each client connection request, the server of the following program creates a new thread for communication. More specifically, it uses the same thread code to generate multiple thread bodies, so the logic of each communication is exactly the same, but the resources used by each thread body are different.

// 061.c concurrent server using thread "TCP/IP network programming" page307 chat_ server. c
//This program performs socket listening and information sending operations
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>

#define BUF_SIZE 100
#define MAX_CLNT 256
void * handle_clnt(void *arg);
void send_msg(char *msg,int len);
void error_handling(char *msg);
//global variable 
int clnt_cnt=0;
int clnt_socks[MAX_CLNT];
pthread_mutex_t mutx;
int main(int argc,char *argv[])
{
	
	int serv_sock,clnt_sock;
	struct sockaddr_in serv_adr,clnt_adr;
	int clnt_adr_sz;
	pthread_t t_id; 
	if(argc!=2)
	{
		printf("Usage: %s <port>\n",argv[0]);
		exit(1); 
	}
	pthread_mutex_init(&mutx,NULL); 
	//socket resources for listening
	serv_sock=socket(PF_INET,SOCK_STREAM,0);
	memset(&serv_adr,0,sizeof(serv_adr));
	serv_adr.sin_family=AF_INET;
	serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
	serv_adr.sin_port=htons(atoi(argv[1]));
	//Binding listening port 
	if(bind(serv_sock,(struct sockaddr*)&serv_adr,sizeof(serv_adr))==-1)
		error_handling("bind() error");
	//Execute listening
	if(listen(serv_sock,5)==-1) 
		error_handling("listen() error");
	while(1)
	{
		clnt_adr_sz=sizeof(clnt_adr);
		//For each client connection request, the server creates a new socket resource for communication
		clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_adr,&clnt_adr_sz);
		
		pthread_mutex_lock(&mutx);
		clnt_socks[clnt_cnt++]=clnt_sock;
		pthread_mutex_unlock(&mutx);
		
		//Create a thread to manage socket communication with the client
		pthread_create(&t_id,NULL,handle_clnt,(void *)&clnt_sock);
		pthread_detach(t_id);
		printf("Connected client IP: %s \n",inet_ntoa(clnt_adr.sin_addr));
	} 
	//Turn off listening socket file resources
	close(serv_sock);
	return 0;
}

//Thread code
void * handle_clnt(void * arg)
{
	int clnt_sock=*((int *)arg);
	int str_len=0,i;
	char msg[BUF_SIZE];
	
	while((str_len=read(clnt_sock,msg,sizeof(msg)))!=0)
		send_msg(msg,str_len);
	pthread_mutex_lock(&mutx);
	for(i=0;i<clnt_cnt;i++)//remove disconnected client
	{
		if(clnt_sock==clnt_socks[i])
		{
			while(i++<clnt_cnt-1)//Move data element forward
				clnt_socks[i]=clnt_socks[i+1];
			break; 
		}
		
	}
	clnt_cnt--;
	pthread_mutex_unlock(&mutx);
	close(clnt_sock);
	return NULL;
}

void send_msg(char * msg,int len)
{
	int i;
	pthread_mutex_lock(&mutx);
	for(i=0;i<clnt_cnt;i++)
		write(clnt_socks[i],msg,len);
	pthread_mutex_unlock(&mutx); 
} 
//End the process after outputting the error message
void error_handling(char * message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1); 
}

The biggest problem with the service program is that there is no exit response, which is an endless loop.

Tags: Python Java Linux network Multithreading

Posted by svenski on Wed, 04 May 2022 11:07:14 +0300