linux processes and threads

Posted by SL-Cowsrule on Sun, 09 Jan 2022 10:26:13 +0100

process

Basic concepts
Process is a dynamic process of a program, and it is also the smallest unit of program execution and resource management allocation.
Process lifecycle
Create - > schedule - > execute - > die
Process status: ready, running, blocked, etc.
Three elements of process
1. Program (instruction and data)
2. Virtual memory
3,PCB
PCB (process control block) is used to record all the information of the process in linux system. It is essentially a task_struct type structure whose data structure mainly contains the following information
1. Process State
2. Scheduling Information
3. Various Identifiers
4. IPC (Inter_Process Communication)
5. Times and Timers
6. Process Links
7. File System
8. Virtual Memory
9. Page management (page)
10. Symmetric multiprocessor SMP
11. Processor Specific Context
12. Other information
Process classification

Process operation

Create process

pid_t fork(void);
Function: the current process creates a child process, which essentially copies the current process (parent process) in memory 0~3G Data space.
Return: successfully returns the child process in the parent process ID(The essence is int Type), failure return-1 Parallel setting errno Value; Successfully returned 0 in the child process.

Copy on write cow (copy on write): only when the write is modified (data is changed) can the copy be made.
Get process ID

pid_t getpid(void);
Function: return the current process ID
pid_t getppid(void);
Function: returns the parent process of the current process ID

Example

#include <stdio.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
	pid_t pid = fork();    //Create child process
	if(pid < 0){    //Creation failed
		perror("fork");
		return -1;    //End process
	}else if(pid > 0){    //This branch is the parent process code
		printf("This is parent process\n");
		printf("The PID of child is %d\n", pid);
	}else{    //This branch is child process code
		printf("This is child process\n");
		printf("The PID is %d\n", getpid());
		printf("The PID of parent is %d\n", getppid());	
    }
	return 0;    //End process
}

Normal end process
1. The main function executes return
2. Execute system call function_ Exit or_ Exit

#include <unistd.h>
void _exit(int status);
#include <stdlib.h>
void _Exit(int status);
Function: end the current process; Close the file descriptor to which it belongs; Its child processes are inherited to init Process( PID==1);send out SIGCHLD Signal to the parent process (inform the parent process to reclaim the process resources).
parameter status: Usually take a constant EXIT_SUCCESS or EXIT_FAILURE,Return to the parent process as the end state of the process
#define EXIT_FAILURE    1   /* Failing exit status.  */
#define EXIT_SUCCESS    0   /* Successful exit status.  */

3. Execute the standard C library function exit

#include <stdlib.h>
void exit(int status);
Function: end the current process (essentially call)_exit);Close the file stream to which it belongs; Refresh standard IO Cache.
parameter status: Same value_exit,take(status & 0377)Return to parent process

Process wait

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
Function: the current process is blocked, waiting for the state of a child process to change and obtain relevant information; The scenarios of state change include the end of the sub process, and the sub process stops or resumes operation after receiving the signal; If the child process ends, the parent process waits to allow the system to release its resources. Otherwise, the child process will remain zombie.
parameter status: Save the status information of the child process
 Return: normal return subprocess ID,Exceptions (including no child processes) returned-1. 
pid_t waitpid(pid_t pid, int *status, int options);
Function: basically the same as wait
 Return: normal return subprocess ID,If non blocking is set and no child process changes state, 0 is returned, and the exception is returned-1. 
parameter pid: if pid>0,Then wait for the process ID==pid Child process of;
if pid=0,Wait for process group ID==Current process ID Child process of;
if pid=-1,Wait for any of its child processes (the same as wait);
if pid<-1,Wait for process group ID==|pid|Child process of.
parameter options: Behavior options, usually from 0 to bitwise or of the following constants;
0(Default blocking (wait)
WNOHANG(Non blocking, return immediately)
WUNTRACED(If not ptrace The child process tracked ends and returns immediately)
WCONTINUED(If a child process receives SIGCONT Signal resume operation, return immediately)
parameter status: You can use the following macro functions to check status;
WIFEXITED(status)
Function: if the subprocess ends normally, return true
WEXITSTATUS(status)
Function: only if WIFEXITED return true When used, it is used to return the exit status of the child process. Its value is a parameter status Lower 8 bits of
WIFSIGNALED(status)
Function: if the subprocess is terminated by a signal, return true

Orphan process: when a process ends, its child process becomes an orphan process and will be automatically entrusted to init process management.
Zombie process: when a process ends and its parent process exists but fails to recover the process resources in time, the process becomes a zombie process; If the resources left by zombie processes fill the kernel scheduling table, new processes cannot be created. Therefore, zombie processes should be avoided.
Solution to zombie process
1. The parent process calls wait/waitpid to block waiting, but can only handle one exiting child process;
2. If the parent process ignores the SIGCHLD signal of the child process, the init process reclaims the legacy resources of its child process;

signal(SIGCHLD, SIG_IGN); // When SIGCHLD is received, the signal is ignored

3. By creating a parent-child process, and then using the orphan process to automatically entrust it to init process management; Examples are as follows

pid_t pid1 pid2;
pid1 = fork(); // Create child process
if(pid1 < 0){  //Child process creation failed
    exit(EXIT_FAILURE);
}else if(pid1 > 0){  //Parent process code branch
	wait(NULL);  //Block waiting for child process to end
}else{  //Subprocess code branch
    pid2 = fork();  //Child process create child process (child process)
    if(pid2 < 0){  //Process creation failed
        exit(EXIT_FAILURE);
    }else if(pid2 > 0){  //Subprocess code branch
        exit(EXIT_SUCCESS);  //The child process ends normally, and the child process is automatically taken over by the init process
    }else{  //Process code branch
        printf("The PID is %d\n", getpid());
    }
}

exec function family

#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
Function: execute another program in the current process. Once the function is called successfully, it will not return; Its essence is to overwrite the 0 of the current process with new program code~3G Memory space, only processes ID Stay the same.
parameter path: Executable with path, for example"./temp/a.out"perhaps shell command"/bin/ls"
parameter file: Executable without path, for example"a.out"perhaps shell command"ls",Based on the environment variables PATH Find the corresponding program
 parameter arg, ...: Command line argument list to NULL End, for example"ls", "-l", NULL
 parameter argv: Array pointer to the command line argument list, for example
char *argv[] = {"ls", "-l", NULL};
parameter envp: Array pointer to the list of environment variables (discarding the environment variables of the original process), for example
char * envp[] = {"HOME=/home/linux", "PATH=/bin:/usr/bin", NULL};
Return: success does not return, failure returns-1 Parallel setting errno Value.

When running a program on the linux command line, its essence is that the shell interpreter (generally bash) calls fork to create a child process, and then the child process calls exec function to execute the program

linux@ubuntu:~/work/temp$ ./a.out
hello world!

Execute shell commands

#include <stdlib.h>
int system(const char *command);
Function: by calling bash implement shell Command (essentially execution)/bin/sh -c command),Execution period SIGCHLD The signal will be blocked, SIGINT and SIGQUIT The signal is ignored and the function returns after execution.
parameter command: shell Command string, for example"ls -l"
Return: return to the command state normally and return to the command state abnormally-1;If the parameter is NULL also sh 0 if unavailable

