“This is the 17th day of my participation in the August More Text Challenge.
Condition variables,
A condition variable is a mechanism specified to accomplish synchronization between threads by suspending a thread on hold. Then the other side sends a signal that the condition is true to wake it up and continue to operate.
Disadvantages: The waiting party must acquire the lock first
The function interface
1. Statically initialize condition variables
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
2. Dynamically initialize condition variables
-
Prototype:
int pthread_cond_init(pthread_cond_t * cond, pthread_condattr_t *cond_attr); Copy the code
-
Parameters:
- Cond: address of the condition variable
- Cond_attr: use the default NULL mode
-
The return value:
- Returns 0 on success
- Failure returns a non-zero value
3.thread_cond_wait
-
Int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
-
Function: active suspension waiting for the signal
-
Parameters:
- Cond: address of the condition variable
- Mutex: address of the lock variable
-
Note: The wait function is bound to a lock, and when cond — wait is run,
1. Unlock the lock first and give up the right to use it
2. Then suspend the thread
3. When the signal arrives, try locking before you are ready to run
1.2 The two steps are atomic operations
-
The return value:
- Returns 0 on success
- Failure returns a non-zero value
4.pthread_cond_signal
- Prototype:
int pthread_cond_signal(pthread_cond_t *cond);
- Function: send a conditional signal, one-time, can only wake up one thread at a time, who received who move
- Parameter: Address of the condition variable
- The return value:
- Returns 0 on success
- Failure returns a non-zero value
5.pthread_cond_broadcast
- Prototype:
int pthread_cond_broadcast(pthread_cond_t *cond);
- Function: sends a conditional signal to wake up all waiting threads, a broadcast signal
- Parameter: Address of the condition variable
- The return value:
- Returns 0 on success
- Failure returns a non-zero value
6.pthread_cond_destroy
- Prototype:
int pthread_cond_destroy(pthread_cond_t *cond);
- Destroys a condition variable
- Parameter: Address of the condition variable
- The return value:
- Returns 0 on success
- Failure returns a non-zero value
7.pthread_cond_timedwait
Prototype:
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,const struct timespec *abstime);
Copy the code
Code: a scene in which two brothers, both of whom failed an exam, are lectured when their mother comes home.
Here special attention should be paid to, not only the brother where to lock to unlock, but also the mother where to lock.
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
// Define a lock, a condition variable
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond1 = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
//myfun1 as the older brother
void * myfun1(void * arg)
{
printf("Brother put his bag back in his room \n");
printf("Brother comes to Mother \n");
// Waiting for my mother to wake me up, I am answering questions
while(1)
{
pthread_mutex_lock(&mutex); / / lock
pthread_cond_wait(&cond,&mutex);// Unlock, suspend, lock
// What my brother wants to say
pthread_mutex_lock(&mutex2);// Brother and brother
printf("The elder brother replied \n");
sleep(3);
printf("Brother finished his answer \n");
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex); / / unlock}}//myfun2 as the younger brother
void * myfun2(void * arg)
{
printf("Brother returned home to put the bag back to the room \n");
printf("Brother comes to mother \n");
// Waiting for my mother to wake me up, I am answering questions
while(1)
{
pthread_mutex_lock(&mutex1); / / lock
pthread_cond_wait(&cond1,&mutex1);// Unlock, suspend, lock
// What the younger brother wants to say
pthread_mutex_lock(&mutex2);
printf("The younger brother replied \n");
sleep(3);
printf("Brother answered \n");
pthread_mutex_unlock(&mutex2);
pthread_mutex_unlock(&mutex1); / / unlock}}int main(int argc, const char *argv[])
{
// Create thread -- two
pthread_t tid[2];
if(0! = pthread_create(&tid[0].NULL,myfun1,NULL))
{
perror("create1");
return - 1;
}
if(0! = pthread_create(&tid[1].NULL,myfun2,NULL))
{
perror("create2");
return - 1;
}
/ / main thread
printf("Brother and brother return, and mother is ready to lecture \n");
printf("Brother and brother came before them, waiting to be awakened \n");
while(1)
{
char a = 0;//1 means elder brother, 2 means younger brother
scanf("%c",&a);
getchar();
switch (a)
{
case '1':
// Wake up brother
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);"//" said the elder brother
pthread_mutex_unlock(&mutex);
//pthread_cond_broadcast(&cond);
break;
case '2':
// Wake up brother
pthread_mutex_lock(&mutex1);
pthread_cond_signal(&cond1);"//" said the elder brother
pthread_mutex_unlock(&mutex1);
break;
default:
break;
}
}
pthread_join(tid[0].NULL);
pthread_join(tid[1].NULL);
return 0;
}
Copy the code
linux@ubuntu:~/demo/test/IO/test$GCC test.c -lpthread linux@ubuntu:~/demo/test/IO/test$./ a.ut Mother is ready to start the lecture brother and brother came to the front, waiting to be woken up brother returned home first put the bag back to the room brother came back to mother brother returned home first put the bag back to the room brother came to mother 1 brother answer 2 2 2 2 brother answer finished brother answer finished brother answer Brother finished answer // only two timesCopy the code
linux@ubuntu:~/demo/test/IO/test$./a. ut Mother is ready to start the lecture brother and brother came to the front, waiting to be woken up brother returned home first put the bag back to the room brother came back to mother brother returned home first put the bag back to the room brother came to mother 1 brother answer 2 2 2 2 brother answer finished brother answer finished brotherCopy the code
1 the elder brother answers 2 1 2 1 the elder brother answers the younger brother answers the younger brother answers the younger brotherCopy the code
linux@ubuntu:~/demo/test/IO/test$./a. ut brother and brother back, Mother ready to taiyuan Elder brother and younger brother all came to the front, waiting to be awakened Brother returned home to put your bag into the room first Brother came before the mother Brother returned home to put your bag into the room first Brother came before the mother brother answer 2 1 2 1 2 elder brother is answered Brother answered My brother is answered Brother answered My brother is answered Brother answered brother answered brother answered brother answered brother answered brother answered brother answered brother answered brother answeredCopy the code
Interprocess communication
It mainly uses the kernel space to complete the transfer of resources and information between two or more processes.
Interprocess communication mode :(7 categories)
1. Traditional communication methods:
1. Queues used by unnamed pipes
2. Queues used by named pipes
3. Signal asynchronous mode
2.IPC communication mode (5th generation operating system) :
1. Collection of message queue pipes
2. Mode of shared memory address mapping
3. Semaphore set A semaphore set
3. Network communication
Socket: socket
The anonymous channel of traditional communication
📃 attached:
Simplex communication: at any point in time, can only be sent from one party to the other party, the direction is not allowed to change.
Half duplex communication: Only one party can send to one party at the same time.
Full-duplex communication: At any point in time, both parties can send messages to each other.
Introduction to nameless pipes:
A nameless pipe is a half-duplex method of communication between processes, similar to a water pipe. There are only two ends, one is the data inflow end (write end), data outflow end (read end). Both of these are fixed ports. Follow the first in, first out of data, data out and disappear. The pipe is a finite length of 64*1024 bytes. Nameless pipe, not represented on the file system, data stored in memory, after the process ends. The data is lost. Pipe files cannot be offset using lseek read/write Pointers.
Schematic diagram of unnamed pipe:
Create an unnamed pipe
- The header file:
#include <unistd.h>
- Prototype: int pipe(int pipeFD [2]);
- Create an anonymous pipe that encapsulates the read-write file descriptors to fd[0] and fd[1], respectively.
- fd[0] —-r
- fd[1] —-w
- The return value:
- Returns 0 on success;
- Returns -1 on failure;
Points to note in pipeline:
1. If there is no data in the pipe, read blocks waiting for data to arrive
// First, what happens when the pipe has no data to read
char buf[123] = {0};
ssize_t ret = read(fd[0],buf,sizeof(buf));
if(- 1 == ret)
{
perror("read");
return - 1;
}
printf("Read data as %s\n",buf);
Copy the code
2. The pipeline conforms to the first-in, first-out principle, and the data will disappear after being read away
write(fd[1]."hello world".11);
char buf[123] = {0};
ssize_t ret = read(fd[0],buf,5);
if(- 1 == ret)
{
perror("read");
return - 1;
}
printf("Read data as %s\n",buf);
read(fd[0],buf,6);
printf("Read data as %s\n",buf);
Copy the code
3. The size of the pipe is 64K. If the pipe is full, writing will block waiting for writing
Prevent loss of valid data
int i = 0;
char ch;
for ( i = 0; i < 64*1024; i++)
{
write(fd[1],&ch,1);
}
printf("Pipe is full \n");
write(fd[1],&ch,1);// Determine the size of the pipe
// What happens when you write again after the pipe is full
Copy the code
4. What happens if the write port is disabled
- Read out the data in the pipe when there is data in it.
- If there is no data in the pipe, the pipeline mechanism will say that the write side is closed, no more data will arrive, and read is useless for blocking while doing a read. Read will not block and wait. The process will not be affected
Code:
char buf[123] = {0};
char buf1[123] = {0};
write(fd[1]."hello world".11);
close(fd[1]);// Close the write end
read(fd[0],buf,sizeof(buf));
printf("buf = %s\n",buf);
read(fd[0],buf1,sizeof(buf));// block wait
printf("buf = %s\n",buf1);
Copy the code
5. If the read end is closed, “pipe break” will occur during writing.
Because: If the read side is closed, the write is meaningless, and the data written each time the write function is called is called valid data. If writing would result in loss of valid data, the process would end with a pipe break during writing.
close(fd[0]);// The read end is closed
// Start writing data
char ch;
write(fd[1],&ch,1);// Write failed
/ / over
printf("Write success \n");
Copy the code
Use unnamed pipes for process communication between relatives
Since the fork function creates the child process, the file descriptor is copied over, which is equivalent to the parent process using the same file descriptor to operate on a file pointer, and thus on a file.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
// Create an unnamed pipe
int fd0[2];
int fd1[2];
if(- 1 == pipe(fd0)) Pipe 0 is used by the parent process to send messages to the child process
{
perror("pipe");
return - 1;
}
if(- 1 == pipe(fd1))Pipe 1 is used by the child process to send messages to the parent process
{
perror("pipe");
return - 1;
}
// Create process
pid_t pid = fork();
if(- 1 == pid)
{
perror("fork");
return - 1;
}
if(0 == pid)
{
// Close the nameless pipe descriptor
close(fd0[1]);
close(fd1[0]);
/ / the child process
while(1)
{
char buf[123] = {0};
read(fd0[0],buf,sizeof(buf));
if(strcmp(buf,"quit") = =0)
{
printf("End of call \n");
exit(0);
}
printf("Parent process says %s\n",buf);
// Start replying to messages
printf("Ask child process to enter \n");
char buf1[123] = {0}; // Receive data to be sent -->stdin
fgets(buf1,123.stdin); // must drop \n
buf1[strlen(buf1)- 1] = '\ 0';
write(fd1[1],buf1,strlen(buf1));
if(strcmp(buf1,"quit") = =0)
{
printf("End of call \n");
exit(0); }}}else if(pid > 0)
{
/ / the parent process
close(fd0[0]);
close(fd1[1]);
// Send a message
while(1)
{
printf("Ask the parent to type \n");
char buf[123] = {0}; // Receive data to be sent -->stdin
fgets(buf,123.stdin); // must drop \n
buf[strlen(buf)- 1] = '\ 0';
write(fd0[1],buf,strlen(buf));
if(strcmp(buf,"quit") = =0)
{
printf("End of call \n");
wait(NULL);
exit(0);
}
// Start receiving data from the child process
char buf1[123] = {0};
read(fd1[0],buf1,sizeof(buf1));
printf("Received data from child process as %s\n",buf1);
if(strcmp(buf1,"quit") = =0)
{
printf("End of call \n");
wait(NULL);
exit(0); }}}return 0;
}
Copy the code
linux@ubuntu:~/demo/test/IO/test$./a.out Please input the parent process to eat? Did the parent process say eat it? If yes, the parent process enters what it has eaten. If yes, the parent process enters what it has eaten. If yes, the parent process enters quitCopy the code
The famous channel of traditional communication
Named pipeline is an interprocess communication method based on nameless pipeline to improve the disadvantage that nameless pipeline can only be used for interprocess. Inherits all points of an unnamed pipe. Named pipes are a special type of pipe file in the file system, although they are represented on the file system. But the data is not stored on disk, the data is stored in memory, the process ends, the data is lost.
A named pipe is a file on a file system. If you want to implement communication between unrelated processes, you need to open this file. Then the two processes need to be opened with read and write permissions respectively. If you open a named pipe, you will not be able to read and write these two permissions. Open blocks and waits for another permission to arrive.
Creating a named pipe
The first way: the Linux command mkfifo + named pipe name
The second way: C language function interface
- The header file:
#include <sys/types.h>
#include <sys/stat.h>
- Prototype:
int mkfifo(const char *pathname, mode_t mode);
- Function: Create a named pipe
- Parameters:
- Pathname: indicates the name of the destination path
- Mode: permission Example: 0666
- The return value:
- Returns 0 on success
- Returns -1 on failure;
Code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, const char *argv[])
{
// Create a named pipe with no ability to open a file if it exists
if(- 1 == mkfifo("./myfifo".0664))
{
if(errno == EEXIST)
{
printf("The file exists, just open it \n");
}else{
perror("mkfifo");
return - 1; }}// Open a named pipe
int fd = open("./myfifo",O_WRONLY);
if(- 1 == fd)
{
perror("open");
return - 1;
}
printf("File opened successfully \n");
return 0;
}
Copy the code
A named pipe is used for communication between unrelated processes
Note: To create two files, two pipe files are generated; At compile time, use two Windows. You can have that conversation.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, const char *argv[])
{
// Create a named pipe with no ability to open a file if it exists
if(- 1 == mkfifo("./myfifo".0664))
{
if(errno == EEXIST)
{
printf("The file exists, just open it \n");
}else{
perror("mkfifo");
return - 1; }}if(- 1 == mkfifo("./myfifo1".0664))
{
if(errno == EEXIST)
{
printf("The file exists, just open it \n");
}else{
perror("mkfifo");
return - 1; }}// Open a named pipe
int fd = open("./myfifo",O_WRONLY);
if(- 1 == fd)
{
perror("open");
return - 1;
}
printf("File opened successfully \n");
// Open a named pipe
int fd1 = open("./myfifo1",O_RDONLY);
if(- 1 == fd)
{
perror("open");
return - 1;
}
printf("File opened successfully \n");
while(1)
{
printf("Ask the parent to type \n");
char buf[123] = {0}; // Receive data to be sent -->stdin
fgets(buf,123.stdin); // must drop \n
buf[strlen(buf)- 1] = '\ 0';
write(fd,buf,strlen(buf));
if(strcmp(buf,"quit") = =0)
{
printf("End of call \n");
wait(NULL);
exit(0);
}
// Start receiving data from the child process
char buf1[123] = {0};
read(fd1,buf1,sizeof(buf1));
printf("Received data from child process as %s\n",buf1);
if(strcmp(buf1,"quit") = =0)
{
printf("End of call \n");
wait(NULL);
exit(0); }}return 0;
}
Copy the code
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, const char *argv[])
{
// Create a named pipe with no ability to open a file if it exists
if(- 1 == mkfifo("./myfifo".0664))
{
if(errno == EEXIST)
{
printf("The file exists, just open it \n");
}else{
perror("mkfifo");
return - 1; }}if(- 1 == mkfifo("./myfifo1".0664))
{
if(errno == EEXIST)
{
printf("The file exists, just open it \n");
}else{
perror("mkfifo1");
return - 1; }}// Open a named pipe
int fd = open("./myfifo",O_RDONLY);
if(- 1 == fd)
{
perror("open");
return - 1;
}
printf("File opened successfully \n");
int fd1 = open("./myfifo1",O_WRONLY);
if(- 1 == fd)
{
perror("open");
return - 1;
}
printf("File opened successfully \n");
while(1)
{
char buf[123] = {0};
read(fd,buf,sizeof(buf));
if(strcmp(buf,"quit") = =0)
{
printf("End of call \n");
exit(0);
}
printf("Parent process says %s\n",buf);
// Start replying to messages
printf("Ask child process to enter \n");
char buf1[123] = {0}; // Receive data to be sent -->stdin
fgets(buf1,123.stdin); // must drop \n
buf1[strlen(buf1)- 1] = '\ 0';
write(fd1,buf1,strlen(buf1));
if(strcmp(buf1,"quit") = =0)
{
printf("End of call \n");
exit(0); }}return 0;
}
Copy the code
Signals of traditional communication methods
What is the signal
Signal layer An asynchronous signal that simulates a hardware layer interrupt by the software layer.
Interrupt: Is a high priority code event
Signals provided by Linux
Linux currently provides 64 signals
Kill -l Kill -9 ID
Principle of signal
At the beginning of process creation, a table of signal functions is created for the process:
The way signals are processed
-
Ignore: refers to the signal coming without taking any action such as: SIGCHLD
SIGCHLD: signals SIGKILL and SIGSTOP sent to the parent process after the child process finishes cannot be ignored.
-
Capture: before the signal arrives, change the default function pointer corresponding to the signal in the signal function table to point to the self-defined function – SIGKILL and SIGSTOP for our use cannot be captured.
-
Default: when the signal arrives, perform the default operation in the signal function table at the beginning of the process creation.
The correlation function of the signal
1.signal
-
Header file: #include
-
Prototype:
-
Man 2 signal:
typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); Copy the code
-
Man 3 signal:
void (*signal(int sig, void (*func)(int))) (int); Copy the code
-
Function: Register a signal function at the beginning of the process
-
Parameters:
-
Signum: indicates the signal number
-
Handle: indicates the signal processing mode
1. Ignore SIG_IGN
2. Default: SIG_DFL
3. Capture: Pointers to custom functions
The void argument is a function pointer of type int
The signal function binds the Signum semaphore to the function pointer
-
-
The return value:
- Success: returns a function pointer to the last function executed, save it
- Failure: SIG_ERR
Code:
#include<stdio.h>
#include <signal.h>
#include <unistd.h>
void myfun(int signum)// Self-defined interrupt functions are also called interrupt events
{
printf("Ha ha, can't shut it off! \n");
}
int main(int argc, const char *argv[])
{
// Register the signal function
// To capture signals, change the processing mode of SIGINT signal to its own processing mode
// To perform my own function
if(signal(SIGINT,myfun) == SIG_ERR)
{
perror("signal");
return - 1;
}
while(1)
{
printf("Main thread doing its own event \n");
sleep(1);
}
return 0;
}
Copy the code
Job: Use the signal function to accomplish the best way to recycle zombie processes
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
void timeout(int sig)
{
if(sig==SIGALRM)
puts("Time out");
alarm(2);
}
void keycontrol(int sig)
{
if(sig==SIGINT)
puts("CTRL + C pressed");
}
int main(a)
{
int i;
signal(SIGALRM,timeout);
signal(SIGINT,keycontrol);
alarm(2);
for(i=0; i<3; i++) {puts("wait...");
sleep(100);
}
return 0;
}
Copy the code