Bootstrap

I\O进程线程(Day32)

一、学习内容

  1. 进程之间的通信(nterprocess communication)

    1. 信号通信

      1. 概念

        1> 信号通信中,多个进程只起到通知作用,没有数据传输的功能

        2> 所谓信号通信,就是软件模拟的硬件的中断请求

        3>原理图

      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))
    2. 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:表示删除某个消息队列、共享内存、信号量集

    3. 消息队列

      1. 原理图
      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:消息的起始地址,一般是如下结构体类型,需要用户自己定义

            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. 功能
            用于控制消息队列的操作
  2. 脑图

二、作业

作业1:

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

 

 代码解答:

方法一:进程法
进程A:
#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:
#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:
#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:
#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 操作的延迟,提升消息队列的处理效率。

;