Create daemon
1. After the child process is created, the parent process ends and the child process is entrusted to init process management.
2. Create a session group and become the leader, leaving the terminal.
3. Change the working directory of the current process to prevent its file system from being unmounted.
4. Change the file permission mask to avoid the inherited permission mask from modifying the permissions of the file created by the current process.
5. Close all file descriptors to avoid resource occupation by inherited file descriptors.
Example

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, const char *argv[])
{
    pid_t pid;
	pid = fork();
	if(pid < 0){
        perror("fork");
        exit(1);
    }else if(pid > 0){
        exit(0); // End of parent process
    }else{
        setsid();  // Create session group
        chdir("/home/linux/"); // Change working directory
        umask(0); // The file permission mask is set to 0
        int fd_num = getdtablesize(); //Gets the number of open file descriptors, including 0
        for(int i = 0; i < fd_num; i++){
            close(i); // Close all file descriptors
        }
        while(1){
            sleep(50);
        }
    }
    return 0;
}

Interprocess communication IPC

Because each process has independent 0-3G memory space in linux system, data interaction between processes cannot be carried out directly, but 7 IPC mechanisms can be realized by using 3-4G kernel space.

Unnamed Pipes

The 64k pipeline space created in the kernel is read and written through the file descriptor. There are independent read and write ends (i.e. half duplex mode). If data is written to the closed pipeline at the read end, SIGPIPE signal will be generated, which can only be used for communication between processes with kinship.

#include <unistd.h>
int pipe(int pipefd[2]);
Function: creating anonymous pipes
 parameter pipefd: The read end obtained after the pipeline is successfully created pipefd[0]And write side pipefd[1],It is essentially a file descriptor.
Return: 0 for success and 0 for failure-1 Parallel setting errno Value.

Example: the child process sends and the parent process receives

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

int main(int argc, const char *argv[])
{
	int fd[2];
	pid_t pid;
	if(-1 == pipe(fd)){  //Create anonymous pipe
		perror("fail to pipe");
		exit(1);
	}

	pid = fork();  //Create child process
	if(pid < 0){
		perror("fail to fork");
		exit(1);
	}else if(pid > 0){   //Parent process
		wait(NULL);  //Block waiting for child process to end
		close(fd[1]);  //Close write side
		char buf[128] = {0};
		read(fd[0],buf,sizeof(buf));  //Read through the reader fd[0]
		printf("parent recv:%s\n",buf);
		exit(0);
	}else{   // Subprocess
		close(fd[0]);  //Close the reader
		char *s1 = "pipe test!";
		printf("child send:%s\n",s1);
		write(fd[1],s1,strlen(s1));  //Write through the write side fd[1]
		exit(0);
	}
	return 0;
}

Famous pipeline

The essence is to create a pipeline file in the file system and read and write through file IO, which can realize the communication between two unrelated processes.

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
Function: create named pipes (pipe files)
parameter pathname: Pipeline file path
 parameter mode: Set file permissions. The actual final file permissions are(mode & ~umask)
Return: 0 for success and 0 for failure-1 Parallel setting errno Value; However, if the reason for the creation failure is that the file already exists, and this situation will not affect the process communication, you should take advantage of this situation errno Value is EEXIST Make a distinction.

Example: process 1 sends and process 2 receives
Process 1

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <fcntl.h>

int main(int argc, const char *argv[])
{
	if(0 != mkfifo("./myfifo",0666)){  //Create a pipe file
		if(errno != EEXIST){  //Exclude file already exists
			perror("fail to mkfifo");
			exit(1);
		}
	}
	int fd;
	char buf[128];
	fd = open("./myfifo",O_WRONLY);  //Open file, write only
	if(fd == -1){
		perror("fail to open");
		exit(1);
	}
	while(1){
		bzero(buf,sizeof(buf));
		fgets(buf,sizeof(buf),stdin);
		write(fd,buf,strlen(buf));  //Write operation
		if(strncmp(buf,"quit",4) == 0){
			exit(0);
		}
	}
	return 0;
}

Process 2

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <fcntl.h>

int main(int argc, const char *argv[])
{
	if(0 != mkfifo("./myfifo",0666)){  //Create a pipe file
		if(errno != EEXIST){  //Exclude file already exists
			perror("fail to mkfifo");
			exit(1);
		}
	}
	int fd;
	char buf[128];
	fd = open("./myfifo",O_RDONLY);  //Open file, read only
	if(fd == -1){
		perror("fail to open");
		exit(1);
	}
	while(1){
		bzero(buf,sizeof(buf));
		read(fd,buf,sizeof(buf));  //Read operation
		printf("recv:%s\n",buf);
		if(strncmp(buf,"quit",4) == 0){
			exit(0);
		}
	}
	return 0;
}

signal

Signal is a kind of soft interrupt, which is maintained and transmitted by the kernel. It adopts unpredictable asynchronous mechanism.
The kernel supports the following types of signals

linux@ubuntu:~$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX

Signals 1 ~ 31 are unreliable signals, that is, they may not be received or ignored by the process, and when multiple identical signals are received at the same time, the process will only process one of them;
The signals 34 ~ 64 are reliable signals, that is, they will be received and processed by the process.
Trigger mode of signal
1) Hardware exception
2) Terminal operation
ctrl + c sends signal 2(SIGINT) to the current process group
ctrl + \ send signal 3(SIGQUIT) to the current process group
ctrl + z sends signal 20(SIGTSTP) to the current process group

to PID Send signal 9 to the process of 4914(SIGKILL)
linux@ubuntu:~$ kill -9 4914

3) Software events

