【Linux】进程间通信IPC机制

目录

一、无名管道

二、有名管道

三、共享内存

四、信号量

五、消息队列

六、套接字


一、无名管道

1.只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程)。

2.是一个单工的通信模式,具有固定的读端和写端。

3.管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的read()、write()等函数。但是它不属于任何文件系统,并且只存在于内存中。

#include <unistd.h>
int pipe(int filedes[2]);
在管道中,文件描述符数组的第一个元素(索引为0)用于读取,第二个元素(索引为1)用于写入。

编程思路

1. 创建管道
        使用 pipe(fd) 函数创建一个无名管道。
        fd[0] 用于读取数据,fd[1] 用于写入数据。
2. 创建子进程
        使用 fork() 创建子进程。
        返回值 pid 用于区分父进程和子进程
3. 父进程操作
        关闭写入端(fd[WRITE_END]),因为父进程只需读取数据。
        从管道读取数据。
        打印读取的数据。
        关闭读取端(fd[READ_END])。

4. 子进程操作
        关闭读取端(fd[READ_END]),因为子进程只需写入数据。
        向管道写入数据。
        关闭写入端(fd[WRITE_END])。

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

#define BUFFER_SIZE 25
#define READ_END 0
#define WRITE_END 1

int main() 
{
    char write_msg[BUFFER_SIZE] = "Hello, child!";
    char read_msg[BUFFER_SIZE];

    int fd[2];
    pid_t pid;

    // 创建管道
    if (pipe(fd) == -1) 
    {
        fprintf(stderr, "Pipe failed");
        return 1;
    }

    // 创建子进程
    pid = fork();

    if (pid < 0) 
    {
        fprintf(stderr, "Fork failed");
        return 1;
    }
    // 父进程
    if (pid > 0)   
    {
        // 关闭写入端,因为父进程不会写入
        close(fd[WRITE_END]);

        // 从管道中读取消息
        read(fd[READ_END], read_msg, BUFFER_SIZE);
        printf("Parent received message from child: %s\n", read_msg);

        // 关闭读取端
        close(fd[READ_END]);
    } 
    // 子进程
    else 
    {  
        // 关闭读取端,因为子进程不会读取
        close(fd[READ_END]);

        // 写入消息到管道
        write(fd[WRITE_END], write_msg, BUFFER_SIZE);

        // 关闭写入端
        close(fd[WRITE_END]);
    }

    return 0;
}

./a.out
执行结果:Parent received message from child: Hello, child! 

二、有名管道

  1. mkfifo(const char *pathname, mode_t mode):

    • 这个函数用于创建一个有名管道。
    • 参数pathname是要创建的管道的路径名,mode是指定管道权限的位掩码。
    • 返回值:成功时返回0,失败时返回-1,并设置errno变量表示错误类型。
  2. open(const char *pathname, int flags, mode_t mode):

    • 这个函数用于打开文件或管道。
    • 参数pathname是要打开的文件或管道的路径名,flags指定打开文件的方式(如只读、只写、读写等),mode是当创建新文件时指定的权限。
    • 返回值:成功时返回文件描述符,失败时返回-1,并设置errno变量表示错误类型。
  3. write(int fd, const void *buf, size_t count):

    • 这个函数用于向文件描述符指定的文件或管道中写入数据。
    • 参数fd是打开文件或管道时返回的文件描述符,buf是要写入的数据缓冲区,count是要写入的字节数。
    • 返回值:成功时返回实际写入的字节数,失败时返回-1,并设置errno变量表示错误类型。
  4. read(int fd, void *buf, size_t count):

    • 这个函数用于从文件描述符指定的文件或管道中读取数据。
    • 参数fd是打开文件或管道时返回的文件描述符,buf是用于接收数据的缓冲区,count是要读取的最大字节数。
    • 返回值:成功时返回实际读取的字节数,失败时返回-1,并设置errno变量表示错误类型。
  5. close(int fd):

    • 这个函数用于关闭打开的文件描述符。
    • 参数fd是要关闭的文件描述符。
    • 返回值:成功时返回0,失败时返回-1,并设置errno变量表示错误类型。
  6. unlink(const char *pathname):

    • 这个函数用于删除文件或链接。
    • 参数pathname是要删除的文件或链接的路径名。
    • 返回值:成功时返回0,失败时返回-1,并设置errno变量表示错误类型。

