content
Essential IPCS command parsing
This article is purely Xiaojie's review of the core key knowledge in Linux after he finished learning the Linux operating system.
Xiaojie will try to sort it out as clearly as possible, everyone learns and progresses together, knowledge is not high or low, computer Xiaojie's study is also a quantitative change ---> the process of qualitative change
is a reward for diligence, water drops through the stone, and at different stages do what I can do at the current stage, at least I don't spend time paralyzed on the bed in the bedroom---- ---- I hope everyone can learn something and gain something
Essential IPCS command parsing
-
ipcs
Function: View information about the system V IPC inter-process communication facility
Common options
-a : View all communication facility information, default -a without option
-m : specify to view shared memory
-q : specify to view the message queue
-s : specify to view the semaphore
-
ipcrm
Function: Delete System V Interprocess Communication (IPC) objects and associated data structures
Usage: ipcrm option id number
Common options
-m : delete shared memory
-q : delete the message queue
-s : delete semaphore
eg : Specifies to delete IPC objects with shmid = 18 and msqid = 8.
Linux IPC message queue
Message queues provide a way for a process to transfer a block of data to another process
Message Queuing Advantages: Compared to Pipeline Communication
- Avoid the synchronous blocking problem of pipeline communication eg: The reading process reads data from the pipeline, but there is no readable data in the pipeline. At this moment, the reading process needs to synchronously block and wait for the writing process to write data.
- The message queue does not have to receive data in a first-in, first-out manner like a pipeline, and the message queue can selectively receive data according to the type (type value).
-
msgget
Function: Create a global message queue IPC object. (kernel data structure)
Parameter analysis: key uniquely identifies, uniquely identifies a global message queue. msgflg specifies permissions
Return value: Returns a positive integer on success, representing the handle of the message queue, returns -1 on failure, and sets errno
Insert a permission introduction: r : read permission w : writable permission X : executable permission. The so-called 9-bit permission flag is aimed at: the file owner, the file owner group, and other personnel's access permission restrictions
-
msgsnd
Function: send a message to the message queue
Parametric Analysis:
- The message queue handle returned by msgget before msqid
- msgp pointer, pointing to a message structure to be sent, the structure is as follows
- msgsz The storage space of the message text, which can store up to msgsz bytes of text
- msgflg controls the form of sending messages eg: The commonly used IPC_NOWAIT means non-blocking sending, which means that normally if non-blocking is not set, if the message queue is full, it will block and wait, but if IPC_NOWAIT is set, it will return immediately, and set errno = EAGAIN
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
//我们必须按照这个形式设置msgbuff
Return value: return 0 on success, -1 on failure and set errno
-
msgrcv
Function: Receive a message from the message queue
Parametric analysis:
- The message queue handle returned by msgget before msqid
- msgp pointer, pointing to a message structure to be sent, the structure is as follows
- msgsz The storage space of the message text, which can store up to msgsz bytes of text
- msgflg controls the form of received messages eg: The commonly used IPC_NOWAIT means non-blocking reception, which means that if non-blocking is not set normally, if there is no message in the message queue, it will block and wait, but if IPC_NOWAIT is set, it will return immediately, and set errno = ENOMSG
msgtyp specifies what type of message is accepted:
- Equal to 0, specify to receive the first message of the message queue
- Greater than 0, specify to receive the first message in the message queue whose type is equal to msgtyp
- msgtyp is less than 0, read the first message in the message queue whose type value is smaller than the absolute value of msgtyp
Return value: return the length of the received message body on success, -1 on failure and set errno
-
msgctl
Function: Control message queue, set up message queue IPC, operate
Parametric analysis:
- The message queue handle returned by msgget before msqid
- cmd For the operation of the message queue, the most commonly used is IPC_RMID, delete the message queue identifier
Return value: return 0 on success, -1 on failure and set errno
Practical case, communication between parent and child processes, the parent process writes messages to the message queue, and the child process receives messages according to the type of the message
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <sys/wait.h>
#define ERR_EXIT(m) \
do { perror(m); exit(EXIT_FAILURE); } while (0);
#define TEXTSIZE 1024
//消息结构体
typedef struct msgbuff {
long int type;
char text[TEXTSIZE];
} msgbuff;
int main(int argc, char* argv[]) {
if (2 != argc) {
fprintf(stderr, "usage: %s <filepath>", argv[0]);
exit(EXIT_FAILURE);
}
key_t key = ftok(argv[1], 'q');
pid_t pid = fork();
int msgid;
if (pid == -1) {
ERR_EXIT("fork");
}
if (pid == 0) {
//son process
//获取消息队列
msgid = msgget(key, 0666 | IPC_CREAT);
msgbuff msg;
//然后接收类型为1的消息并且打印
int read_size = msgrcv(msgid, &msg, TEXTSIZE, 1, 0);
if (-1 == read_size) {
ERR_EXIT("msgrcv");
}
printf("type: %d, text: %s\n", msg.type, msg.text);
memset(&msg, 0, sizeof(msg));
//再接收一下类型为2的消息并且打印
read_size = msgrcv(msgid, &msg, TEXTSIZE, 2, 0);
if (-1 == read_size) {
ERR_EXIT("msgrcv");
}
printf("type: %d, text: %s\n", msg.type, msg.text);
} else {
//fa process
//创建消息队列
msgid = msgget(key, 0666 | IPC_CREAT);
//创建消息结构体
msgbuff msg1, msg2;
memset(&msg1, 0, sizeof(msg1));
memset(&msg1, 0, sizeof(msg2));
msg1.type = 1;
memcpy(msg1.text, "hello msg queue I am type1", strlen("hello msg queue I am type1"));
msgsnd(msgid, &msg1, TEXTSIZE, 0);
msg2.type = 2;
memcpy(msg2.text, "hello msg queue, I am type2", strlen("hello msg queue, I am type2"));
msgsnd(msgid, &msg2, TEXTSIZE, 0);
waitpid(pid, NULL, 0);
if (-1 == msgctl(msgid, IPC_RMID, NULL)) {
ERR_EXIT("msgctl");
exit(1);
}
}
return 0;
}
Linux IPC semaphores
-
understand semaphores
A semaphore is a resource, a critical resource, that exists for synchronization and mutual exclusion between multiple processes or multiple threads.
The semaphore itself is also an IPC object, which is a critical shared resource, which enables communication between multiple processes or multiple threads.
The semaphore itself is an atomic counter, which means that the operation of the semaphore is inherently atomic, so there is no need to worry about the problem of multi-process or multi-threaded mutually exclusive write operations.
What is synchronization problem: Synchronization, the nature of synchronization between two events, means that there is a conditional constraint between the two events, and there is a waiting relationship. For example, A must occur before B occurs, otherwise B can only Suspend waiting, then A and B are said to be synchronous
The most common synchronization problem in Linux: Condition variables, a condition is met before an operation can be performed. Suspend and wait until the condition is met, and the satisfaction of the condition actually depends on the occurrence of another event.
When communicating, both parties must also maintain synchronization. For example, shared memory needs to use a synchronization mechanism such as a semaphore to maintain synchronization between the processes of both parties.
What is the mutual exclusion problem: When multiple processes or threads write to the same resource, the write operations must be mutually exclusive, that is, only one process or thread is allowed to perform one write operation on this resource at the same time.
-
semget
Function: Create or obtain a semaphore set IPC object
Parametric analysis:
- key : identifies the global semaphore set
- nsems: Indicates the number of semaphores in the semaphore set to be created/acquired. If the semaphore is to be created, this value must be specified. If you are getting an existing semaphore, you can set it to 0.
- semflg: Specify permissions
Insert a permission introduction: r : read permission w : writable permission X : executable permission. The so-called 9-bit permission flag is aimed at: the file owner, the file owner group, and other personnel's access permission restrictions
Return value: return the handle of the signal set on success, -1 on failure and set errno
-
semop
Function: perform PV operation on semaphore resources and change the value of semaphore
Parametric analysis:
- The handle of the semaphore collection returned by semget before semid
- nsops: The nsops parameter specifies the number of operations to be performed, that is, the number of elements in the sops array. semop performs operations on each element in the array sops in order of the array, and this process is an atomic operation.
- sops structure pointer, pointing to the first address of the struct sembuf array The structure is defined as follows
struct sembuf { unsigned short int sem_num;/* semaphore number */ short int sem_op; /* semaphore operation */ short int sem_flg;/* operation flag */ };
if (sem_op is positive integer) add the val to semval if (sem_op is zero) wait-for-zero" operation if (sem_op is less than zero) add the val to semval (加负数, 相当于减)
-
The optional values of sem_flg are IPC_NOWAIT, SEM_UNDO. IPC_NOWAIT means that the semop call returns immediately regardless of whether the semaphore set operation is successful or not. And set errno = EAGAIN, SEM_UNDO means that when the process exits, cancel the ongoing semop operation
Return value: return 0 on success, -1 on failure and set errno
-
semctl
Function: operate on the specified semaphore IPC
Parametric analysis:
- The handle of the semaphore collection returned by semget before semid
- semnum specifies the number of the operation semaphore in the semaphore set, (The semaphores in a set are numbered starting at 0. That is to say, the semaphore number in the set starts from 0.
- The operation performed by cmd on the specified semaphore
- Some commands need to pass in the fourth parameter. This parameter type is defined by the user, but the kernel gives it a fixed form
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) */
};
Commonly used cmd lists:
-
SETVAL sets the semval value of the semaphore to semun.val
-
IPC_RMID immediately removes the semaphore set and wakes up all processes waiting for the semaphore set
Return value: return 0 on success, -1 on failure and set errno
Practical case: Using semaphores to achieve a simple synchronous waiting and communication between parent and child processes, that is, using PV to achieve the same effect as locks, to achieve synchronous printing of parent and child processes, and no random entry (reentrant in the middle of assembly instructions) problem, guaranteed atomic operation
#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <sys/wait.h>
#define ERR_EXIT(m) \
do { perror(m); exit(EXIT_FAILURE); } while (0);
//联合结构体
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*/
};
int initsem(key_t key) {
int semid = -1;
//创建一个信号量集合IPC, 其中包含一个信号量
if (-1 == (semid = semget(key, 1, 0666 | IPC_CREAT))) {
ERR_EXIT("semget");
}
//设置信号量val
union semun sem_un;
sem_un.val = 1;
if (-1 == semctl(semid, 0, SETVAL, sem_un)) {
ERR_EXIT("semctl");
}
return semid;
}
//销毁
void destorysem(int semid) {
if (-1 == semctl(semid, 0, IPC_RMID)) {
ERR_EXIT("semctl destorysem");
}
}
void P(int semid) {
//因为信号量集合中就只是一个
//所以sembuf无需设置为vector
struct sembuf op;//操作
op.sem_num = 0;
op.sem_op = -1;// - 1
op.sem_flg = SEM_UNDO;
if (-1 == semop(semid, &op, 1)) {
ERR_EXIT("semop P");
}
}
void V(int semid) {
struct sembuf op;//操作
op.sem_num = 0;
op.sem_op = 1;// + 1
op.sem_flg = SEM_UNDO;
if (-1 == semop(semid, &op, 1)) {
ERR_EXIT("semop V");
}
}
int main(int argc, char* argv[]) {
if (2 != argc) {
fprintf(stderr, "usage: %s <filepath>", argv[0]);
exit(EXIT_FAILURE);
}
//1.获取唯一标识key
key_t key = ftok(argv[1], 'a');
//2. 创建信号量集合
int semid = initsem(key);
int cnt = 10;
pid_t pid = fork();
if (pid == -1) {
ERR_EXIT("fork");
}
if (pid == 0) {
//son process
while (cnt > 0) {
// P(semid);
printf("I am son process\n");
sleep(1);
//V(semid);
cnt -= 1;
}
} else {
//fa process
while (cnt > 0) {
//P(semid);
printf("I am fa process\n");
sleep(1);
//V(semid);
cnt -= 1;
}
waitpid(pid, NULL, 0);
destorysem(semid);
}
return 0;
}
Linux IPC Shared Memory
Shared memory is the fastest IPC mechanism because it does not involve any data transfer. Instead, it uses a common physical memory map and mounts it to the process address space, so that multiple processes can see the same physical memory for communication. .
However, there are also certain drawbacks, that is, synchronization cannot be supported, and we need to use other communication mechanisms to synchronize the process's access to shared memory
eg : My process A has written data to the shared memory and needs to communicate with process B to establish synchronization. At this time, there is a big problem. How can process B know that process A has written data. At this time, we You need to use semaphore to achieve synchronization. --- This is very important to understand why the use of shared memory needs to cooperate with other communication mechanisms to complete synchronization, because shared memory itself does not support synchronous access. Unlike semaphores, they support atomic operations. of.
-
shmget
Function: apply for shared memory
shmflag : 9-bit permission flag: we usually use 0x666 | IPC_CREAT | IPC_EXCL
Insert a permission introduction: r : read permission w : writable permission X : executable permission. The so-called 9-bit permission flag is aimed at: the file owner, the file owner group, and other personnel's access permission restrictions
-
shmat
Function: connect, mount, and associate physical memory to the process address space
shmaddr: NULL automatically selects the connection address, not NULL, it is manually assigned to the address you want to mount
We still fill in NULL
Return Val: Returns a ptr pointing to the first address of shared physical memory. The first address of the connection in the virtual space
-
shmdt
Function: Unmount, detach the shared memory from the current process.
shmaddr : The return val of the previous shmat, the first address of the shared memory mapped in the virtual address space.
Return Val: success return 0 failure return -1
-
shmctl
Function: operate on shared memory IPC
cmd: IPC_RMID delete shared memory
buf can pass NULL
Practical case: use shared memory to realize communication between parent and child processes, the parent process writes data to the shared memory, and the child process prints the written data synchronously when writing.
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#define ERR_EXIT(m) \
do { perror(m); exit(EXIT_FAILURE); } while (0);
#define SHMSIZE 1024
//联合结构体
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*/
};
int initsem(key_t key) {
int semid = -1;
if (-1 == (semid = semget(key, 1, 0666 | IPC_CREAT))) {
ERR_EXIT("semid");
}
//设置信号量集合中编号为0号的信号量val 为 2
union semun sem_un;
sem_un.val = 1;
if (-1 == semctl(semid, 0, SETVAL, sem_un)) {
ERR_EXIT("semop set val");
}
return semid;
}
void destorysem(int semid) {
if (-1 == semctl(semid, 0, IPC_RMID)) {
ERR_EXIT("semctl destorysem");
}
}
void P(int semid) {
struct sembuf op;
op.sem_num = 0;
op.sem_op = -1;
op.sem_flg = SEM_UNDO;
if (-1 == semop(semid, &op, 1)) {
ERR_EXIT("semop P");
}
}
void V(int semid) {
struct sembuf op;
op.sem_num = 0;
op.sem_op = 1;
op.sem_flg = SEM_UNDO;
if (-1 == semop(semid, &op, 1)) {
ERR_EXIT("semop V");
}
}
void waitforzero(int semid) {
struct sembuf op;
op.sem_num = 0;
op.sem_op = 0;
op.sem_flg = SEM_UNDO;
if (-1 == semop(semid, &op, 1)) {
ERR_EXIT("semop zero");
}
}
int main(int argc, char* argv[]) {
if (2 != argc) {
fprintf(stderr, "usage: %s <filepath>", argv[0]);
exit(EXIT_FAILURE);
}
key_t key1 = ftok(argv[1], 'c');
key_t key2 = ftok(argv[1], 'C');
//创建信号量集合IPC
int semid = initsem(key1);
int shmid = -1;
pid_t pid = fork();
if (pid < 0) {
ERR_EXIT("fork");
}
if (pid == 0) {
//son process
waitforzero(semid);
if (-1 == (shmid = shmget(key2, SHMSIZE, 0666 | IPC_CREAT))) {
ERR_EXIT("shmget");
}
void* p = shmat(shmid, NULL, 0);
if ((void*)-1 == p)
{
ERR_EXIT("shmat");
}
char buf[SHMSIZE] = {0};
memcpy(buf, p, SHMSIZE);
printf("%s\n", buf);
//卸载共享内存
if (-1 == shmdt(p))
{
ERR_EXIT("shmdt in parent");
}
V(semid);//为父进程归还这个资源.
} else {
// fa process
if (-1 == (shmid = shmget(key2, SHMSIZE, 0666 | IPC_CREAT))) {
ERR_EXIT("shmget");
}
void* p;
p = shmat(shmid, NULL, 0);
if ((void*)-1 == p)
{
ERR_EXIT("shmat");
}
char buff[SHMSIZE];
printf("请输入要传输给子进程的数据\n");
scanf("%s", buff);
memcpy(p, buff, strlen(buff));
P(semid);
waitpid(pid, NULL, 0);
//通信完成, 销毁所有IPC
destorysem(semid);
//卸载内存, 销毁shmid共享内存
if (-1 == shmdt(p)) {
ERR_EXIT("shmdt");
}
if (-1 == shmctl(shmid, IPC_RMID, NULL)) {
ERR_EXIT("shmctl destoryshm");
}
}
return 0;
}