The process usually has the following four processing methods for the received signal
1) Default processing
2) Ignore
3) Capture
4) Block
Note: signal 9(SIGKILL) and signal 19 (SIGSTOP) cannot be ignored, captured or blocked, that is, the processing methods of 2, 3 and 4 cannot be used.
The signal has five types of default functions
Term:Default action is to terminate the process.
Ign:Default action is to ignore the signal.
Core:Default action is to terminate the process and dump core (see core(5)).
Stop:Default action is to stop the process.
Cont:Default action is to continue the process if it is currently stopped.

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
Function: register signal capture with the kernel
 parameter signum: Specified signal value
 parameter handler: It is generally a user-defined signal processing function, which is executed when the process receives a signal; If it is SIG_IGN,The process ignores the signal; If it is SIG_DFL,Default processing is adopted.
Return: if successful, the pointer of the previous processing function of the signal is returned (if not, it is null) NULL),Return if failed SIG_ERR Parallel setting errno;Use this return value if necessary( sighandler_t Type), the code is compiled with -D_GNU_SOURCE. 
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
Function: send signal to process
 parameter pid: Specified process ID;
if pid>0,Send to process ID==pid The process of;
if pid=0,Send to each process of the process group to which the current process belongs;
if pid=-1,It is sent to each process that the current process has permission to send (except init Process);
if pid<-1,Send to process group ID==|pid|For each process.
parameter sig: Transmitted signal value; If 0, no signal is sent and can be used to check processes or process groups ID Whether it exists.
Return: 0 for success and 0 for failure-1 Parallel setting errno value
#include <signal.h>
int raise(int sig);
Function: to send signals to the currently invoked process or thread. In a single threaded process, equivalent to kill(getpid(), int sig),In a multithreaded process, equivalent to pthread_kill(pthread_self(), int sig). 
parameter sig: Signal value
 Return value: 0 for success and non-0 for failure
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
Function: periodically send a process to the currently invoked process. SIGALRM Signal that terminates the process by default.
parameter seconds: How many seconds are timed; If 0, all pending timed alarms are cancelled.
Return: usually returns the remaining time of the last timing. If not, it returns 0
#include <unistd.h>
int pause(void);
Function: blocks the currently invoked process or thread until any signal is received.
Return: returns only when the signal is captured and the signal processing function returns. At this time, it returns-1 Parallel setting errno by EINTR. 

Example:

#include <stdio.h>
#include <signal.h>

/* Signal processing function */
void sig_handler(int signo)
{
	if(signo == SIGQUIT){
		printf("ctrl+ \\ is used!\n");
	}else if(signo == SIGINT){
		printf("ctrl + c is used!\n");
	}else{
		printf("other signal is used!\n");
	}
}

int main(int argc, const char *argv[])
{
	signal(SIGQUIT,sig_handler);  //Register SIGQUIT signal acquisition
	signal(SIGINT,sig_handler);  //Register SIGINT signal acquisition
	signal(SIGKILL,sig_handler);  //Register SIGKILL signal acquisition
	signal(SIGALRM,sig_handler);  //Register SIGALRM signal acquisition
	alarm(5);  //SIGALRM signal is sent at a fixed time of 5s
	while(1){
		sleep(1);
		printf("the process is alive\n");
	}
	return 0;
}

Message queue

The implementation mechanism of message queue is similar to that of pipeline, but different from pipeline. Message queue will package and mark the data sent each time with a type, so that the receiving end can receive selectively according to the type; The message queue and the subsequent shared memory and semaphore set mentioned here belong to the System V standard.

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
Function: generate 4 byte of System V IPC key Value, calculated as(proj_id Lower 8 bits of | The lower 8 digits of the secondary device number of the file system to which the file belongs | pathname Corresponding inode Lower 16 bits of No). 
parameter pathname: The file path must be an existing accessible file
 parameter proj_id: project ID(Non zero value, only the lower 8 bits are valid)
Return value: returned successfully key Value, failure returned-1 Parallel setting errno Value.

View Message Queues from the command line

linux@ubuntu:~/work$ ipcs -q

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

Delete message queue

linux@ubuntu:~/work$ ipcrm -q msqid
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
Function: get a and key Associated message queue; If key Set as IPC_PRIVATE perhaps msgflg Specified IPC_CREAT,Then try to create a new message queue.
parameter key: Key value (4) byte of int Type value), generally through ftok()Get or set to IPC_PRIVATE. 
parameter msgflg: Create flag and permission attributes, which are generally set to(IPC_CREAT | IPC_EXCL | mode),among mode Values can be referenced open()Parameters of mode. 
Return: successfully returns the identifier of the message queue msqid,Failure Return-1 Parallel setting errno Value; When the message queue already exists and msgflg Specified(IPC_CREAT | IPC_EXCL)Set when errno by EEXIST. 
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
Function: send messages to the queue, that is, data queuing
 parameter msqid: Specified message queue identifier
 parameter msgp: Pointer to the message structure. The structure type is as follows
struct msgbuf {
    long mtype;       /* message type, must be > 0 */
    char mtext[1];    /* message data */
};
parameter msgsz: Message data size, i.e. message structure member mtext The size of the; Can be 0, i.e. no mtext. 
parameter msgflg: Behavior attribute, usually set to 0. When the queue is full, it will block waiting until space is available; If specified IPC_NOWAIT,When the queue is full, it will immediately return failure and set errno by EAGAIN. 
Return: 0 for success and 0 for failure-1 Parallel setting errno value
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
Function: receive messages from the queue, that is, data out of the queue
 parameter msqid: Specified message queue identifier
 parameter msgp: Pointer to the message structure, which is used to save the received data
 parameter msgsz: Message structure member mtext Which determines the maximum length of data reception; If the message length is greater than msgsz,The excess part (parameter) is discarded msgflg Specified MSG_NOERROR)Or fail to return and set errno by E2BIG(msgflg Not specified MSG_NOERROR). 
parameter msgtyp: Specify the received message type and match the message structure members mtype;If msgtyp > 0,The type in the receive queue is msgtyp First message of (if any) msgflg Specified MSG_EXCEPT,Is not equal to msgtyp The first message of type); If msgtyp==0,Receive the first message in the queue; If msgtyp < 0,Then no more than|msgtyp|The first message of the smallest type.
parameter msgflg: Behavior attribute, usually set to 0. When there is no message of the specified type in the queue, it will block and wait until there is a message of the required type in the queue; If specified IPC_NOWAIT,When there is no message of the specified type in the queue, it will immediately return the failure and set errno by ENOMSG. 
Return: successfully returns the actual data size obtained, and fails-1 Parallel setting errno Value.
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
Function: operation message queue
 parameter msqid: Specified message queue identifier
 parameter cmd: For the specified control operation, the common values are as follows:
IPC_STAT
 Get and parameters msqid The relevant kernel data is stored in the parameters buf Medium;
IPC_SET
 Will parameter buf The data in the message queue is written into the kernel data structure related to the message queue;
IPC_RMID
 Delete this message queue and wake up all sending or receiving blocked processes (these processes will return errors and set errno by EIDRM). 
parameter buf: Structure pointer, pointing to the data cache space; If cmd==IPC_RMID Can be set to NULL. 
Return: successful return of non negative value (the specific value depends on cmd),Failure Return-1 Parallel setting errno Value.

Example:
Process 1 send data

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

typedef struct msgbuf
{
	long mtype;
	char buf[128];
}MSG;

int main(int argc, const char *argv[])
{

	key_t key;
	int msgid;
	MSG msg;
	int rv;

	key = ftok("/home/linux/",'a');//Get key value

	if(key == -1)
	{
		perror("ftok");
		exit(1);
	}

	msgid = msgget(key,IPC_CREAT | IPC_EXCL | 0664);//Open or create a message queue


	if(msgid == -1)
	{
		if(errno != EEXIST)//If the message queue exists, reopen it
		{
			perror("msgget");
			exit(1);
		}
		else
		{
			msgid = msgget(key,0664);
		}
	}

	msg.mtype = 100;//Sets the type of message to send

	while(1)
	{

		bzero(msg.buf,sizeof(msg.buf));//Empty array

		fgets(msg.buf,sizeof(msg.buf),stdin);//Get the string from the keyboard and write it to MSG buf

		msg.buf[strlen(msg.buf) - 1] = '\0';

		rv = msgsnd(msgid,&msg,sizeof(MSG) - sizeof(long),0);//Sends a message to the specified message queue


		if(rv == -1)
		{
			perror("msgsnd");
			exit(1);
		}

		if(0 == strncmp(msg.buf,"quit;",4))
		{
			break;
		}
	}

	sleep(1);


	if(-1 == msgctl(msgid,IPC_RMID,NULL))//Delete message queue
	{
		perror("msgctl");
		exit(1);
	}

	/*
		//Read message content from message queue
		rv = msgrcv(msgid,&msg1,sizeof(MSG) - sizeof(long),100,0);

		if(rv == -1)
		{
			perror("msgrcv");
			exit(1);
		}

		printf("%s\n",msg1.buf);
	}*/
	return 0;
}

Process 2 receiving data

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

typedef struct msgbuf
{
	long mtype;
	char buf[128];
}MSG;

int main(int argc, const char *argv[])
{

	key_t key;
	int msgid;
	MSG msg;
	int rv;

	key = ftok("/home/linux/",'a');//Get key value

	if(key == -1)
	{
		perror("ftok");
		exit(1);
	}

	msgid = msgget(key,IPC_CREAT | IPC_EXCL | 0664);//Open or create a message queue


	if(msgid == -1)
	{
		if(errno != EEXIST)//If the message queue exists, reopen it

		{
			perror("msgget");
			exit(1);
		}
		else
		{
			msgid = msgget(key,0664);
		}
	}


	while(1)
	{
		rv = msgrcv(msgid,&msg,sizeof(MSG) - sizeof(long),100,0);

		if(rv == -1)
		{
			perror("msgrcv");
			exit(1);
		}

		printf("%s\n",msg.buf);

		if(0 == strncmp(msg.buf,"quit",4))
		{
			break;
		}
	}
	return 0;
}

Shared memory

The implementation principle of shared memory is to apply for a section of real physical memory, and then map it to the virtual memory buffer of multiple processes, so that multiple processes operate on the same section of physical memory. Therefore, when multiple processes access shared memory, synchronization and mutual exclusion should be used. Signals or variables in shared memory can be used to realize synchronization and mutual exclusion between processes.
View Shared Memory Segments from the command line

linux@ubuntu:~/work$ ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x00000000 294912     linux      600        524288     2          dest         
0x00000000 1277953    linux      600        524288     2          dest         
0x00000000 425986     linux      600        524288     2          dest

Delete shared memory

linux@ubuntu:~/work$ ipcrm -m shmid
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
Function: get a piece of and key Associated shared memory, usage and msgget()similar
 parameter key: Key value, usage reference msgget()
parameter size: The size of the shared memory when it is created, at least 4 k(Memory paging)
parameter shmflg: Create flag and permission attributes, usage reference msgget()Parameters of msgflg
 Return: the identifier of the shared memory is returned successfully shmid,Failure Return-1 Parallel setting errno Value; When shared memory already exists and shmflg Specified(IPC_CREAT | IPC_EXCL)Set when errno by EEXIST. 
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
Function: map the shared memory to the space of the current process
 parameter shmid: Shared memory identifier
 parameter shmaddr: Specify the address of the mapping; If set to NULL,The system automatically assigns appropriate mapping addresses; If not NULL meanwhile shmflg Appointed again SHM_RND Flag bit (rounded alignment), the address is offset to an integer multiple of the low boundary address, i.e shmaddr-(shmaddr % SHMLBA),among SHMLBA Is the low-end boundary constant address.
parameter shmflg: Flag and permission attributes; If specified SHM_RDONLY,The process has read-only access to shared memory.
Return value: the shared memory address is returned if successful, and the shared memory address is returned if failed(void *)-1 Parallel setting errno Value.
int shmdt(const void *shmaddr);
Function: cancel a shared memory mapping of the current process
 parameter shmaddr: Shared memory address
 Return value: 0 for success and 0 for failure-1 Parallel setting errno Value.
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
Function: operate shared memory
 parameter shmid: Shared memory identifier
 parameter cmd: Control commands, common values can be referred to msgctl()Parameters of cmd
 parameter buf: Structure pointer, pointing to the data cache space; If cmd==IPC_RMID Can be set to NULL. 
Return value: a non negative value is returned successfully (the specific value depends on cmd),Failure Return-1 Parallel setting errno Value.

Example: two processes send and receive data through shared memory, and use signals for synchronization and mutual exclusion

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/shm.h>
#include <errno.h>
#include <signal.h>
#include <strings.h>
#include <string.h>

typedef struct node
{
	pid_t pid_recv;
	pid_t pid_send;
	char buf[128];
}shm_t;

void sighand(int sig)
{
	;
}

