Source: https://blog.csdn.net/wh_sjc/article/details/70283843
IPC (InterProcess Communication) refers to the communication or exchange of information between different processes.
IPC usually includes pipeline (including nameless pipeline and named pipeline), message queue, semaphore, shared storage, socket, Streams, etc. Socket and Streams support two IPC processes on different hosts.
Take C programming in Linux as an example.
1, Pipeline
Pipe, usually the nameless pipe, is the oldest form of IPC for UNIX systems.
1. Features:
-
It is half duplex (that is, data can only flow in one direction), with fixed read and write ends.
-
It can only be used for communication between related processes (also between parent-child processes or sibling processes).
-
It can be regarded as a special file. For its reading and writing, you can also use ordinary read, write and other functions. But it is not a normal file, does not belong to any other file system, and only exists in memory.
1, Pipeline
Pipe, usually the nameless pipe, is the oldest form of IPC for UNIX systems.
1. Features:
-
It is half duplex (that is, data can only flow in one direction), with fixed read and write ends.
-
It can only be used for communication between related processes (also between parent-child processes or sibling processes).
-
It can be regarded as a special file. For its reading and writing, you can also use ordinary read, write and other functions. But it is not a normal file, does not belong to any other file system, and only exists in memory.
2. Prototype:
1 #include <unistd.h> 2 int pipe(int fd[2]); // Return value: 0 for success, 1 for failure
When a pipeline is established, it creates two file descriptors: fd[0] is opened for reading and fd[1] is opened for writing. As shown below:
To close the pipeline, just close the two file descriptors.
3. Examples
Pipes in a single process are of little use. Therefore, the process that usually calls pipe then calls fork, which creates the IPC channel between the parent process and the child process. As shown in the figure below:
If you want the data flow from the parent process to the child process, close the read end (fd[0]) and the write end (fd[1]) of the parent process; otherwise, you can make the data flow from the child process to the parent process.
1 #include<stdio.h> 2 #include<unistd.h> 3 4 int main() 5 { 6 int fd[2]; // Two file descriptors 7 pid_t pid; 8 char buff[20]; 9 10 if(pipe(fd) < 0) // Create pipe 11 printf("Create Pipe Error!\n"); 12 13 if((pid = fork()) < 0) // Create child process 14 printf("Fork Error!\n"); 15 else if(pid > 0) // Parent process 16 { 17 close(fd[0]); // Close the reader 18 write(fd[1], "hello world\n", 12); 19 } 20 else 21 { 22 close(fd[1]); // Close write end 23 read(fd[0], buff, 20); 24 printf("%s", buff); 25 } 26 27 return 0; 28 }
2, FIFO
FIFO, also known as named pipe, is a file type.
1. Features
-
FIFO can exchange data between unrelated processes, unlike anonymous pipes.
-
FIFO has a path name associated with it, which exists in the file system in the form of a special device file.
2. Archetype
1 #include <sys/stat.h> 2 // Return value: 0 for success and - 1 for error 3 int mkfifo(const char *pathname, mode_t mode);
The mode parameter is the same as that in the open function. Once a FIFO has been created, it can be manipulated with general file I/O functions.
When a FIFO is open ed, the difference between setting the non blocking flag (o ﹣ Nonblock) and not:
-
If O ﹣ Nonblock (default) is not specified, read-only open blocks the FIFO to be opened by some other process for writing. Similarly, write only open is blocked until another process opens it for reading.
-
If O "Nonblock is specified, read-only open returns immediately. Write open only returns - 1 if no process has opened the FIFO for reading, its errno is set to Enio.
3. Examples
FIFO communication is similar to using files to transfer data in the process, but FIFO type files have the characteristics of pipeline at the same time. During data reading, data is cleared in FIFO pipeline at the same time, and "first in, first out". The following example demonstrates the process of using FIFO for IPC:
write_fifo.c
1 #include<stdio.h> 2 #include<stdlib.h> // exit 3 #include<fcntl.h> // O_WRONLY 4 #include<sys/stat.h> 5 #include<time.h> // time 6 7 int main() 8 { 9 int fd; 10 int n, i; 11 char buf[1024]; 12 time_t tp; 13 14 printf("I am %d process.\n", getpid()); // Description process ID 15 16 if((fd = open("fifo1", O_WRONLY)) < 0) // Open a FIFO with write 17 { 18 perror("Open FIFO Failed"); 19 exit(1); 20 } 21 22 for(i=0; i<10; ++i) 23 { 24 time(&tp); // Take the current time of the system 25 n=sprintf(buf,"Process %d's time is %s",getpid(),ctime(&tp)); 26 printf("Send message: %s", buf); // Printing 27 if(write(fd, buf, n+1) < 0) // Write to FIFO 28 { 29 perror("Write FIFO Failed"); 30 close(fd); 31 exit(1); 32 } 33 sleep(1); // Sleep for 1 second 34 } 35 36 close(fd); // Close FIFO file 37 return 0; 38 }
read_fifo.c
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<errno.h> 4 #include<fcntl.h> 5 #include<sys/stat.h> 6 7 int main() 8 { 9 int fd; 10 int len; 11 char buf[1024]; 12 13 if(mkfifo("fifo1", 0666) < 0 && errno!=EEXIST) // Create FIFO pipe 14 perror("Create FIFO Failed"); 15 16 if((fd = open("fifo1", O_RDONLY)) < 0) // Open FIFO with read 17 { 18 perror("Open FIFO Failed"); 19 exit(1); 20 } 21 22 while((len = read(fd, buf, 1024)) > 0) // Read FIFO pipe 23 printf("Read message: %s", buf); 24 25 close(fd); // Close FIFO file 26 return 0; 27 }
Compile and run the above two files with gcc in the two terminals, and the output results are as follows:
1 [cheesezh@localhost]$ ./write_fifo 2 I am 5954 process. 3 Send message: Process 5954's time is Mon Apr 20 12:37:28 2015 4 Send message: Process 5954's time is Mon Apr 20 12:37:29 2015 5 Send message: Process 5954's time is Mon Apr 20 12:37:30 2015 6 Send message: Process 5954's time is Mon Apr 20 12:37:31 2015 7 Send message: Process 5954's time is Mon Apr 20 12:37:32 2015 8 Send message: Process 5954's time is Mon Apr 20 12:37:33 2015 9 Send message: Process 5954's time is Mon Apr 20 12:37:34 2015 10 Send message: Process 5954's time is Mon Apr 20 12:37:35 2015 11 Send message: Process 5954's time is Mon Apr 20 12:37:36 2015 12 Send message: Process 5954's time is Mon Apr 20 12:37:37 2015
1 [cheesezh@localhost]$ ./read_fifo 2 Read message: Process 5954's time is Mon Apr 20 12:37:28 2015 3 Read message: Process 5954's time is Mon Apr 20 12:37:29 2015 4 Read message: Process 5954's time is Mon Apr 20 12:37:30 2015 5 Read message: Process 5954's time is Mon Apr 20 12:37:31 2015 6 Read message: Process 5954's time is Mon Apr 20 12:37:32 2015 7 Read message: Process 5954's time is Mon Apr 20 12:37:33 2015 8 Read message: Process 5954's time is Mon Apr 20 12:37:34 2015 9 Read message: Process 5954's time is Mon Apr 20 12:37:35 2015 10 Read message: Process 5954's time is Mon Apr 20 12:37:36 2015 11 Read message: Process 5954's time is Mon Apr 20 12:37:37 2015
The above example can be extended to client process server process As an example of communication, write FIFO is similar to the client. It can open multiple clients to send request information to a server. Read FIFO is similar to the server. It timely monitors the read end of FIFO. When there is data, it reads out and processes it. However, a key problem is that each client must know the FIFO interface provided by the server in advance, as shown in the figure below This arrangement:
3, Message queuing
Message queue, which is the link table of messages, is stored in the kernel. A message queue is identified by an identifier (queue ID).
1. Features
-
Message queues are record oriented, where messages have a specific format and priority.
-
Message queuing is independent of the sending and receiving processes. When the process terminates, the message queue and its contents are not deleted.
-
Message queue can realize random query of messages. Messages can be read not only in first in first out order, but also by message type.
2. Archetype
1 #include <sys/msg.h> 2 // Create or open message queue: queue ID returned successfully, and - 1 returned failed 3 int msgget(key_t key, int flag); 4 // Add message: 0 for success and - 1 for failure 5 int msgsnd(int msqid, const void *ptr, size_t size, int flag); 6 // Read message: the length of message data is returned successfully, and - 1 is returned in case of failure 7 int msgrcv(int msqid, void *ptr, size_t size, long type,int flag); 8 // Control message queue: success returns 0, failure returns - 1 9 int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msgget creates a new message queue in two cases:
- If there is no message queue corresponding to the key value key, and the flag contains the IPC "creat flag bit.
- The key parameter is IPC? Private.
When the msgrcv function reads the message queue, the type parameter has the following situations:
- type == 0, return the first message in the queue;
- Type > 0, returns the first message of type in the queue;
- Type < 0, returns the message in the queue whose message type value is less than or equal to the absolute value of type. If there are multiple messages, the message with the lowest type value is taken.
As you can see, when the type value is not 0, it is used to read messages in non FIFO order. You can also think of type as the weight of priority. (please Google other parameters)
3. Examples
The following is a simple example of using message queuing for IPC. The server program has been waiting for a specific type of message. After receiving the message, it sends another specific type of message as the feedback. The client reads the feedback and prints it out.
msg_server.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/msg.h> 4 5 // Used to create a unique key 6 #define MSG_FILE "/etc/passwd" 7 8 // Message structure 9 struct msg_form { 10 long mtype; 11 char mtext[256]; 12 }; 13 14 int main() 15 { 16 int msqid; 17 key_t key; 18 struct msg_form msg; 19 20 // Get key value 21 if((key = ftok(MSG_FILE,'z')) < 0) 22 { 23 perror("ftok error"); 24 exit(1); 25 } 26 27 // Print key value 28 printf("Message Queue - Server key is: %d.\n", key); 29 30 // Create message queue 31 if ((msqid = msgget(key, IPC_CREAT|0777)) == -1) 32 { 33 perror("msgget error"); 34 exit(1); 35 } 36 37 // Print message queue ID and process ID 38 printf("My msqid is: %d.\n", msqid); 39 printf("My pid is: %d.\n", getpid()); 40 41 // Loop read message 42 for(;;) 43 { 44 msgrcv(msqid, &msg, 256, 888, 0);// Return the first message of type 888 45 printf("Server: receive msg.mtext is: %s.\n", msg.mtext); 46 printf("Server: receive msg.mtype is: %d.\n", msg.mtype); 47 48 msg.mtype = 999; // Message type received by client 49 sprintf(msg.mtext, "hello, I'm server %d", getpid()); 50 msgsnd(msqid, &msg, sizeof(msg.mtext), 0); 51 } 52 return 0; 53 }
msg_client.c
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/msg.h> 4 5 // Used to create a unique key 6 #define MSG_FILE "/etc/passwd" 7 8 // Message structure 9 struct msg_form { 10 long mtype; 11 char mtext[256]; 12 }; 13 14 int main() 15 { 16 int msqid; 17 key_t key; 18 struct msg_form msg; 19 20 // Get key value 21 if ((key = ftok(MSG_FILE, 'z')) < 0) 22 { 23 perror("ftok error"); 24 exit(1); 25 } 26 27 // Print key value 28 printf("Message Queue - Client key is: %d.\n", key); 29 30 // Open message queue 31 if ((msqid = msgget(key, IPC_CREAT|0777)) == -1) 32 { 33 perror("msgget error"); 34 exit(1); 35 } 36 37 // Print message queue ID and process ID 38 printf("My msqid is: %d.\n", msqid); 39 printf("My pid is: %d.\n", getpid()); 40 41 // Add message of type 888 42 msg.mtype = 888; 43 sprintf(msg.mtext, "hello, I'm client %d", getpid()); 44 msgsnd(msqid, &msg, sizeof(msg.mtext), 0); 45 46 // Read messages of type 777 47 msgrcv(msqid, &msg, 256, 999, 0); 48 printf("Client: receive msg.mtext is: %s.\n", msg.mtext); 49 printf("Client: receive msg.mtype is: %d.\n", msg.mtype); 50 return 0; 51 }
4, Semaphore
semaphore is a counter, which is different from IPC structure. Semaphores are used to realize mutual exclusion and synchronization between processes, not to store inter process communication data.
1. Features
-
Semaphores are used for inter process synchronization. To transfer data between processes, shared memory is needed.
-
Semaphores are based on the PV operation of the operating system, and the operation of the program on semaphores is atomic operation.
-
Each PV operation on a semaphore is not limited to adding or subtracting 1 to the semaphore value, but also to adding or subtracting any positive integer.
-
Support semaphore group.
2. Archetype
The simplest semaphore is a variable that can only take 0 and 1, which is also the most common form of semaphore, called Binary Semaphore. A semaphore that can take multiple positive integers is called a general semaphore.
In Linux, semaphore functions operate on a common semaphore array, rather than a single binary semaphore.
1 #include <sys/sem.h> 2 // Create or get a semaphore group: if the semaphore set ID is returned successfully, the failure returns - 1 3 int semget(key_t key, int num_sems, int sem_flags); 4 // Operate on semaphore group, change semaphore value: return 0 for success, return - 1 for failure 5 int semop(int semid, struct sembuf semoparray[], size_t numops); 6 // Information about control semaphores 7 int semctl(int semid, int sem_num, int cmd, ...);
When semget creates a new semaphore set, it must specify the number of semaphores in the set (i.e. num UU SEMS), which is usually 1; if it refers to an existing set, specify num UU SEMS as 0.
In the semop function, the sembuf structure is defined as follows:
1 struct sembuf 2 { 3 short sem_num; // The corresponding sequence number in semaphore group, 0-sem ﹣ nums-1 4 short sem_op; // Change of semaphore value in one operation 5 short sem_flg; // IPC_NOWAIT, SEM_UNDO 6 }
Where SEM? OP is the change amount of semaphore in one operation:
-
If SEM? OP > 0, it means that the process releases the corresponding number of resources and adds the value of SEM? OP to the value of semaphore. If there are processes sleeping waiting for this semaphore, wrap them.
-
If SEM UUP < 0, the resource requesting the absolute value of SEM UUP.
- If the corresponding number of resources can meet the request, the semaphore value is subtracted from the absolute value of SEM Ou OP, and the function returns successfully.
- When the corresponding number of resources cannot meet the request, this operation is related to SEM ﹣ Flg.
- If SEM? Flg specifies IPC? Nowait, the error of semop function returns EAGAIN.
- If SEM? Flg does not specify IPC? Nowait, the semaphore's semncnt value is increased by 1, and then the process is suspended until:
- When the corresponding number of resources can meet the request, the semaphore's semncnt value is reduced by 1, and the semaphore's value is reduced by the absolute value of SEM Ou Op. Successful return;
- This semaphore is deleted, and error in function smeop returns to EIDRM;
- The process captures the signal and returns it from the signal processing function. In this case, the semant value of this semaphore is reduced by 1, and the error of function semop returns EINTR
-
If SEM Ou OP = = 0, the process is blocked until the corresponding value of semaphore is 0:
- When the semaphore is already 0, the function returns immediately.
- If the semaphore value is not 0, the function action is determined according to SEM ﹣ flg:
- If SEM? Flg specifies IPC? Nowait, an error is returned to EAGAIN.
- If SEM? Flg does not specify IPC? Nowait, the semaphore's semncnt value is increased by 1, and then the process is suspended until:
- When the semaphore value is 0, reduce the semaphore semzcnt value by 1, and the function semop returns successfully;
- This semaphore is deleted, and error in function smeop returns to EIDRM;
- The process captures the signal and returns it from the signal processing function. In this case, the semant value of this semaphore is reduced by 1, and the error of function semop returns EINTR
There are many commands in the semctl function. Here are two common ones:
- SETVAL: used to initialize the semaphore to a known value. The required value is passed as a val member of the union semun. The semaphore needs to be set before it is used for the first time.
- IPC? Rmid: delete a semaphore set. If you do not delete the semaphore, it will continue to exist in the system, even if the program has exited, it may cause problems the next time you run this program, and the semaphore is a limited resource.
3. Examples
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/sem.h> 4 5 // Consortium for semctl initialization 6 union semun 7 { 8 int val; /*for SETVAL*/ 9 struct semid_ds *buf; 10 unsigned short *array; 11 }; 12 13 // Initialization semaphore 14 int init_sem(int sem_id, int value) 15 { 16 union semun tmp; 17 tmp.val = value; 18 if(semctl(sem_id, 0, SETVAL, tmp) == -1) 19 { 20 perror("Init Semaphore Error"); 21 return -1; 22 } 23 return 0; 24 } 25 26 // P operation: 27 // If the semaphore value is 1, obtain the resource and set the semaphore value - 1 28 // If the semaphore value is 0, the process hangs waiting 29 int sem_p(int sem_id) 30 { 31 struct sembuf sbuf; 32 sbuf.sem_num = 0; /*No*/ 33 sbuf.sem_op = -1; /*P operation*/ 34 sbuf.sem_flg = SEM_UNDO; 35 36 if(semop(sem_id, &sbuf, 1) == -1) 37 { 38 perror("P operation Error"); 39 return -1; 40 } 41 return 0; 42 } 43 44 // V operation: 45 // Release resources and add semaphore value + 1 46 // Wake up processes if they are pending 47 int sem_v(int sem_id) 48 { 49 struct sembuf sbuf; 50 sbuf.sem_num = 0; /*No*/ 51 sbuf.sem_op = 1; /*V operation*/ 52 sbuf.sem_flg = SEM_UNDO; 53 54 if(semop(sem_id, &sbuf, 1) == -1) 55 { 56 perror("V operation Error"); 57 return -1; 58 } 59 return 0; 60 } 61 62 // Delete semaphore set 63 int del_sem(int sem_id) 64 { 65 union semun tmp; 66 if(semctl(sem_id, 0, IPC_RMID, tmp) == -1) 67 { 68 perror("Delete Semaphore Error"); 69 return -1; 70 } 71 return 0; 72 } 73 74 75 int main() 76 { 77 int sem_id; // Semaphore set ID 78 key_t key; 79 pid_t pid; 80 81 // Get key value 82 if((key = ftok(".", 'z')) < 0) 83 { 84 perror("ftok error"); 85 exit(1); 86 } 87 88 // Create a semaphore set with only one semaphore 89 if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1) 90 { 91 perror("semget error"); 92 exit(1); 93 } 94 95 // Initialization: the initial value is set to 0, and the resource is occupied 96 init_sem(sem_id, 0); 97 98 if((pid = fork()) == -1) 99 perror("Fork Error"); 100 else if(pid == 0) /*Subprocess*/ 101 { 102 sleep(2); 103 printf("Process child: pid=%d\n", getpid()); 104 sem_v(sem_id); /*Release resources*/ 105 } 106 else /*Parent process*/ 107 { 108 sem_p(sem_id); /*Waiting for resources*/ 109 printf("Process father: pid=%d\n", getpid()); 110 sem_v(sem_id); /*Release resources*/ 111 del_sem(sem_id); /*Delete semaphore set*/ 112 } 113 return 0; 114 }
In the above example, if the semaphore is not added, the parent process will finish execution first. The semaphore is added here to let the parent process wait for the child process to finish executing before executing.
5, Shared memory
Shared Memory refers to two or more processes sharing a given storage area.
1. Features
-
Shared memory is the fastest type of IPC, because processes access memory directly.
-
Because multiple processes can operate at the same time, synchronization is required.
-
Semaphores + shared memory are usually used together. Semaphores are used to synchronize access to shared memory.
2. Archetype
1 #include <sys/shm.h> 2 // Create or get a shared memory: return shared memory ID successfully, return - 1 failed 3 int shmget(key_t key, size_t size, int flag); 4 // Connect shared memory to address space of current process: return pointer to shared memory successfully, return - 1 failed 5 void *shmat(int shm_id, const void *addr, int flag); 6 // Disconnect from shared memory: success returns 0, failure returns - 1 7 int shmdt(void *addr); 8 // Information about controlling shared memory: 0 for success and - 1 for failure 9 int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
When you use the shmget function to create a piece of shared memory, you must specify its size; if you reference an existing shared memory, you specify the size as 0.
When a piece of shared memory is created, it cannot be accessed by any process. The shmat function must be used to connect the shared memory to the address space of the current process. After the connection is successful, the shared memory area object is mapped to the address space of the calling process, and then it can be accessed like the local space.
The shmdt function is used to disconnect the connection established by shmat. Note that this is not to remove the shared memory from the system, only that the current process can no longer access the shared memory.
The shmctl function can perform various operations on the shared memory, and perform corresponding operations according to the parameter cmd. A common one is IPC? Rmid (remove the shared memory from the system).
3. Examples
In the following example, the combination of shared memory + semaphore + message queue is used to realize the communication between the server process and the client process.
- Shared memory is used to transfer data;
- Semaphores are used for synchronization;
- Message queuing is used to notify the server to read after the client modifies the shared memory.
server.c
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/shm.h> // shared memory 4 #include<sys/sem.h> // semaphore 5 #include<sys/msg.h> // message queue 6 #include<string.h> // memcpy 7 8 // Message queue structure 9 struct msg_form { 10 long mtype; 11 char mtext; 12 }; 13 14 // Consortium for semctl initialization 15 union semun 16 { 17 int val; /*for SETVAL*/ 18 struct semid_ds *buf; 19 unsigned short *array; 20 }; 21 22 // Initialization semaphore 23 int init_sem(int sem_id, int value) 24 { 25 union semun tmp; 26 tmp.val = value; 27 if(semctl(sem_id, 0, SETVAL, tmp) == -1) 28 { 29 perror("Init Semaphore Error"); 30 return -1; 31 } 32 return 0; 33 } 34 35 // P operation: 36 // If the semaphore value is 1, obtain the resource and set the semaphore value - 1 37 // If the semaphore value is 0, the process hangs waiting 38 int sem_p(int sem_id) 39 { 40 struct sembuf sbuf; 41 sbuf.sem_num = 0; /*No*/ 42 sbuf.sem_op = -1; /*P operation*/ 43 sbuf.sem_flg = SEM_UNDO; 44 45 if(semop(sem_id, &sbuf, 1) == -1) 46 { 47 perror("P operation Error"); 48 return -1; 49 } 50 return 0; 51 } 52 53 // V operation: 54 // Release resources and add semaphore value + 1 55 // Wake up processes if they are pending 56 int sem_v(int sem_id) 57 { 58 struct sembuf sbuf; 59 sbuf.sem_num = 0; /*No*/ 60 sbuf.sem_op = 1; /*V operation*/ 61 sbuf.sem_flg = SEM_UNDO; 62 63 if(semop(sem_id, &sbuf, 1) == -1) 64 { 65 perror("V operation Error"); 66 return -1; 67 } 68 return 0; 69 } 70 71 // Delete semaphore set 72 int del_sem(int sem_id) 73 { 74 union semun tmp; 75 if(semctl(sem_id, 0, IPC_RMID, tmp) == -1) 76 { 77 perror("Delete Semaphore Error"); 78 return -1; 79 } 80 return 0; 81 } 82 83 // Create a semaphore set 84 int creat_sem(key_t key) 85 { 86 int sem_id; 87 if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1) 88 { 89 perror("semget error"); 90 exit(-1); 91 } 92 init_sem(sem_id, 1); /*The initial value is set to 1. The resource is not occupied*/ 93 return sem_id; 94 } 95 96 97 int main() 98 { 99 key_t key; 100 int shmid, semid, msqid; 101 char *shm; 102 char data[] = "this is server"; 103 struct shmid_ds buf1; /*Used to delete shared memory*/ 104 struct msqid_ds buf2; /*For deleting message queues*/ 105 struct msg_form msg; /*Message queuing is used to inform the other party that the shared memory has been updated*/ 106 107 // Get key value 108 if((key = ftok(".", 'z')) < 0) 109 { 110 perror("ftok error"); 111 exit(1); 112 } 113 114 // Create shared memory 115 if((shmid = shmget(key, 1024, IPC_CREAT|0666)) == -1) 116 { 117 perror("Create Shared Memory Error"); 118 exit(1); 119 } 120 121 // Connect shared memory 122 shm = (char*)shmat(shmid, 0, 0); 123 if((int)shm == -1) 124 { 125 perror("Attach Shared Memory Error"); 126 exit(1); 127 } 128 129 130 // Create message queue 131 if ((msqid = msgget(key, IPC_CREAT|0777)) == -1) 132 { 133 perror("msgget error"); 134 exit(1); 135 } 136 137 // Create semaphore 138 semid = creat_sem(key); 139 140 // Read data 141 while(1) 142 { 143 msgrcv(msqid, &msg, 1, 888, 0); /*Read messages of type 888*/ 144 if(msg.mtext == 'q') /*quit - Jump out of the loop*/ 145 break; 146 if(msg.mtext == 'r') /*read - Read shared memory*/ 147 { 148 sem_p(semid); 149 printf("%s\n",shm); 150 sem_v(semid); 151 } 152 } 153 154 // Disconnect 155 shmdt(shm); 156 157 /*Delete shared memory, message queues, semaphores*/ 158 shmctl(shmid, IPC_RMID, &buf1); 159 msgctl(msqid, IPC_RMID, &buf2); 160 del_sem(semid); 161 return 0; 162 }
client.c
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<sys/shm.h> // shared memory 4 #include<sys/sem.h> // semaphore 5 #include<sys/msg.h> // message queue 6 #include<string.h> // memcpy 7 8 // Message queue structure 9 struct msg_form { 10 long mtype; 11 char mtext; 12 }; 13 14 // Consortium for semctl initialization 15 union semun 16 { 17 int val; /*for SETVAL*/ 18 struct semid_ds *buf; 19 unsigned short *array; 20 }; 21 22 // P operation: 23 // If the semaphore value is 1, obtain the resource and set the semaphore value - 1 24 // If the semaphore value is 0, the process hangs waiting 25 int sem_p(int sem_id) 26 { 27 struct sembuf sbuf; 28 sbuf.sem_num = 0; /*No*/ 29 sbuf.sem_op = -1; /*P operation*/ 30 sbuf.sem_flg = SEM_UNDO; 31 32 if(semop(sem_id, &sbuf, 1) == -1) 33 { 34 perror("P operation Error"); 35 return -1; 36 } 37 return 0; 38 } 39 40 // V operation: 41 // Release resources and add semaphore value + 1 42 // Wake up processes if they are pending 43 int sem_v(int sem_id) 44 { 45 struct sembuf sbuf; 46 sbuf.sem_num = 0; /*No*/ 47 sbuf.sem_op = 1; /*V operation*/ 48 sbuf.sem_flg = SEM_UNDO; 49 50 if(semop(sem_id, &sbuf, 1) == -1) 51 { 52 perror("V operation Error"); 53 return -1; 54 } 55 return 0; 56 } 57 58 59 int main() 60 { 61 key_t key; 62 int shmid, semid, msqid; 63 char *shm; 64 struct msg_form msg; 65 int flag = 1; /*while Cyclic condition*/ 66 67 // Get key value 68 if((key = ftok(".", 'z')) < 0) 69 { 70 perror("ftok error"); 71 exit(1); 72 } 73 74 // Get shared memory 75 if((shmid = shmget(key, 1024, 0)) == -1) 76 { 77 perror("shmget error"); 78 exit(1); 79 } 80 81 // Connect shared memory 82 shm = (char*)shmat(shmid, 0, 0); 83 if((int)shm == -1) 84 { 85 perror("Attach Shared Memory Error"); 86 exit(1); 87 } 88 89 // Create message queue 90 if ((msqid = msgget(key, 0)) == -1) 91 { 92 perror("msgget error"); 93 exit(1); 94 } 95 96 // Acquire semaphore 97 if((semid = semget(key, 0, 0)) == -1) 98 { 99 perror("semget error"); 100 exit(1); 101 } 102 103 // Write data 104 printf("***************************************\n"); 105 printf("* IPC *\n"); 106 printf("* Input r to send data to server. *\n"); 107 printf("* Input q to quit. *\n"); 108 printf("***************************************\n"); 109 110 while(flag) 111 { 112 char c; 113 printf("Please input command: "); 114 scanf("%c", &c); 115 switch(c) 116 { 117 case 'r': 118 printf("Data to send: "); 119 sem_p(semid); /*Access resources*/ 120 scanf("%s", shm); 121 sem_v(semid); /*Release resources*/ 122 /*Clear standard input buffer*/ 123 while((c=getchar())!='\n' && c!=EOF); 124 msg.mtype = 888; 125 msg.mtext = 'r'; /*Send message to inform server to read data*/ 126 msgsnd(msqid, &msg, sizeof(msg.mtext), 0); 127 break; 128 case 'q': 129 msg.mtype = 888; 130 msg.mtext = 'q'; 131 msgsnd(msqid, &msg, sizeof(msg.mtext), 0); 132 flag = 0; 133 break; 134 default: 135 printf("Wrong input!\n"); 136 /*Clear standard input buffer*/ 137 while((c=getchar())!='\n' && c!=EOF); 138 } 139 } 140 141 // Disconnect 142 shmdt(shm); 143 144 return 0; 145 }
Note: when scanf() enters characters or strings, the buffer is left \ n, so you need to clear the buffer of standard input after each input operation. However, since the gcc compiler does not support fflush(stdin) (it is only an extension of standard C), we use an alternative:
1 while((c=getchar())!='\n' && c!=EOF);
Summary of five communication modes
1. Pipeline: slow speed, limited capacity, only parent-child process can communicate
2.FIFO : any process can communicate with each other, but it is slow
3. Message queuing: the capacity is limited by the system, and it should be noted that when reading for the first time, the problem of not reading the data last time should be considered
4. Semaphore: unable to deliver complex messages, only used for synchronization
5. Shared memory area: it is easy to control the capacity and speed, but it needs to be synchronized. For example, when one process is writing, another process should pay attention to the problem of reading and writing, which is equivalent to the thread safety in the thread. Of course, shared memory area can also be used for inter thread communication, but it is unnecessary. The threads have already shared a piece of memory in the same process