编程思路:

 1. 创建有名管道
        使用 mkfifo() 函数创建有名管道。
        fifo_name 是管道的文件路径名,0666 是文件权限。   

2. 创建子进程
        使用 fork() 创建子进程。
        根据返回值区分父进程和子进程。

3. 子进程操作
        打开有名管道进行写入(O_WRONLY)。
        使用 write() 向管道写入数据。
        关闭文件描述符。

4. 父进程操作
        打开有名管道进行读取(O_RDONLY)。
        使用 read() 从管道读取数据。
        打印读取的数据。
        关闭文件描述符。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#define BUFFER_SIZE 25

int main() 
{
    char *fifo_name = "./name";
    char write_msg[BUFFER_SIZE] = "Hello, world!";
    char read_msg[BUFFER_SIZE];

    // 创建有名管道
    mkfifo(fifo_name, 0666);

    pid_t pid = fork();

    if (pid == 0)// 子进程负责写入
	{
        // 打开管道进行写入
        int fd_write = open(fifo_name, O_WRONLY);
        write(fd_write, write_msg, BUFFER_SIZE);
        close(fd_write);
    } 
	else if (pid > 0) // 父进程负责读取
	{ 
        // 打开管道进行读取
        int fd_read = open(fifo_name, O_RDONLY);
        read(fd_read, read_msg, BUFFER_SIZE);
        printf("Received message: %s\n", read_msg);
        close(fd_read);
    } 
	else  // fork失败
	{
        fprintf(stderr, "Fork failed");
        return 1;
    }

    // 删除管道
    unlink(fifo_name);

    return 0;
}

三、共享内存

共享内存是最高效的,因为避免了数据在用户空间和内核空间的来回拷贝。各个进程申请的共享内存是各个进程虚拟地址空间的一部分,从物理地址上看各个虚拟地址指向同一块物理空间。

  1. ftok(const char *pathname, int proj_id):

    • ftok()函数用于生成一个唯一的key,用于创建或访问共享内存。
    • 参数pathname是一个路径名,proj_id是一个整数,用于生成key。
    • 返回值:如果成功,返回一个唯一的key,如果失败,返回-1。
  2. shmget(key_t key, size_t size, int shmflg):

    • shmget()函数用于创建共享内存段或获取共享内存段的标识符。
    • 参数key是由ftok()生成的唯一key,size是共享内存的大小,shmflg是标志位,用于指定权限和行为。
    • 返回值:如果成功,返回共享内存段的标识符,如果失败,返回-1。
  3. shmat(int shmid, const void *shmaddr, int shmflg):

    • shmat()函数用于将共享内存连接到当前进程的地址空间。
    • 参数shmid是共享内存段的标识符,shmaddr通常为NULL(表示系统自动选择地址),shmflg是标志位,通常为0。
    • 返回值:如果成功,返回指向共享内存段的指针,如果失败,返回(void *)-1。
  4. shmdt(const void *shmaddr):

    • shmdt()函数用于将共享内存从当前进程的地址空间分离。
    • 参数shmaddr是指向共享内存段的指针。
    • 返回值:如果成功,返回0,如果失败,返回-1。
  5. shmctl(int shmid, int cmd, struct shmid_ds *buf):

    • shmctl()函数用于对共享内存进行控制操作,如删除共享内存段。
    • 参数shmid是共享内存段的标识符,cmd是控制命令,buf是一个指向shmid_ds结构体的指针,用于获取共享内存的状态信息。
    • 返回值:如果成功,返回0,如果失败,返回-1。

 读端编程思路:

1. 生成共享内存的键值
        使用 ftok() 函数生成一个唯一的共享内存键(key)。ftok() 函数需要两个参数:一个路径和一个字符,用于生成一个唯一的键值。键值用于唯一标识一个共享内存段。通过唯一的键值,可以在进程间访问同一共享内存段。不同的键值对应不同的共享内存段。

2. 创建共享内存段
        使用 shmget() 函数创建一个共享内存段。如果共享内存段不存在,则创建一个新段。

shmget() 的参数包括:键值、共享内存段的大小(SHM_SIZE)、权限标志(IPC_CREAT | 0666)。