int main(int argc, const char *argv[])
{
	key_t key;
	int shmid;
	shm_t *addr;
	key = ftok("/home/linux/",'a');
	if(key == -1)
	{
		perror("fail to ftok");
		exit(1);
	}

	shmid = shmget(key,sizeof(shm_t),IPC_CREAT|IPC_EXCL|0666);
	if(-1 == shmid)
	{
		if(errno == EEXIST)
		{
			shmid = shmget(key,sizeof(shm_t),0666);//Open directly if the creation is not successful
		}
		else 
		{
			perror("fail to shmget");
			exit(1);
		}
	}

	system("ipcs -m");

	//mapping
	addr = shmat(shmid,NULL,0);
	if(addr == (void *)-1)
	{
		perror("fail to shmat");
		exit(1);
	}

	system("ipcs -m");

	//Registration signal
	signal(SIGUSR1,sighand);

	addr->pid_send = getpid();

	while(1)
	{
		bzero(addr->buf,sizeof(addr->buf));

		fgets(addr->buf,sizeof(addr->buf),stdin);
		
		kill(addr->pid_recv,SIGUSR2);
		if(strncmp(addr->buf,"quit",4) == 0)
			break;
		
		pause();
	}

	shmdt(addr);
	system("ipcs -m");
	return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>

typedef struct node
{
	pid_t pid_recv;
	pid_t pid_send;
	char buf[128];
}shm_t;

void sighand(int sig)
{
	;
}

int main(int argc, const char *argv[])
{
	key_t key;
	int shmid;
	shm_t *addr;
	key = ftok("/home/linux/",'a');
	if(key == -1)
	{
		perror("fail to ftok");
		exit(1);
	}

	shmid = shmget(key,sizeof(shm_t),IPC_CREAT|IPC_EXCL|0666);
	if(-1 == shmid)
	{
		if(errno == EEXIST)
		{
			shmid = shmget(key,sizeof(shm_t),0666);//Open directly if the creation is not successful
		}
		else 
		{
			perror("fail to shmget");
			exit(1);
		}
	}

	system("ipcs -m");

	//mapping
	addr = shmat(shmid,NULL,0);
	if(addr == (void *)-1)
	{
		perror("fail to shmat");
		exit(1);
	}

	system("ipcs -m");

	//register
	signal(SIGUSR2,sighand);

	//dosomething
	addr->pid_recv = getpid();

	//Block first, then receive, and send signal SIGUSR1
	while(1)
	{
		pause();
		printf("recv:%s\n",addr->buf);

		if(strncmp(addr->buf,"quit",4) == 0)
			break;
		kill(addr->pid_send,SIGUSR1);
	}

	shmdt(addr);
	shmctl(shmid,IPC_RMID,NULL);
	system("ipcs -m");
	return 0;
}

Semaphore set

It is essentially a semaphore array, which can realize synchronization and mutual exclusion between processes.
Viewing Semaphore Arrays from the command line

linux@ubuntu:~/work$ ipcs -s

------ Semaphore Arrays --------
key        semid      owner      perms      nsems

Delete semaphore set

linux@ubuntu:~/work$ ipcrm -s semid
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
Function: get a and key Associated semaphore set, usage and msgget()be similar.
parameter key: Key value
 parameter nsems: How many semaphores are included when the semaphore set is created
 parameter semflg: Create flag and permission attributes, usage reference msgget()Parameters of msgflg. 
Return value: semaphore set identifier returned successfully semid,Failure Return-1 And set errno Value; When the semaphore set already exists and semflg Specified(IPC_CREAT | IPC_EXCL)Set when errno by EEXIST. 
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
Function: execute on a semaphore set cmd Specified control operation 
parameter semid: Semaphore set identifier
 parameter semnum: Specifies the number of semaphores in the semaphore set
 parameter cmd: Control commands, common values can be referred to msgctl()Parameters of cmd,also
SETVAL
 Put the third in the collection semnum Values of semaphores semval Set as union semun Consortium members val Value of.
GETVAL
 Return to page semnum Values of semaphores semval. 
SETALL
 The values of all semaphores in the set semval Set as union semun Consortium members array Value of.
GETALL
 Gets the values of all semaphores in the collection semval And kept in the consortium members array Array.
parameter...: Whether this parameter exists depends on cmd Value of; If cmd by IPC_RMID,Omit this parameter; If this parameter is required, its type is union semun Union (calling process customization).
union semun {
    int              val;    /* Value for SETVAL */
    struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
    unsigned short  *array;  /* Array for GETALL, SETALL */
    struct seminfo  *__buf;  /* Buffer for IPC_INFO(Linux-specific) */
};
Return value: a non negative value is returned successfully (the specific value depends on cmd),Failure Return-1 And set errno Value.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
Function: perform operations on the semaphores specified in a semaphore set
 parameter semid: Semaphore set identifier
 parameter sops: struct sembuf Structure pointer of type, pointing to the first address of the structure array (structure members need to be initialized); Its structure members include
    unsigned short sem_num;  /* semaphore number */
    short          sem_op;   /* semaphore operation */
    short          sem_flg;  /* operation flags */
sem_op The values are as follows
> 0 When, proceed V Operation, i.e semval += sem_op,Indicates the release semaphore;
== 0 When, the current process blocks and waits until semval 0;
< 0 When, proceed P Operation, i.e semval -= |sem_op|,Indicates the holding semaphore, if(semval - |sem_op|) < 0 It will block waiting until the semaphore is available;
sem_flg Common values are as follows
== 0 When, set the semaphore as the default operation;
== IPC_NOWAIT Set the semaphore to non blocking. If the process cannot obtain the semaphore, it will return directly EAGAIN Error;
== SEM_UNDO This option causes the kernel to record a record related to the calling process UNDO Record that if the process crashes, according to the UNDO Record the count value of the corresponding semaphore automatically recovered.
parameter nsops: Number of semaphores operated, i.e sops The number of structure array members pointed to.
Return value: 0 for success and 0 for failure-1 And set errno Value.

Example: use semaphore sets between processes to access shared memory synchronously
Custom semaphore operation function

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <errno.h>

int mysem_init(int semid,int n,int value)
{
	union semun {
		int		val;	/* Value for SETVAL */
		struct semid_ds *buf;	/* Buffer for IPC_STAT, IPC_SET */
		unsigned short  *array;	/* Array for GETALL, SETALL */
		struct seminfo  *__buf;	/* Buffer for IPC_INFO(Linux-specific) */
	};

	union semun buf;
	buf.val = value; //The initial value of the semaphore is transmitted through main

	if(-1 == semctl(semid,n,SETVAL,buf))
	{
		perror("fail to semctl");
		return -1;
	}
	return 0;
}

int mysem_wait(int semid,int n)
{
	struct sembuf buf;
	buf.sem_num = n; //Specify the number of semaphores and pass parameters through main
	buf.sem_op = -1; //Semaphore p operation, that is, the value of semaphore - 1
	buf.sem_flg = SEM_UNDO;

	if(-1 == semop(semid,&buf,1))
	{
		perror("fail to semop");
		return -1;
	}
	return 0;
}

int mysem_post(int semid,int n)
{
	struct sembuf buf;
	buf.sem_num = n; //Specify the number of semaphores and pass parameters through main
	buf.sem_op = 1; //Semaphore v operation, that is, the value of semaphore + 1
	buf.sem_flg = SEM_UNDO;

	if(-1 == semop(semid,&buf,1))
	{
		perror("fail to semop");
		return -1;
	}
	return 0;
}

Process 1 sends data to shared memory

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <errno.h>
#include <strings.h>
#include <string.h>

typedef struct node
{
	char buf[128];
}shm_t;   //Defines the structure of shared memory

int main(int argc, const char *argv[])
{
	key_t key;
	int shmid,semid;
	shm_t *addr;
	key = ftok("/home/linux/",'a');
	if(key == -1)
	{
		perror("fail to ftok");
		exit(1);
	}

	//Create shared memory
	shmid = shmget(key,sizeof(shm_t),IPC_CREAT|IPC_EXCL|0666);
	if(-1 == shmid)
	{
		if(errno == EEXIST)
		{
			shmid = shmget(key,sizeof(shm_t),0666);//Open directly if the creation is not successful
		}
		else 
		{
			perror("fail to shmget");
			exit(1);
		}
	}

	system("ipcs -m");

	//Create semaphore set
	semid = semget(key,2,IPC_CREAT|IPC_EXCL|0666);
	if(semid == -1)
	{
		if(errno == EEXIST)
		{
			semid = semget(key,2,0666);
		}
		else 
		{
			perror("fail to semget");
			exit(1);
		}
	}
	system("ipcs -s");
	//mapping
	addr = shmat(shmid,NULL,0);
	if(addr == (void *)-1)
	{
		perror("fail to shmat");
		exit(1);
	}
	while(1)
	{
		mysem_wait(semid,0);//sem0 implements the p operation
		bzero(addr->buf,sizeof(addr->buf));
		fgets(addr->buf,sizeof(addr->buf),stdin);

		mysem_post(semid,1);//sem1 implements the v operation
		if(strncmp(addr->buf,"quit",4) == 0)
			break;
	}
	shmdt(addr);
	system("ipcs -m");
	system("ipcs -s");
	return 0;
}

Process 2 gets data from shared memory

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdlib.h>
#include <sys/shm.h>
#include <errno.h>
#include "mysem.h"
#include <string.h>

typedef struct node
{
	char buf[128];
}shm_t;   //Defines the structure of shared memory

int main(int argc, const char *argv[])
{
	key_t key;
	int shmid,semid;
	shm_t *addr;
	key = ftok("/home/linux/",'a');
	if(key == -1)
	{
		perror("fail to ftok");
		exit(1);
	}

	//Create shared memory
	shmid = shmget(key,sizeof(shm_t),IPC_CREAT|IPC_EXCL|0666);
	if(-1 == shmid)
	{
		if(errno == EEXIST)
		{
			shmid = shmget(key,sizeof(shm_t),0666);//Open directly if the creation is not successful
		}
		else 
		{
			perror("fail to shmget");
			exit(1);
		}
	}
	system("ipcs -m");
	//Create semaphore set
	semid = semget(key,2,IPC_CREAT|IPC_EXCL|0666);
	if(semid == -1)
	{
		if(errno == EEXIST)
		{
			semid = semget(key,2,0666);
		}
		else 
		{
			perror("fail to semget");
			exit(1);
		}
	}
	system("ipcs -s");
	//mapping
	addr = shmat(shmid,NULL,0);
	if(addr == (void *)-1)
	{
		perror("fail to shmat");
		exit(1);
	}
	//do someting
	//initialization  
	mysem_init(semid,0,1);//The value of sem0 is set to 1
	mysem_init(semid,1,0);//Set the value of sem1 to 0
	while(1)
	{
		mysem_wait(semid,1);//sem1 performs the p operation
		printf("recv:%s\n",addr->buf);
		if(strncmp(addr->buf,"quit",4) == 0)
			break;

		mysem_post(semid,0);//sem0 implements the v operation
	}
	shmdt(addr);
	shmctl(shmid,IPC_RMID,NULL);//Delete shared memory
	semctl(semid,0,IPC_RMID); //Delete semaphore set

	system("ipcs -m");
	system("ipcs -s");
	return 0;
}

socket

It belongs to BSD standard (Berkeley Software Distribution)

thread

Basic concepts
Thread is the basic execution unit of a process, which is equivalent to a function with time slice (schedulable execution body). A process can execute multiple threads concurrently.
The difference between thread and process
Process: the smallest unit of resource allocation. The resources between processes are independent.
Thread: the smallest unit of resource scheduling. Multiple threads share the resources of a process, including process ID, heap and global variables. However, each thread has an independent thread ID, thread stack, local variables, program counter and errno in the process buffer.

In linux, thread related interfaces are provided by a separate dynamic library (libpthread.so), so the thread library may be installed before use

linux@ubuntu:~/work/temp$ sudo apt-get install manpages-posix
linux@ubuntu:~/work/temp$ sudo apt-get install manpages-posix-dev

When compiling the source code, you need to link the thread library

linux@ubuntu:~/work/temp$ gcc main.c -lpthread
 perhaps
linux@ubuntu:~/work/temp$ gcc main.c -pthread  // Compatible with other Unix Systems

Thread operation

Thread creation

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
Function: execute a new thread
 parameter thread: Allocated thread ID,It is essentially an unsigned long global variable
 parameter attr: Set the thread property to NULL Represents the default attribute
 parameter start_routine: The entry address of a thread function is essentially a function pointer
 parameter arg: The parameter passed to the new thread during execution is set to NULL No parameters are passed
 Return: 0 for success and thread for failure errno

Thread exit

#include <pthread.h>
void pthread_exit(void *retval);
Function: exit the current thread
 parameter retval: The exit status returned. If not, it is set to NULL

Thread blocking

#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
Function: wait for the specified thread to exit
 parameter thread: Specified thread ID
 parameter retval: The exit state of the receiving thread is essentially the address of the pointer and does not need to be set to NULL
 Return: 0 for success and thread for failure errno

Thread cancellation

#include <pthread.h>
int pthread_cancel(pthread_t thread);
Function: send cancellation request to the specified thread
 parameter thread: Specified thread ID
 Return: 0 for success and thread for failure errno

Example

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

pthread_t tid1,tid2,tid3;

void* fun1(void *arg)  // Define thread function 1
{
	printf("tid1 args:%s\n",(char *)arg);
	while(1){
        printf("I'm pthread1!\n");
    	sleep(1);
	}
	pthread_exit(NULL); // Exit thread
}

void* fun2(void *arg)  // Define thread function 2
{
    printf("tid2 args:%s\n",(char *)arg);	
	while(1){  
	    printf("I'm pthread2!\n");
		sleep(1);
	}
	pthread_exit(NULL); // Exit thread
}

void* fun3(void *arg)  // Define thread function 3
{
	sleep(5);
	pthread_cancel(tid1); // Request to cancel thread 1
	pthread_cancel(tid2); // Request to cancel thread 2
	pthread_exit(NULL); // Exit thread
}

int main(int argc, const char *argv[])
{
	char *s1 = "create pthread1!";	
	char *s2 = "create pthread2!";
	int ret=pthread_create(&tid1,NULL,fun1,s1); // Create thread 1
	if(ret != 0){
		fprintf(stderr,"fail to create tid1:%d\n",ret);
		exit(1);
	}

	ret = pthread_create(&tid2,NULL,fun2,s2); // Create thread 2
	if(ret != 0){
		fprintf(stderr,"fail to create tid2:%d\n",ret);
		exit(1);
	}

	ret = pthread_create(&tid3,NULL,fun3,NULL); // Create thread 3
	if(ret != 0){
		fprintf(stderr,"fail to create tid3:%d\n",ret);
		exit(1);
	}
	char *rt1 = NULL;
	char *rt2 = NULL;
	char *rt3 = NULL;	
	pthread_join(tid1,(void **)&rt1); // Wait for thread 1 to end
	pthread_join(tid2,(void **)&rt2); // Wait for thread 2 to end
	pthread_join(tid3,(void **)&rt3); // Wait for thread 3 to end
	return 0;
}

Synchronization and mutual exclusion

When multiple threads or processes use critical resources (shared resources) at the same time, exceptions are easy to occur. Therefore, synchronization and mutual exclusion should be used to avoid resource competition.
Synchronization: multiple tasks are executed in a specified order
Mutual exclusion: multiple tasks can only execute one of them at the same time point

Semaphore

Semaphores are abstracted as the available quantity of shared resources and can only be accessed through two atomic operations (PV operations). The semaphores mentioned here belong to POSIX standard (Portable Operating System Interface of UNIX).

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
Function: initialize semaphore
 parameter sem: Semaphore addressing
 parameter pshared: Used to indicate whether this semaphore is shared between processes. If it is non-zero, it means shared between processes; otherwise, it means shared between threads. It is usually set to 0.
parameter value: The initial value of the semaphore, the size of which does not exceed SEM_VALUE_MAX
/* Maximum value the semaphore can have.  */
#define SEM_VALUE_MAX   (2147483647)
Return: normal return 0, abnormal return-1 Parallel setting errno value
#include <semaphore.h>
int sem_wait(sem_t *sem);
Function: P Operation (from Dutch) passeren),If the semaphore value is 0, block and wait until the semaphore value>0;If semaphore value>0,Then subtract the semaphore value by 1 (hold semaphore) and continue to execute.
Parameter: semaphore addressing
 Return: normal return 0, abnormal return-1 Parallel setting errno value
