I\O进程线程(Day32)

一、学习内容

进程之间的通信(nterprocess communication)

  #### 信号通信

  1.
     >
     > ##### 概念
     >
     > 1\> 信号通信中,多个进程只起到通知作用,没有数据传输的功能
     >
     > 2\> 所谓信号通信,就是软件模拟的硬件的中断请求
     >
     > 3\>原理图
     >
     > ![](https://i-blog.csdnimg.cn/direct/e0fe697140d3401db8c26a49ca91fa53.png)
     >
  2.

     ##### 信号处理方式

     1.

        ###### 默认(SIG_DFL)

        停下正在执行的任务,去执行信号功能的任务,大多数默认处理是杀死进程
     2.

        ###### 捕获

        需要给定一个函数名,表示停下正在执行的任务,去执行给定的函数
     3.

        ###### 忽略(SIG_IGN)

        忽略当前信号,继续执行自己的任务
  3.

     ##### 收到信号类型

     1.

        ###### 内核发送过来的信号

        如管道破裂时,内核向用户空间发射SIGPIPE信号,指针操作失误时内核向用户空间发射SIGSEGV信号
     2.

        ###### 用户发送过来的信号

        如用户键入ctrl+c,表示终端向该进程发送SIGINT信号;用户键入ctrl+z,表示终端向进程发送SIGSTOP信号;
     3.

        ###### 一个进程向另一个进程发送信号

        需要使用相关函数完成,kill 函数,表示向某个进程发送信号
  4.

     ##### signal(信号绑定函数)

     typedef void (\*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler);
     1.

        ###### 返回值

        成功返回处理方式,失败返回SIG_ERR( void \* -1 ),并置位错误码
     2.

        ###### 参数

        > 参数1:要处理的信号号
        >
        > 参数2:信号处理方式一共有三种
        >
        > SIG_IGN:表示忽略该信号
        >
        > SIG_DFL:表示默认处理该信号
        >
        > 自定义函数名:返回值为void,参数为int
     3.

        ###### 功能

        将发送给当前进程的信号与信号处理方式进行绑定
  5.

     ##### alarm(闹钟)

     unsigned int alarm(unsigned int seconds);
     1.

        ###### 返回值

        如果之前没有启动定时器,那么该函数返回0,如果之前启动了定时器,那么该函数返回上一个定时器剩余的秒数,并更新该定时器时间
     2.

        ###### 参数

        要延迟的秒数,如果为0,表示取消该定时器
     3.

        ###### 功能

        启动一个定时器,延迟seconds秒后,向该进程发送SIGALRM信号
  6.

     ##### kill(发信号)

     int kill(pid_t pid, int sig);
     1.

        ###### 返回值

        成功返回0,失败返回-1并置位错误码
     2.

        ###### 参数

        > 参数1:进程ID号
        >
        > \>0:表示向指定进程发送信号  
        >
        > =0:表示向当前进程所在的进程组发送信号  
        >
        > =-1:表示向所有进程发送信号  
        >
        > \<-1:表示向进程号为pid的绝对值那个进程组发送信号
        >
        > 参数2:要发送的信号号
     3.

        ###### 功能

        向指定的进程或进程组发信号
  7.

     ##### raise(发信号)

     int raise(int sig);
     1.

        ###### 返回值

        成功返回0,失败返回非0错误码
     2.

        ###### 参数

        要发送的信号号
     3.

        ###### 功能

        向当前进程发送信号(自己给自己发信号 kill(getpid(), sig))
  #### System V提供的IPC通信方式

  1.

     ##### 概念

     system V提供的通信方式,是将通信容器独立于可执行程序而存在。即使相关程序已经退出,写入容器中的数据如果取出,依然存在
  2.

     ##### 通信方式

     > 消息队列
     >
     > 共享内存
     >
     > 信号量集
     >
     > 注意:
     >
     > 使用该通信方式时,需要先创建一个 IPC 对象,后续只需对该对象进行操作,就可以实现进程间通信
  3.

     ##### 有关System V 提供的IPC对象操作的指令

     > 1、ipcs:可以查看当前所有的消息队列、共享内存、信号量集的属性
     >
     > 2、ipcs -q:只查看消息队列的相关信息
     >
     > 3、ipcs -m:只查看共享内存的相关信息
     >
     > 4、ipcs -s:只查看信号量集的相关信号
     >
     > 5、ipcrm -q/-m/-s ID:表示删除某个消息队列、共享内存、信号量集
  #### 消息队列

  1.

     ##### 原理图

     ![](https://i-blog.csdnimg.cn/direct/393d291b93694fdd991113b0752d88df.png)
  2.

     ##### key(钥匙)值的创建

     key_t ftok(const char \*pathname, int proj_id);
     1.

        ###### 返回值

        成功返回当前ipc对象key值,失败返回-1并置位错误码
     2.

        ###### 参数

        > 参数1:一个必须已经存在的文件路径,占key值的四分之三,其中inode号占2字节,设备号占1字节
        >
        > 参数2:一个随机值,栈key值的1字节
     3.

        ###### 功能

        创建IPC通信的key值
  3.

     ##### API

     1.

        ###### 创建消息队列

        int msgget(key_t key, int msgflg);
        1.

           ###### 返回值

           成功返回该消息队列的id号,以便于后期使用该消息队列,失败返回-1并置位错误码
        2.

           ###### 参数

           > 参数1:key值,可以由ftok创建出来,也可以使用IPC_PRIVATE(适用于亲缘进程间通信)
           >
           > 参数2:消息队列创建的标识
           >
           > IPC_CREAT:表示创建一个消息队列,如果消息队列已经存在,则直接打开  
           >
           > IPC_EXEC:表示确保创建一个新的消息队列,如果该消息队列存在,则报错  
           >
           > 注意:  
           >
           > 如果是创建消息队列,那么该参数中也要包含该消息队列的权限
        3.

           ###### 功能

           通过给定的key值创建一个消息队列对象,并返回该消息队列的id号
     2.

        ###### 向消息队列中存放数据

        int msgsnd(int msqid, const void \*msgp, size_t msgsz, int msgflg);
        1.

           ###### 返回值

           成功返回0,失败返回-1并置位错误码
        2.

           ###### 参数

           > 参数1:消息队列id号
           >
           > 参数2:消息的起始地址,一般是如下结构体类型,需要用户自己定义
           >
           > ```cpp
           > struct msgbuf {
           >                long mtype;       /* 消息类型,必须是大于0的数字,必须放在结构体的最上面 */
           >                char mtext[1];    /* 消息正文 */
           >            };
           > ```
           >
           > 参数3:当前消息的正文的大小,不包含消息的类型大小  
           >
           > 参数4:是否阻塞
           >
           > 0:表示阻塞  
           >
           > IPC_NOWAIT:表示非阻塞
        3.

           ###### 功能

           向msqid对应的消息队列容器中存放以msgp作为起始地址的msgsz大小的数据
     3.

        ###### 从消息队列中读取消息

        ssize_t msgrcv(int msqid, void \*msgp, size_t msgsz, long msgtyp,int msgflg);
        1.

           ###### 返回值

           成功返回读取数据正文的大小,失败返回-1并置位错误码
        2.

           ###### 参数

           > 参数1:消息队列id
           >
           > 参数2:取出数据存放的容器起始地址
           >
           > 参数3:消息正文的大小
           >
           > 参数4:要取出的消息类型
           >
           > \>0:表示取得指定类型的消息的第一个
           >
           > =0:表示取得消息队列的第一个(不限制类型)
           >
           > \<0: 从消息队列中取出类型为小于等于给定类型的绝对值的第一个消息
           >
           > 参数5:是否阻塞
           >
           > 0:表示阻塞
           >
           > IPC_NOWAIT:表示非阻塞
        3.

           ###### 功能

           从消息队列中取出一条消息
     4.

        ###### 消息队列的控制函数

        int msgctl(int msqid, int cmd, struct msqid_ds \*buf);
        1.

           ###### 返回值

           成功返回0,失败返回-1并置位错误码
        2.

           ###### 参数

           > 参数1:消息队列id
           >
           > 参数2: 该函数要执行的操作指令,该函数会因不同的指令执行不同的操作
           >
           > IPC_STAT: 获取当前消息队列的属性信息,并将该属性放入参数3中
           >
           > IPC_SET: 设置当前消息队列的属性信息,需要将新信息当做第三个参数传入
           >
           > IPC_RMID: 删除当前的消息队列,此时参数3可以省略填NULL即可
           >
           > 参数3:根据参数2而定
        3.

           ###### 功能

           用于控制消息队列的操作

脑图

二、作业

作业1:

使用消息队列实现两个进程的相互通信

代码解答:

方法一:进程法
进程A:
cpp 复制代码
#include <myhead.h>  
struct msgbuf
{
	long mtype;      // 消息类型,必须是长整型
	char mtext[128]; // 消息正文,最多包含128个字符
};

#define MSGSZ (sizeof(struct msgbuf)-sizeof(long))  // 定义消息正文的大小,不包含消息类型

int main(int argc, const char *argv[])
{
	key_t key = ftok("/", 'I');  // 使用ftok函数生成唯一的消息队列key
	if (key == -1)  // 错误检查
	{
		perror("ftok error");  // 打印ftok调用错误的详细信息
		return -1;  // 返回错误
	}
	printf("key=%#x\n", key);  // 打印生成的key

	pid_t pid = fork();  // 创建子进程
	if (pid > 0)  // 父进程执行此部分代码
	{
		int msqid = msgget(key, IPC_CREAT | 0664);  // 创建或获取消息队列
		if (msqid == -1)  // 错误检查
		{
			perror("msgget error");  // 打印msgget调用错误信息
			return -1;  // 返回错误
		}
		printf("msqid=%d\n", msqid);  // 打印消息队列ID
		
		struct msgbuf buf;  // 定义发送消息的缓冲区
		while (1)
		{
			printf("请输入消息类型>>>(类型只能有1)");
			scanf("%ld", &buf.mtype);  // 从用户输入中读取消息类型
			getchar();  // 清除输入缓冲区中残留的换行符		
			if (buf.mtype != 1)  // 如果消息类型不为1,提示重新输入
			{
				printf("输入的类型有误请重新输入\n");
				continue;
			}
			printf("请输入消息正文>>>");
			fgets(buf.mtext, MSGSZ, stdin);  // 从标准输入中获取消息正文
			buf.mtext[strlen(buf.mtext) - 1] = 0;  // 去除换行符

			msgsnd(msqid, &buf, MSGSZ, 0);  // 将消息发送到消息队列
			printf("发送成功\n");  // 成功发送提示

			if (strcmp(buf.mtext, "quit") == 0)  // 如果消息内容为"quit",退出循环
			{
				break;
			}
		}
	} 
	else if (pid == 0)  // 子进程执行此部分代码
	{
		int msqid = msgget(key, IPC_CREAT | 0664);  // 获取父进程中创建的消息队列
		if (msqid == -1)  // 错误检查
		{
			perror("msgget error");  // 打印msgget调用错误信息
			return -1;  // 返回错误
		}

		printf("msqid=%d\n", msqid);  // 打印消息队列ID

		struct msgbuf rbuf;  // 定义接收消息的缓冲区
		while (1)
		{
			msgrcv(msqid, &rbuf, MSGSZ, 2, 0);  // 从消息队列中读取类型为2的消息,阻塞方式接收

			printf("收到的消息为:%s\n", rbuf.mtext);  // 打印接收到的消息内容

			if (strcmp(rbuf.mtext, "quit") == 0)  // 如果收到的消息内容为"quit",退出循环
			{
				break;
			}
		}

		// 删除消息队列
		if (msgctl(msqid, IPC_RMID, NULL) == -1)  // 删除消息队列
		{
			perror("msgctl error");  // 打印删除消息队列时的错误信息
			return -1;  // 返回错误
		}

		exit(EXIT_SUCCESS);  // 子进程正常退出
	}
	else  // fork函数调用出错
	{
		perror("fork error");  // 打印fork调用错误信息
		return -1;  // 返回错误
	}

	waitpid(0, NULL, 0);  // 父进程等待子进程退出

	return 0;  // 程序正常结束
}
进程B:
cpp 复制代码
#include <myhead.h>  
struct msgbuf
{
	long mtype;      // 消息类型,必须是长整型
	char mtext[128]; // 消息正文,最多包含128个字符
};

#define MSGSZ (sizeof(struct msgbuf)-sizeof(long))  // 定义消息正文的大小,不包含消息类型

int main(int argc, const char *argv[])
{
	key_t key = ftok("/", 'I');  // 使用ftok函数生成唯一的消息队列key
	if (key == -1)  // 错误检查
	{
		perror("ftok error");  // 打印ftok调用错误的详细信息
		return -1;  // 返回错误
	}
	printf("key=%#x\n", key);  // 打印生成的key

	pid_t pid = fork();  // 创建子进程
	if (pid == 0)  // 子进程执行此部分代码
	{
		int msqid = msgget(key, IPC_CREAT | 0664);  // 创建或获取消息队列
		if (msqid == -1)  // 错误检查
		{
			perror("msgget error");  // 打印msgget调用错误信息
			return -1;  // 返回错误
		}
		printf("msqid=%d\n", msqid);  // 打印消息队列ID
		
		struct msgbuf buf;  // 定义发送消息的缓冲区
		while (1)
		{
			printf("请输入消息类型>>>(类型只能有2)");
			scanf("%ld", &buf.mtype);  // 从用户输入中读取消息类型
			getchar();  // 清除输入缓冲区中残留的换行符		
			if (buf.mtype != 2)  // 如果消息类型不为2,提示重新输入
			{
				printf("输入的类型有误请重新输入\n");
				continue;
			}
			printf("请输入消息正文>>>");
			fgets(buf.mtext, MSGSZ, stdin);  // 从标准输入中获取消息正文
			buf.mtext[strlen(buf.mtext) - 1] = 0;  // 去除换行符

			msgsnd(msqid, &buf, MSGSZ, 0);  // 将消息发送到消息队列
			printf("发送成功\n");  // 成功发送提示

			if (strcmp(buf.mtext, "quit") == 0)  // 如果消息内容为"quit",退出循环
			{
				break;
			}
		}
	} 
	else if (pid > 0)  // 父进程执行此部分代码
	{
		int msqid = msgget(key, IPC_CREAT | 0664);  // 创建或获取消息队列
		if (msqid == -1)  // 错误检查
		{
			perror("msgget error");  // 打印msgget调用错误信息
			return -1;  // 返回错误
		}

		printf("msqid=%d\n", msqid);  // 打印消息队列ID

		struct msgbuf rbuf;  // 定义接收消息的缓冲区
		while (1)
		{
			msgrcv(msqid, &rbuf, MSGSZ, 1, 0);  // 从消息队列中读取类型为1的消息

			printf("收到的消息为:%s\n", rbuf.mtext);  // 打印接收到的消息内容

			if (strcmp(rbuf.mtext, "quit") == 0)  // 如果收到的消息内容为"quit",退出循环
			{
				break;
			}
		}

		// 删除消息队列
		if (msgctl(msqid, IPC_RMID, NULL) == -1)  // 删除消息队列
		{
			perror("msgctl error");  // 打印删除消息队列时的错误信息
			return -1;  // 返回错误
		}

		exit(EXIT_SUCCESS);  // 父进程正常退出
	} 
	else  // fork函数调用出错
	{
		perror("fork error");  // 打印fork调用错误信息
		return -1;  // 返回错误
	}

	waitpid(0, NULL, 0);  // 父进程等待子进程退出

	return 0;  // 程序正常结束
}
方法二:线程法
进程A:
cpp 复制代码
#include <myhead.h>  
// 定义消息缓冲区结构体,包含消息类型和消息正文
struct msgbuf
{
	long mtmype;  // 消息类型,必须是长整型
	char mtext[128];  // 消息正文,最多128个字符
};

#define MSGSZ (sizeof(struct msgbuf) - sizeof(long))  // 计算消息正文的大小,不包含消息类型

// 发送消息到B进程的函数,作为父线程的执行体
void *send_to_b(void *arg)
{
	key_t key = ftok("/", 'I');  // 使用ftok生成唯一的key
	if (key == -1)  // 错误检查
	{
		perror("ftok error");  // 打印ftok调用错误信息
		pthread_exit(NULL);  // 线程退出
	}
	printf("key=%#x\n", key);  // 打印生成的key

	int msqid = msgget(key, IPC_CREAT | 0664);  // 创建或获取消息队列
	if (msqid == -1)  // 错误检查
	{
		perror("msgget error");  // 打印msgget调用错误信息
		pthread_exit(NULL);  // 线程退出
	}
	printf("msqid=%d\n", msqid);  // 打印消息队列ID

	struct msgbuf buf;  // 定义发送消息的缓冲区

	while (1)
	{
		printf("请输入消息类型>>>(只能输入1)");
		scanf("%ld", &buf.mtmype);  // 获取消息类型
		getchar();  // 清除输入缓冲区中的换行符
		if (buf.mtmype != 1)  // 如果消息类型不是1,提示重新输入
		{
			printf("输入有误,请重新输入\n");
			continue;  // 重新输入
		}
		printf("请输入消息正文>>>");
		fgets(buf.mtext, MSGSZ, stdin);  // 获取消息正文
		buf.mtext[strlen(buf.mtext) - 1] = 0;  // 去掉最后的换行符

		msgsnd(msqid, &buf, MSGSZ, 0);  // 将消息发送到消息队列
		printf("发送成功\n");  // 提示发送成功

		if (strcmp(buf.mtext, "quit") == 0)  // 如果消息内容为"quit",退出循环
		{
			break;
		}
	}
	pthread_exit(NULL);  // 线程正常退出
}

// 接收来自B进程消息的函数,作为子线程的执行体
void *receive_from_b(void *arg)
{
	key_t key = ftok("/", 'I');  // 生成唯一的key
	if (key == -1)  // 错误检查
	{
		perror("ftok error");  // 打印ftok调用错误信息
		pthread_exit(NULL);  // 线程退出
	}

	printf("key=%#x\n", key);  // 打印生成的key

	int msqid = msgget(key, IPC_CREAT | 0664);  // 获取或创建消息队列
	if (msqid == -1)  // 错误检查
	{
		perror("msgget error");  // 打印msgget调用错误信息
		pthread_exit(NULL);  // 线程退出
	}

	printf("msqid=%d\n", msqid);  // 打印消息队列ID

	struct msgbuf buf;  // 定义接收消息的缓冲区
	
	while (1)
	{
		msgrcv(msqid, &buf, MSGSZ, 2, 0);  // 从消息队列中接收类型为2的消息
		printf("收到消息为%s\n", buf.mtext);  // 打印收到的消息内容

		if (strcmp(buf.mtext, "quit") == 0)  // 如果收到的消息为"quit",退出循环
		{
			break;
		}
	}

	if (msgctl(msqid, IPC_RMID, NULL) == -1)  // 删除消息队列
	{
		perror("msgctl error");  // 打印msgctl调用错误信息
		pthread_exit(NULL);  // 线程退出
	}
	
	pthread_exit(NULL);  // 线程正常退出
}

// 主函数
int main(int argc, const char *argv[])
{
	pthread_t tid1, tid2;  // 定义两个线程ID变量

	pthread_create(&tid1, NULL, send_to_b, NULL);  // 创建发送消息的父线程
	pthread_create(&tid2, NULL, receive_from_b, NULL);  // 创建接收消息的子线程

	pthread_join(tid1, NULL);  // 等待父线程执行完毕
	pthread_join(tid2, NULL);  // 等待子线程执行完毕

	return 0;  // 程序正常结束
}
进程B:
cpp 复制代码
#include <myhead.h>  

// 定义消息缓冲区结构体,包含消息类型和消息正文
struct msgbuf
{
	long mtmype;  // 消息类型,必须是长整型
	char mtext[128];  // 消息正文,最多128个字符
};

#define MSGSZ (sizeof(struct msgbuf) - sizeof(long))  // 计算消息正文的大小,不包含消息类型

// 发送消息到A进程的函数,作为子线程的执行体
void *send_to_a(void *arg)
{
	key_t key = ftok("/", 'I');  // 使用ftok生成唯一的key
	if (key == -1)  // 错误检查
	{
		perror("ftok error");  // 打印ftok调用错误信息
		pthread_exit(NULL);  // 线程退出
	}
	printf("key=%#x\n", key);  // 打印生成的key

	int msqid = msgget(key, IPC_CREAT | 0664);  // 创建或获取消息队列
	if (msqid == -1)  // 错误检查
	{
		perror("msgget error");  // 打印msgget调用错误信息
		pthread_exit(NULL);  // 线程退出
	}
	printf("msqid=%d\n", msqid);  // 打印消息队列ID

	struct msgbuf buf;  // 定义发送消息的缓冲区

	while (1)
	{
		printf("请输入消息类型>>>(只能输入2)");
		scanf("%ld", &buf.mtmype);  // 获取消息类型
		getchar();  // 清除输入缓冲区中的换行符
		if (buf.mtmype != 2)  // 如果消息类型不是2,提示重新输入
		{
			printf("输入有误,请重新输入\n");
			continue;  // 重新输入
		}
		printf("请输入消息正文>>>");
		fgets(buf.mtext, MSGSZ, stdin);  // 获取消息正文
		buf.mtext[strlen(buf.mtext) - 1] = 0;  // 去掉最后的换行符

		msgsnd(msqid, &buf, MSGSZ, 0);  // 将消息发送到消息队列
		printf("发送成功\n");  // 提示发送成功

		if (strcmp(buf.mtext, "quit") == 0)  // 如果消息内容为"quit",退出循环
		{
			break;
		}
	}
	pthread_exit(NULL);  // 线程正常退出
}

// 接收来自A进程消息的函数,作为父线程的执行体
void *receive_from_a(void *arg)
{
	key_t key = ftok("/", 'I');  // 生成唯一的key
	if (key == -1)  // 错误检查
	{
		perror("ftok error");  // 打印ftok调用错误信息
		pthread_exit(NULL);  // 线程退出
	}

	printf("key=%#x\n", key);  // 打印生成的key

	int msqid = msgget(key, IPC_CREAT | 0664);  // 获取或创建消息队列
	if (msqid == -1)  // 错误检查
	{
		perror("msgget error");  // 打印msgget调用错误信息
		pthread_exit(NULL);  // 线程退出
	}

	printf("msqid=%d\n", msqid);  // 打印消息队列ID

	struct msgbuf buf;  // 定义接收消息的缓冲区
	
	while (1)
	{
		msgrcv(msqid, &buf, MSGSZ, 1, 0);  // 从消息队列中接收类型为1的消息
		printf("收到消息为%s\n", buf.mtext);  // 打印收到的消息内容

		if (strcmp(buf.mtext, "quit") == 0)  // 如果收到的消息为"quit",退出循环
		{
			break;
		}
	}

	if (msgctl(msqid, IPC_RMID, NULL) == -1)  // 删除消息队列
	{
		perror("msgctl error");  // 打印msgctl调用错误信息
		pthread_exit(NULL);  // 线程退出
	}
	
	pthread_exit(NULL);  // 线程正常退出
}

// 主函数
int main(int argc, const char *argv[])
{
	pthread_t tid1, tid2;  // 定义两个线程ID变量

	pthread_create(&tid1, NULL, receive_from_a, NULL);  // 创建接收消息的父线程
	pthread_create(&tid2, NULL, send_to_a, NULL);  // 创建发送消息的子线程

	pthread_join(tid1, NULL);  // 等待父线程执行完毕
	pthread_join(tid2, NULL);  // 等待子线程执行完毕

	return 0;  // 程序正常结束
}

成果展示:

方法一:进程法
方法二:线程法

三、总结

学习内容概述

1. 信号通信:

信号处理的三种方式(默认处理、捕获、忽略)、常见信号类型及其处理、如何使用 `signal` 函数绑定信号处理方式、通过 `alarm` 启动定时器,以及使用 `kill` 和 `raise` 发送信号给进程。

2. System V 提供的 IPC 机制:

包括消息队列的创建、发送、接收及控制函数的使用,重点学习了如何通过 `ftok` 函数生成消息队列的 `key`,以及消息队列的存取操作(`msgsnd` 和 `msgrcv`)。

学习难点

1. 信号处理的机制与应用:

1、理解信号的捕获机制和如何使用 `signal` 函数绑定信号处理函数是较为复杂的部分,尤其是不同信号的处理行为,如 SIGPIPE、SIGSEGV、SIGINT 等的触发条件和处理方式。

2、理解信号的实时性及不可排队处理。多个相同信号发送到一个进程时,可能会导致一些信号丢失,这要求处理程序具有足够的容错能力。

2. 消息队列的使用:

1、理解消息队列的结构体 `msgbuf` 的设计,以及消息类型 `mtype` 的特殊要求(必须为大于 0 的值)。

2、学习如何根据消息类型有选择地从消息队列中接收消息(通过 `msgrcv` 的 `msgtyp` 参数指定),并处理消息队列中的并发与顺序问题。

3、控制消息队列的操作,如 `msgctl` 用于删除或查看消息队列的状态,这在多进程协作中是一个重要但复杂的环节。

主要事项

1. 信号通信的使用:

信号的捕获与处理:

信号是操作系统发送给进程的异步通知,通过 `signal` 函数,进程可以指定自定义的信号处理函数来捕获并处理特定的信号。

常见信号处理:

例如 `SIGINT` 用于捕捉用户按下 `Ctrl+C` 产生的中断信号,`SIGPIPE` 用于处理写管道断裂,`SIGALRM` 是通过 `alarm` 函数生成的定时信号。

信号的发送:

通过 `kill` 可以向指定进程发送信号,`raise` 则用于向当前进程发送信号。信号可以用于进程间的简单通信和控制。

2. System V IPC 机制中的消息队列:

创建消息队列:

通过 `msgget` 函数创建消息队列,`ftok` 函数用于生成消息队列的唯一标识 `key`,确保不同进程可以共享同一个队列。

消息的发送与接收:

使用 `msgsnd` 向队列发送消息,使用 `msgrcv` 从队列中读取消息。每条消息都必须指定一个类型 `mtype`,用于标识消息的优先级或类别。

消息队列的控制:

`msgctl` 提供了一些管理消息队列的功能,如删除队列或查看队列状态,确保队列不再使用时能够清理资源。

未来学习的重点:

1. 深入理解信号的并发处理:

1、学习如何处理多个信号的并发情况,避免重要信号被忽略或丢失,特别是在高并发环境下如何避免信号混乱。同时学习如何使用 `sigaction` 替代 `signal` 进行更精细的信号处理控制。

2、学习更多系统信号的具体作用和场景应用,如 `SIGCHLD` 用于处理子进程退出,`SIGKILL` 强制结束进程等。

2. 高级 IPC 机制:

1、继续深入学习其他 System V IPC 机制,如共享内存(`shmget`、`shmat`)和信号量(`semget`、`semop`),这些是进程间通信的重要方式。

2、理解消息队列的并发访问控制,通过适当的同步机制(如信号量或锁)保证多进程之间的顺序操作和数据一致性。

3. 实战应用与优化:

1、将所学的信号处理机制与 IPC 机制结合应用于实际项目中,如创建一个简单的多进程任务管理系统,使用信号和消息队列进行进程间的协调和通信。

2、学习如何优化 IPC 性能,特别是在多进程并发较高的场景中,如何减少 IPC 操作的延迟,提升消息队列的处理效率。

相关推荐
菜鸟xy..4 分钟前
windows server 2008 建立ftp服务器
运维·服务器
长潇若雪10 分钟前
结构体(C 语言)
c语言·开发语言·经验分享·1024程序员节
feilieren12 分钟前
leetcode - 684. 冗余连接
java·开发语言·算法
Peter44720 分钟前
-bash: ./my_rename.sh: /bin/bash^M: bad interpreter: No such file or directory
开发语言·bash
The Future is mine23 分钟前
Java根据word模板导出数据
java·开发语言
ChinaDragonDreamer23 分钟前
HarmonyOS:@Watch装饰器:状态变量更改通知
开发语言·harmonyos·鸿蒙
团儿.27 分钟前
KVM磁盘配置:构建高效虚拟环境的基石
linux·运维·centos·kvm·kvm磁盘
一颗甜苞谷36 分钟前
开源一款前后端分离的企业级网站内容管理系统,支持站群管理、多平台静态化,多语言、全文检索的源码
java·开发语言·开源
星夜孤帆36 分钟前
Java面试题集锦
java·开发语言
论迹44 分钟前
【Java】-- 接口
java·开发语言