3. 将共享内存连接到当前进程的地址空间

        该操作就是将同一块物理地址映射加进当前进程的虚拟空间
        使用 shmat() 函数将共享内存段附加到当前进程的地址空间。

        如果 shmat() 返回 (void *)-1,表示附加失败。

4. 写入数据到共享内存
        使用 strcpy() 将数据写入共享内存。这里写入的字符串是 "Hello, shared memory!"。

5. 分离共享内存
        使用 shmdt() 函数将共享内存从当前进程的地址空间分离。

        如果 shmdt() 返回 -1,表示分离失败。

读端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>

#define SHM_SIZE 1024

int main() 
{
    key_t key = ftok("/", 'R'); // 生成共享内存的key

    // 创建共享内存段
    int shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
    if (shmid == -1) 
    {
        perror("shmget");
        exit(1);
    }

    // 将共享内存连接到当前进程的地址空间
    char *shmaddr = shmat(shmid, NULL, 0);
    if (shmaddr == (void *)-1) 
    {
        perror("shmat");
        exit(1);
    }

    // 写入数据到共享内存
    strcpy(shmaddr, "Hello, shared memory!");

    // 分离共享内存
    if (shmdt(shmaddr) == -1) 
    {
        perror("shmdt");
        exit(1);
    }

    printf("数据已写入共享内存\n");

    return 0;
}

 写端编程思路:

1.生成共享内存的 Key:

        使用 ftok() 生成一个共享内存的唯一键(key)。ftok() 将一个路径名和一个项目标识符组合生成键值。
        key_t key = ftok("/", 'R');

2.获取共享内存段:

        使用 shmget() 根据生成的键(key)获取共享内存段的标识符(shmid)。0666 是共享内存的权限标志,表示读写权限。
        int shmid = shmget(key, SHM_SIZE, 0666);

3.附加共享内存到进程地址空间:

        使用 shmat() 将共享内存段附加到当前进程的虚拟地址空间。shmat() 返回一个指向共享内存的虚拟地址的指针(shmaddr)。
        char *shmaddr = shmat(shmid, NULL, 0);

4.读取共享内存中的数据:

        使用 printf() 从共享内存中读取数据并打印。读取的数据应该是之前写入到共享内存中的内容。
        printf("从共享内存中读取到的消息:%s\n", shmaddr);
5.分离共享内存:

        使用 shmdt() 将共享内存从进程的虚拟地址空间中分离。

 写端

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define SHM_SIZE 1024

int main() 
{
    key_t key = ftok("/", 'R'); // 生成共享内存的key

    // 获取共享内存段
    int shmid = shmget(key, SHM_SIZE, 0666);
    if (shmid == -1) 
    {
        perror("shmget");
        exit(1);
    }

    // 将共享内存连接到当前进程的地址空间
    char *shmaddr = shmat(shmid, NULL, 0);
    if (shmaddr == (void *)-1)
    {
        perror("shmat");
        exit(1);
    }

    printf("从共享内存中读取到的消息:%s\n", shmaddr);

    // 分离共享内存
    if (shmdt(shmaddr) == -1) 
    {
        perror("shmdt");
        exit(1);
    }

    return 0;
}

写端执行后:

数据已写入共享内存

读端执行后:

从共享内存中读取到的消息:Hello, shared memory! 

四、信号量

信号量是一种整数值,用于控制访问共享资源的进程数量。信号量有两种基本操作:

        P操作(Proberen,测试):也称为“等待”或“减1”操作。
        V操作(Verhogen,增加):也称为“释放”或“加1”操作。

在信号量的实现中,**P操作(等待操作)**是先检查信号量的值,然后再进行减1操作。这个过程通常是原子的,也就是说,它在执行时不会被其他进程打断。

P操作(等待操作)的过程
        减1操作:如果信号量的值大于0,进程会将信号量的值减1。这个减1操作表示占用了一个资源。
        阻塞条件:如果信号量的值为0,进程将被阻塞。这意味着当前没有可用的资源,进程需要等待其他进程释放资源,才可以继续执行。

1.信号量实现互斥

编程思路:

1.初始化信号量:

        创建一个信号量并将其初始值设为1。这个信号量用来表示资源的可用性(val = 1 表示资源可用)。