#include <semaphore.h>
int sem_post(sem_t *sem);
Function: V Operation (from Dutch) vrijgeven),Increase the semaphore value by 1 (release semaphore)
Parameter: semaphore addressing
 Return: normal return 0, abnormal return-1 Parallel setting errno value
#include <semaphore.h>
int sem_destroy(sem_t *sem);
Function: destroy semaphore
 Parameter: semaphore addressing
 Return: normal return 0, abnormal return-1 Parallel setting errno value

Example: implementing synchronization

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

pthread_t tid1,tid2;
sem_t sem1,sem2;

void* fun1(void *arg)
{
    while(1){
        sem_wait(&sem1); //Wait for semaphore 1 to be available
        printf("pthread1 is running!\n"); //Critical zone
        sleep(1);
        sem_post(&sem2); //Release semaphore 2
    }
    pthread_exit(NULL);
}

void* fun2(void *arg)
{
    while(1){
        sem_wait(&sem2); //Wait for semaphore 2 to be available
        printf("pthread2 is running!\n"); //Critical zone
        sleep(1);
        sem_post(&sem1); //Release semaphore 1
    }
    pthread_exit(NULL);
}

int main(int argc, const char *argv[])
{
    char *s1 = "create pthread1!";	
    char *s2 = "create pthread2!";
    char *rt1 = NULL;
    char *rt2 = NULL;

    int ret_sem = sem_init(&sem1,0,1); //Semaphore 1 is initialized to 1
    if(ret_sem == -1){
        perror("fail to sem_init");
        exit(1);
    }

    ret_sem = sem_init(&sem2,0,0); //Semaphore 2 is initialized to 0
    if(ret_sem == -1){
        perror("fail to sem_init");
        exit(1);
    }

    int ret=pthread_create(&tid1,NULL,fun1,s1); //Create thread 1
    if(ret != 0){
        fprintf(stderr,"fail to create tid1:%d\n",ret);
        exit(1);
    }

    ret = pthread_create(&tid2,NULL,fun2,s2); //Create thread 2
    if(ret != 0){
        fprintf(stderr,"fail to create tid2:%d\n",ret);
        exit(1);
    }

    pthread_join(tid1,(void **)&rt1); //Wait for thread 1 to end
    pthread_join(tid2,(void **)&rt2); //Wait for thread 2 to end
    sem_destroy(&sem1); //Destroy semaphore 1
    sem_destroy(&sem2); //Destroy semaphore 2
    return 0;
}