2.P操作(等待资源):

        在进程需要访问共享资源时,执行P操作。这会将信号量的值减1。如果信号量的值变为0,进程会被阻塞,直到信号量值大于0为止。

3.访问共享资源:

        在信号量值为1时,进程可以安全地访问共享资源。由于信号量的值被设为0,其他进程被阻塞,直到资源被释放。

4.V操作(释放资源):

        在进程访问完共享资源后,执行V操作。这会将信号量的值加1。如果有其他进程因为P操作而阻塞,它们将被唤醒并可以继续尝试获取资源。

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

#define KEY 1234 // 信号量的键值

// 定义一个联合体,用于semctl初始化
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

// 创建一个二值信号量并初始化为1
int create_semaphore() 
{
    // 创建一个信号量集,包含1个信号量
    int semid = semget(KEY, 1, IPC_CREAT | 0666); 
    if (semid == -1) 
    {
        perror("semget");
        exit(1);
    }

    union semun arg;
    arg.val = 1; // 初始值为1,表示资源可用
    if (semctl(semid, 0, SETVAL, arg) == -1) // 初始化信号量
	{
        perror("semctl");
        exit(1);
    }

    return semid;
}

// P操作(等待资源)
void P(int semid) 
{
    struct sembuf op;
    op.sem_num = 0; // 信号量集中的第一个信号量
    op.sem_op = -1; // 对信号量执行P操作
    op.sem_flg = 0;
    if (semop(semid, &op, 1) == -1) 
	{
        perror("semop");
        exit(1);
    }
}

// V操作(释放资源)
void V(int semid) 
{
    struct sembuf op;
    op.sem_num = 0; // 信号量集中的第一个信号量
    op.sem_op = 1; // 对信号量执行V操作
    op.sem_flg = 0;
    if (semop(semid, &op, 1) == -1) 
	{
        perror("semop");
        exit(1);
    }
}

int main()
{
    int semid = create_semaphore(); // 创建信号量

    pid_t pid = fork(); // 创建子进程
    if (pid == -1) 
	{
        perror("fork");
        exit(1);
    }

    if (pid == 0)  // 子进程
	{
        P(semid); // 等待资源
        printf("Child process: Counter decremented by 1.\n");
        V(semid); // 释放资源
    } 
	else // 父进程
	{
        sleep(1); // 让子进程有机会先运行
        P(semid); // 等待资源
        printf("Parent process: Counter incremented by 1.\n");
        V(semid); // 释放资源
    }

    return 0;
}

相关API:

  1. semget(key_t key, int nsems, int semflg)

    • 这个函数用于创建一个新的信号量集或获取一个现有信号量集的标识符。
    • key是一个用于唯一标识信号量集的键值。
    • nsems指定了信号量集中的信号量数量。
    • semflg是一组标志,用于指定创建信号量集的权限和行为。
    • 返回值:成功时返回信号量集的标识符,失败时返回-1。
  2. semctl(int semid, int semnum, int cmd, union semun arg)

    • 这个函数用于对信号量集进行控制操作,如初始化、设置值、获取值等。
    • semid是信号量集的标识符。
    • semnum是指定的信号量在集合中的索引,通常为0。
    • cmd是指定要执行的控制命令。
    • arg是一个联合体,用于传递控制命令的参数。
    • 返回值:根据控制命令不同而不同。
  3. semop(int semid, struct sembuf *sops, size_t nsops)

    • 这个函数用于执行一组信号量操作,如等待资源(P操作)和释放资源(V操作)。
    • semid是信号量集的标识符。
    • sops是一个指向信号量操作结构体数组的指针。
    • nsops是指定的信号量操作结构体数组的大小。
    • 返回值:成功时返回0,失败时返回-1。
  4. struct sembuf

    • 这是一个结构体,用于描述信号量操作。
    • 它包含了三个字段:
      • sem_num:信号量集中的信号量索引。
      • sem_op:信号量操作,通常是-1(P操作,等待资源)或1(V操作,释放资源)。
      • sem_flg:信号量操作的标志位,通常为0。
  5. union semun

    • 这是一个联合体,用于传递给semctl()函数的参数。
    • 它包含了多个字段,其中的一个字段可以根据不同的控制命令来使用,比如用于设置或获取信号量值时使用的val字段,用于获取信号量状态信息时使用的buf字段等