Example: implement mutual exclusion

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

pthread_t tid1,tid2;
sem_t sem1;

void* fun1(void *arg)
{
    while(1){
        sem_wait(&sem1); //Wait for semaphore 1 to be available
        printf("pthread1 is running!\n"); //Critical zone
        sleep(1);
        sem_post(&sem1); //Release semaphore 1
    }
    pthread_exit(NULL);
}

void* fun2(void *arg)
{
    while(1){
        sem_wait(&sem1); //Wait for semaphore 1 to be available
        printf("pthread2 is running!\n"); //Critical zone
        sleep(1);
        sem_post(&sem1); //Release semaphore 1
    }
    pthread_exit(NULL);
}

int main(int argc, const char *argv[])
{
    char *s1 = "create pthread1!";	
    char *s2 = "create pthread2!";
    char *rt1 = NULL;
    char *rt2 = NULL;

    int ret_sem = sem_init(&sem1,0,1); //Semaphore 1 is initialized to 1
    if(ret_sem == -1){
        perror("fail to sem_init");
        exit(1);
    }

    int ret=pthread_create(&tid1,NULL,fun1,s1); //Create thread 1
    if(ret != 0){
        fprintf(stderr,"fail to create tid1:%d\n",ret);
        exit(1);
    }

    ret = pthread_create(&tid2,NULL,fun2,s2); //Create thread 2
    if(ret != 0){
        fprintf(stderr,"fail to create tid2:%d\n",ret);
        exit(1);
    }

    pthread_join(tid1,(void **)&rt1); //Wait for thread 1 to end
    pthread_join(tid2,(void **)&rt2); //Wait for thread 2 to end
    sem_destroy(&sem1); //Destroy semaphore 1
    return 0;
}