操作封装:

  1. create_semaphore():

    • 这个函数用于创建一个二值信号量,并将其初始化为1。
    • 首先,它调用 semget() 函数创建一个包含一个信号量的信号量集,如果创建失败则会打印错误信息并退出程序。
    • 然后,它使用 semctl() 函数将信号量的值初始化为1,表示资源可用。
    • 最后,它返回创建的信号量集的标识符。
  2. P(int semid):

    • 这个函数用于执行 P 操作,即等待资源。
    • 首先,它定义了一个 struct sembuf 结构体 op,用于描述信号量操作。
    • 然后,它将 op.sem_num 设置为0,表示信号量集中的第一个信号量。
    • 接着,它将 op.sem_op 设置为-1,表示对信号量执行 P 操作。
    • 最后,它调用 semop() 函数执行信号量操作,如果操作失败则会打印错误信息并退出程序。
  3. V(int semid):

    • 这个函数用于执行 V 操作,即释放资源。
    • 它的实现与 P() 函数类似,只是将 op.sem_op 设置为1,表示对信号量执行 V 操作。

2.信号量实现同步互斥 

        交替实现奇偶数打印,打印一个奇数后必须是一个偶数

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

#define KEY 1234 // 信号量的键值

// 定义一个联合体,用于semctl初始化
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

// 创建一个信号量并初始化为1
int create_semaphore() 
{
    int semid = semget(KEY, 1, IPC_CREAT | 0666); // 创建一个信号量集,包含1个信号量
    if (semid == -1) 
	{
        perror("semget");
        exit(1);
    }

    union semun arg;
    arg.val = 0; // 初始值为0
    if (semctl(semid, 0, SETVAL, arg) == -1) // 初始化信号量
    { 
        perror("semctl");
        exit(1);
    }

    return semid;
}

// P操作(等待资源)
void P(int semid) 
{
    struct sembuf op;
    op.sem_num = 0; // 信号量集中的第一个信号量
    op.sem_op = -1; // 对信号量执行P操作
    op.sem_flg = 0;
    if (semop(semid, &op, 1) == -1) 
	{
        perror("semop");
        exit(1);
    }
}

// V操作(释放资源)
void V(int semid) 
{
    struct sembuf op;
    op.sem_num = 0; // 信号量集中的第一个信号量
    op.sem_op = 1; // 对信号量执行V操作
    op.sem_flg = 0;
    if (semop(semid, &op, 1) == -1) 
	{
        perror("semop");
        exit(1);
    }
}

int main() 
{
	int  i = 1;
	int j = 2;
    int semid = create_semaphore(); // 创建信号量

    pid_t pid = fork(); // 创建子进程
    if (pid == -1) 
	{
        perror("fork");
        exit(1);
    }

    if (pid == 0)  // 子进程打印奇数
	{
		while(1)
		{ 		
           	printf("i = %d\n",i);
			i += 2;
            V(semid); // 释放资源
			sleep(1);
		}       
    } 
	else // 父进程打印偶数
	{
     	while(1)
		{
            P(semid); // 等待资源
			printf("j = %d\n",j);
			j += 2;
		}		
         
    }

    return 0;
}

执行结果

i = 1
j = 2
i = 3
j = 4
i = 5
j = 6
i = 7
j = 8
i = 9
j = 10
i = 11
j = 12
i = 13
j = 14
i = 15
j = 16
 

五、消息队列

消息队列在内核中的实现通常基于链表数据结构

发送进程

1.定义消息结构体:

        msg_buffer 结构体定义了消息的格式,包括消息类型 msg_type 和消息内容 msg_text。
2.创建消息队列:

        使用 msgget 函数创建一个消息队列,指定 MSG_KEY 作为键值。如果消息队列不存在,IPC_CREAT 会创建一个新的队列,权限设置为 0666。函数返回消息队列的标识符 msgid。
3.准备消息:

        在 message_send 结构体中设置要发送的消息内容和类型。msg_type 设为 1,表示消息的类型。msg_text 存储要发送的字符串。