mutex

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
Function: initialize lock
 parameter mutex: Mutex access
 parameter attr: Properties of mutex, NULL Is the default property
 Return: normal returns 0, and abnormal returns the corresponding value errno Value.
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
Function: lock, i.e. block and wait until the lock is available
 parameter mutex: Mutex access
 Return: normal returns 0, and abnormal returns the corresponding value errno Value.
In addition, there is a non blocking mode that returns immediately if the lock is not currently available EBUSY. 
int pthread_mutex_trylock(pthread_mutex_t *mutex);
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
Function: unlock, i.e. release the lock held
 parameter mutex: Mutex access
 Return: normal returns 0, and abnormal returns the corresponding value errno Value.
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
Function: destroy mutex
 parameter mutex: Mutex access
 Return: normal returns 0, and abnormal returns the corresponding value errno Value.

Example: implement mutual exclusion

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

pthread_t tid1,tid2;
pthread_mutex_t  mutex1;
int i = 0,a = 0,b = 0;

void* pth1(void *arg)
{
	while(1){
		pthread_mutex_lock(&mutex1); // Lock
		i++;
		a = i;
		b = i;
		printf("pthread1 is running!\n");
		pthread_mutex_unlock(&mutex1); // Unlock
	}
	pthread_exit(NULL);
}

void* pth2(void *arg)
{
	while(1){
		pthread_mutex_lock(&mutex1); // Lock
		if(a!=b){
			printf("i=%d a=%d b=%d\n",i,a,b);
		}
		printf("pthread2 is running!\n");
		pthread_mutex_unlock(&mutex1); // Unlock
	}
	pthread_exit(NULL);
}

int main(int argc, const char *argv[])
{
	pthread_mutex_init(&mutex1,NULL); // Initialization lock
	pthread_create(&tid1,NULL,pth1,NULL);
	pthread_create(&tid2,NULL,pth2,NULL);

	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	pthread_mutex_destroy(&mutex1); // Destroy mutex
	return 0;
}

Conditional variable

Conditional variables can only be used with mutexes to achieve synchronization.

#include <pthread.h>
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
Function: initializing condition variables
 parameter cond: Conditional variable addressing
 parameter attr: Properties of condition variables, NULL Is the default property
 Return value: 0 for normal operation and the corresponding value for abnormal operation errno Value.
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
Function: unlock after blocking the current thread and wait for the condition variable to arrive before locking
 parameter cond: Conditional variable addressing
 parameter mutex: Mutex access
 Return value: 0 for normal operation and the corresponding value for abnormal operation errno Value.
int pthread_cond_signal(pthread_cond_t *cond);
Function: wake up a thread waiting for condition variables
 parameter cond: Conditional variable addressing
 Return value: 0 for normal operation and the corresponding value for abnormal operation errno Value.
int pthread_cond_broadcast(pthread_cond_t *cond);
Function: wake up all threads waiting for condition variables
 parameter cond: Conditional variable addressing
 Return value: 0 for normal operation and the corresponding value for abnormal operation errno Value.
int pthread_cond_destroy(pthread_cond_t *cond);
Function: destroy condition variables
 parameter cond: Conditional variable addressing
 Return value: 0 for normal operation and the corresponding value for abnormal operation errno Value.

Example: implementing synchronization

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

pthread_t tid1,tid2,tid3;
pthread_mutex_t  mutex1;
pthread_cond_t cond1;

int i = 0,a = 0,b = 0;
char *buf;

void* pth3(void *arg)
{
	while(1)
	{
	//	bzero(buf,128);
		printf("(pth3) please input:\n");
		fgets(buf,128,stdin);
	//	pthread_cond_signal(&cond1);
		pthread_cond_broadcast(&cond1);
	}
	pthread_exit(NULL);
}

void* pth1(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex1); //Lock
		pthread_cond_wait(&cond1,&mutex1); //Wait condition variable
		printf("pth1 buf:%s\n",buf);
		if(strncmp(buf,"quit",4)==0)//Compare the first n characters of the string
		{
			exit(0);
		}
		pthread_mutex_unlock(&mutex1); //Unlock
	}
	pthread_exit(NULL);
}

void* pth2(void *arg)
{
	while(1)
	{
		pthread_mutex_lock(&mutex1); //Lock
		pthread_cond_wait(&cond1,&mutex1); //Wait condition variable
		printf("pth2 buf:%s\n",buf);
		if(strncmp(buf,"quit",4)==0)//Compare the first n characters of the string
		{
			exit(0);
		}
		
		pthread_mutex_unlock(&mutex1); //Unlock
	}
	pthread_exit(NULL);
}

int main(int argc, const char *argv[])
{
	pthread_mutex_init(&mutex1,NULL);//Create mutex
	pthread_cond_init(&cond1,NULL);//Create condition variable	
	buf = malloc(sizeof(char)*128); //Apply for heap space

	pthread_create(&tid3,NULL,pth3,NULL);
	pthread_create(&tid1,NULL,pth1,NULL);
	pthread_create(&tid2,NULL,pth2,NULL);

	pthread_join(tid3,NULL);
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);

    free(buf);
	pthread_cond_destroy(&cond1);//Destroy condition variable
	pthread_mutex_destroy(&mutex1);//Destroy mutex
	return 0;
}

Topics: Linux Multithreading multiple processes