4.发送消息:

        使用 msgsnd 函数将消息发送到队列。传递 msgid 作为消息队列的标识符,message_send 作为消息内容。sizeof(message_send) - sizeof(long) 计算消息内容的大小,排除消息类型字段的大小。发送成功后,打印确认消息。

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

#define MSG_KEY 1234 // 消息队列的键值

// 定义消息结构体
struct msg_buffer 
{
    long msg_type; // 消息类型
    char msg_text[100]; // 消息内容
};

int main() 
{
    int msgid;
    struct msg_buffer message_send;

    // 创建消息队列
    msgid = msgget(MSG_KEY, IPC_CREAT | 0666);
    if (msgid == -1) 
	{
        perror("msgget");
        exit(1);
    }

    // 准备发送的消息
    strcpy(message_send.msg_text, "Hello, Message Queue!");
    message_send.msg_type = 1; // 消息类型为1

    // 发送消息
    if (msgsnd(msgid, &message_send, sizeof(message_send) - sizeof(long), 0) == -1) 
	{
        perror("msgsnd");
        exit(1);
    }
    printf("Parent process sent message: %s\n", message_send.msg_text);

    return 0;
}

 接收进程

1.定义消息结构体:

        msg_buffer 结构体定义了消息的格式,包括消息类型 msg_type 和消息内容 msg_text。
2.创建消息队列:

        使用 msgget 函数创建或获取消息队列,指定 MSG_KEY 作为键值。如果消息队列不存在,IPC_CREAT 会创建一个新的队列,权限设置为 0666。函数返回消息队列的标识符 msgid。
3.接收消息:

        使用 msgrcv 函数从消息队列中接收消息。传递 msgid 作为消息队列的标识符,&message_rcv 作为接收的消息结构体。sizeof(message_rcv) - sizeof(long) 计算消息内容的大小,排除消息类型字段的大小。1 表示消息类型为 1,0 表示以阻塞方式接收消息(即如果没有消息,进程会等待)。
4.打印接收到的消息:

        接收成功后,打印从消息队列中读取到的消息内容。

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

#define MSG_KEY 1234 // 消息队列的键值

// 定义消息结构体
struct msg_buffer {
    long msg_type; // 消息类型
    char msg_text[100]; // 消息内容
};

int main() 
{
    int msgid;
    struct msg_buffer message_rcv;

    // 创建消息队列
    msgid = msgget(MSG_KEY, IPC_CREAT | 0666);
    if (msgid == -1) 
	{
        perror("msgget");
        exit(1);
    }

    // 接收消息
    if (msgrcv(msgid, &message_rcv, sizeof(message_rcv) - sizeof(long), 1, 0) == -1) 
	{
        perror("msgrcv");
        exit(1);
    }
    printf("Child process received message: %s\n", message_rcv.msg_text);

    return 0;
}

执行结果:

./s

Parent process sent message: Hello, Message Queue!
./r
Child process received message: Hello, Message Queue!
 

在这两个示例代码中,主要使用了以下系统调用:

  1. msgget: 用于创建或打开一个消息队列。它接受一个参数,即消息队列的键值(一个唯一的标识符),并返回一个消息队列的标识符(msgid)。

  2. msgsnd: 用于向消息队列发送消息。它接受四个参数:消息队列的标识符(msgid)、指向要发送的消息的指针、消息的大小(不包括消息类型字段的大小)、消息的标志(通常为0)。

  3. msgrcv: 用于从消息队列接收消息。它接受五个参数:消息队列的标识符(msgid)、指向用于接收消息的缓冲区的指针、缓冲区大小(不包括消息类型字段的大小)、消息类型(通常为正整数)、接收消息的标志(通常为0,会按照先进先出的顺序接收消息,如果为负值,接收最小的绝对值消息类型的消息,如果为正值,接收具有特定类型的消息,这里的类型就是定义的消息结构体的类型。)。

  4. msgctl: 用于控制消息队列的属性,如删除消息队列。在这个示例中,我们使用它来删除消息队列。它接受三个参数:消息队列的标识符(msgid)、命令(IPC_RMID 表示删除消息队列)、指向用于控制消息队列属性的结构体的指针(在这个例子中不需要)。

六、套接字

网络部分的内容

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

放牛的守护神_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值