系统编程第三节——信号

进程间通信 — 信号

1,Linux下的信号

信号机制是Linux系统内核管理任务的一种重要机制,信号传递的信息有限,所以都是表达一些特定意义,大部分的信号的默认功能都是让目标进程退出,暂停(SIGSTOP),继续(SIGCONT)。
1)Linux下的信号可以通过命令 kill -l 查看

一共是 62个信号。
前面的31个信号, 1~31
1、这些信号被称为非实时信号,也叫作不可靠信号
2、信号不会排队,但是会嵌套,如果有新的信号到达,但是原来的信号没有及时响应,前面的信号会被丢弃掉。
3、每一个信号对应一个事件,当这个事件发送的时候,系统就会给进程发送信号
4、如果进程挂起的信号中同时存在实时信号与非实时信号,进程会优先响应实时信号,实时信号的优先级从大到小。

后面31个信号,34~64
1、实时信号,也叫可靠信号
2、接受排队,不接受嵌套
3、如果同时发生多个相同的实时信号,只响应一次
4、实时信号没有具体的事件对应。

–》信号9 (SIGKILL)、 19(SIGSTOP)不允许被捕捉,忽略,阻塞的。

在这里插入图片描述

2)信号的发送
==> kill ==> kill -信号值 进程ID
==> killall ==> killall -信号值 进程名

==> kill() 发送信号的函数
函数的API
SYNOPSIS

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

==> pid : 目标进程的进程ID
==> sig : 信号值
返回值: 成功返回0,失败返回-1

练习1:父进程创建2个子进程,父进程3s之后发送3号信号给子进程1,5s之后发送1号信号给子进程2,父进程等待两个子进程都结束之后,父进程再结束。

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
/*父进程给两个子进程分别发送信号*/
int main(int argc, char *argv[])
{
	//1,父进程创建两个子进程
	pid_t pid1, pid2;
	pid1 = fork();  //创建进程
	if(pid1 > 0)	//父进程执行部分
	{
		pid2 = fork();  //创建进程
		if(pid2 > 0)	//父进程执行部分
		{
			sleep(3);   // 等待3S
			kill(pid1, 3);	//发送3号信号给子进程1
			printf("发送信号3给子进程1\n");
			sleep(2);
			kill(pid2, 1);	//发送1号信号给子进程2
			printf("发送信号1给子进程2\n");
			wait(NULL);
			wait(NULL);
			exit(0);     //退出进程
		}
		if(0 == pid2)	//子进程2执行部分
		{
			int time;
			for(time = 0; ; time++)
			{
				printf("child2: time:%d\n", time);
				sleep(1);
			}	
		}
	}
	if(0 == pid1)	//子进程1执行部分
	{
		while(1);
	}
	
	return 0;
}


关于参数pid的补充:
1、Pid > 0; 信号发送给进程号为pid的进程
2、Pid == 0; 信号发送给同一组内其他所有进程
3、Pid == -1; 信号发送给所有进程(当前进程具有权限发送的进程)
4、Pid < -1; 信号发送给进程组ID为pid绝对值的进程

发送成功返回0,失败返回-1,并且错误被设置
EINVAL : 信号无效或不支持
EPERM : 没有权限发送
ESRCH :目标进程不存在

3、信号的捕捉 signal ()
==> 进程在接收到信号之后,如果没有特殊设置,会响应信号的默认动作,但是一些时候我们希望接收到信号之后不去响应退出,此时可以通过signal函数对信号进行捕捉,忽略。
·函数原型 signal

SYNOPSIS

   #include <signal.h>
   typedef void (*sighandler_t)(int);	//参数为int,返回值为void函数指针
   sighandler_t signal(int signum, sighandler_t handler);

==> signum : 要捕捉的信号值 (SIGKILL,SIGSTOP不能被捕捉)
==> handler : 捕捉到信号之后执行的动作
SIG_IGN : 忽略这个信号
SIG_DFL : 执行默认动作
Fun : 自定义的函数指针 --> 捕捉到信号之后执行规定的动作

==> 例子: 设计程序。使用signal函数捕捉信号 ctrl + c (SIGINT),每次捕捉到信号,就打印一句话 :”有话好好说!”

==> 练习2: 设计程序,父进程创建一个子进程,父进程使用signal函数捕捉信号1,每次捕捉到信号1就打印 “小样!”, 父进程循环打印时间,子进程3s之后给父进程发送一个信号1, 再等2s发送一个信号2,然后子进程退出。

4)alarm --> 给自己发送一个闹钟信号 SIGALRM
SYNOPSIS

   #include <unistd.h>

   unsigned int alarm(unsigned int seconds);

==> seconds : 时间(单位 秒)

==> 例子: 设计程序,每5秒钟打印一次helloworld.

2,信号集

1) 信号集的概念
Signal函数的功能是将信号捕捉,然后去执行特定的事件,或者是忽略,或者执行默认动作。而信号集的作用是把信号阻塞。
每一个进程都会有一个描述将来哪些信号会被阻塞的信号集,这个信号集也被成为信号掩码,当某一个信号在这个被阻塞的信号集 (sigset_t) 中,那么传送到该进程的这种信号会被阻塞。
创建子进程时,子进程会继承父进程的信号集。

2)信号忽略与信号阻塞的区别
阻塞: 操作系统在进程解除信号阻塞之前不会把信号传递出去,被阻塞的信号也不会影响进程的行为。
忽略:当进程忽略一个信号时,信号会被传递出去,但进程会把信号丢弃。

3)相关函数
SYNOPSIS 信号集操作概念函数

   #include <signal.h>

   int sigemptyset(sigset_t *set);		//清空信号集
   int sigfillset(sigset_t *set);		//把信号集填满
   int sigaddset(sigset_t *set, int signum);	//把信号添加到信号集
   int sigdelset(sigset_t *set, int signum);	//把信号移出信号集
   int sigismember(const sigset_t *set, int signum);	//判断信号是否在信号集中

SYNOPSIS 设置对信号集内的信号的处理方式

   #include <signal.h>

   int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

==> how : 对信号集中的信号的处理方式
SIG_BLOCK : 在原来的基础上,添加参数set信号集中的信号
SIG_SETMASK :将set中信号替换掉原来的信号
SIG_UNBLOCK :在原来的基础上解除set里面的信号

==> set : 信号集变量的地址
==> oldset : 原来的信号集地址
返回值: 成功返回0,失败返回-1.

==》例子: 设计一个程序,把信号集添加 SIGHUP, SIGINT, SIGQUIT 信号添加阻塞,10s做自己的事情,10s之后解除阻塞。其他的进程这个过程中给这个进程发送 SIGHUP信号,

进程间通信 – System V IPC对象

1, System V IPC对象
System V IPC对象是Linux从Unix中继承过来的进程间通信方式。
最初进程间的通信主要是依赖信号与管道,但是这两种通信方式各有不足之处,随着系统发展,这些通信方式不太能满足通信需求。例如:无名管道只能实现亲缘进程之间的通信,有名管道传递的数据接收方只能有一个,不知道发送方是谁,信号传递的信息量太少…
为了解决这些问题,人们发明了IPC对象

2,IPC对象
IPC对象主要是指: 消息队列, 共享内存, 信号量
IPC对象不存在与Linux的文件系统中,它的唯一标识符是它的key值。通过key值来识别IPC对象,IPC对象除了key值之外,还有对应的ID,这个ID是我们操作IPC对象的句柄。

==> 注意: 每一个IPC对象的key值是唯一的,ID不是惟一的。
==> IPC对象会一直存在于Linux系统中,不会随着进程的结束而消失。

1)相关命令
Ipcs -a 查看当前系统中所有的IPC对象

------ Message Queues -------- //消息队列
key msqid owner perms used-bytes messages

------ Shared Memory Segments -------- //共享内存
key shmid owner perms bytes nattch status
0x00000000 393216 gec 600 524288 2 dest
0x00000000 1245185 gec 600 524288 2 dest
0x00000000 589826 gec 600 16777216 2
0x00000000 622595 gec 600 524288 2 dest
0x00000000 884740 gec 600 524288 2 dest
0x00000000 983045 gec 600 524288 2 dest
0x00000000 1081350 gec 600 524288 2 dest
0x00000000 1146887 gec 600 16777216 2 dest
0x00000000 1343496 gec 600 524288 2 dest
0x00000000 1605641 gec 600 524288 2 dest
0x00000000 1835019 gec 600 67108864 2 dest

------ Semaphore Arrays -------- //信号量
key semid owner perms nsems

==> 删除IPC对象 ipcrm
==> 删除消息队列 : ipcrm -q msgid
==> 删除共享内存 : ipcrm -m shmid
==> 删除信号量 : pcrm -s semid

消息队列(类似于管道,消息是带有类型的)
在这里插入图片描述

2)相关函数
(1)获取IPC对象键值 key – ftok()
SYNOPSIS

   #include <sys/types.h>
   #include <sys/ipc.h>

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

==> pathname : 一个合法的路径 (路径要存在)
==> proj_id : 一个大于0的整数
==> 返回值: 成功返回键值,失败返回-1;

(2)获取消息队列的ID --> msgget()
SYNOPSIS

   #include <sys/types.h>
   #include <sys/ipc.h>
   #include <sys/msg.h>

   int msgget(key_t key, int msgflg);	

==> key : IPC对象的key值
==> msgflg : 标志位
IPC_CREAT | 0666 ; 如果对象不存在则创建
IPC_EXCL 如果对象存在则报错
返回值: 成功返回消息队列的ID,失败返回-1

(3)收发消息 msgsnd, msgrcv,
SYNOPSIS

   #include <sys/types.h>
   #include <sys/ipc.h>
   #include <sys/msg.h>

   int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);		//发送消息

==> msqid : 消息队列的ID
==> msgp : 需要发送的消息
==> msgsz : 消息的大小
==> msgflg : 消息的标志,默认是0

   ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);	//接收

==> msqid : 消息队列的ID
==> msgp : 接收消息的缓冲区地址
==> msgsz : 消息的大小
==> msgtyp : 消息的类型
==> msgflg : 消息的标志,默认是0

消息结构体:

struct msgbuf {
long mtype; /* message type, must be > 0 / 消息类型
char mtext[1]; /
message data */ //消息数据 ==> 自行设计大小
};

(4)消息队列的删除 msgctl
SYNOPSIS

   #include <sys/types.h>
   #include <sys/ipc.h>
   #include <sys/msg.h>

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

==> msqid : 消息队列ID
==> cmd : 操作命令 (IPC_RMID:删除IPC对象)
==> buf : 如果第二个参数选择IPC_RMID ,那第三个参数不要了

练习
1,理解消息队列单向通信功能代码,实现使用一个消息队列实现双向通信。
进程A 进程B
父进程A写入类型1的消息 子进程b读取类型1的消息
子进程a读取类型2的消息 父进程B写入类型2的消息

进程A端代码

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

#define MSQ_PATH "."
#define PROJ_ID  10

#define MTYPE_1  1		//消息类型1
#define MTYPE_2  2		//消息类型2
#define BUF_SIZE 1024

struct msgbuf {
	long mtype;       /* message type, must be > 0 */ //消息类型
	char mtext[BUF_SIZE];    /* message data */		//消息数据 ==> 自行设计大小
};

/*进程A*/
int main(int argc, char *argv[])
{
	struct msgbuf msgp;		//消息缓冲区
	
	//1, 获取消息队列key值
	key_t key = ftok(MSQ_PATH, PROJ_ID);
	if(-1 == key)
	{
		perror("ftok failed");
		return -1;
	}
	
	//2, 获取消息队列操作ID
	int msqid = msgget(key, IPC_CREAT | 0666);
	if(-1 == msqid)
	{
		perror("msgget failed");
		return -1;
	}	
	
	//3, 创建子进程 --> fork
	pid_t pid = fork();
	if(-1 == pid)
	{
		perror("fork failed");
		return -1;
	}	
	//父进程循环往消息队列发送类型为1的消息
	if(pid > 0)
	{
		for( ; ;)
		{
			memset(msgp.mtext, 0, sizeof(msgp.mtext));
			msgp.mtype = MTYPE_1;	
			fgets(msgp.mtext, sizeof(msgp.mtext), stdin);	//scanf
			msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0);	//发送消息			
		}
	}
	
	//子进程循环从消息队列中获取类型2的消息
	if(pid == 0)
	{
		for(; ;)
		{
			memset(msgp.mtext, 0, sizeof(msgp.mtext));
			msgrcv(msqid, &msgp, sizeof(msgp.mtext), MTYPE_2, 0);	//接收消息
			printf("recv from rose : %s", msgp.mtext);		
		}

	}
	
	return 0;
}

进程B端代码

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

#define MSQ_PATH "."
#define PROJ_ID  10

#define MTYPE_1  1		//消息类型1
#define MTYPE_2  2		//消息类型2
#define BUF_SIZE 1024

struct msgbuf {
	long mtype;       /* message type, must be > 0 */ //消息类型
	char mtext[BUF_SIZE];    /* message data */		//消息数据 ==> 自行设计大小
};

/*进程B*/
int main(int argc, char *argv[])
{
	struct msgbuf msgp;		//消息缓冲区
	
	//1, 获取消息队列key值
	key_t key = ftok(MSQ_PATH, PROJ_ID);
	if(-1 == key)
	{
		perror("ftok failed");
		return -1;
	}
	
	//2, 获取消息队列操作ID
	int msqid = msgget(key, IPC_CREAT | 0666);
	if(-1 == msqid)
	{
		perror("msgget failed");
		return -1;
	}	
	
	//3, 创建子进程 --> fork
	pid_t pid = fork();
	if(-1 == pid)
	{
		perror("fork failed");
		return -1;
	}	
	//父进程循环往消息队列发送类型为2的消息
	if(pid > 0)
	{
		for( ; ;)
		{
			memset(msgp.mtext, 0, sizeof(msgp.mtext));
			msgp.mtype = MTYPE_2;	
			fgets(msgp.mtext, sizeof(msgp.mtext), stdin);	//scanf
			msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0);	//发送消息			
		}
	}
	
	//子进程循环从消息队列中获取类型1的消息
	if(pid == 0)
	{
		for(; ;)
		{
			memset(msgp.mtext, 0, sizeof(msgp.mtext));
			msgrcv(msqid, &msgp, sizeof(msgp.mtext), MTYPE_1, 0);	//接收消息
			printf("recv from jack : %s", msgp.mtext);		
		}

	}
	
	return 0;
}








2,利用相关函数实现以下功能
父进程创建2个子进程。父进程循环打印时间。 alarm
父进程每隔5s打印”开饭了!”,并且给子进程发送信号,子进程接收到信号之后打印 “Child 1 process 吃饱了!”
“Child 2 process 吃饱了!”
或者
“Child 2 process 吃饱了!”
“Child 1 process 吃饱了!”
不断循环实现这个效果。

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>

pid_t pid1, pid2;

void fun(int arg)
{
	printf("开饭了!\n");
	kill(pid1, SIGQUIT);
	kill(pid2, SIGQUIT);
	alarm(5);
}

void fun1(int arg)
{
	printf("Child 1 process 吃饱了!\n");
}

void fun2(int arg)
{
	printf("Child 2 process 吃饱了!\n");
}

/*父进程循环给子进程喂饭*/
int main(int argc, char *argv[])
{
	//1,创建2条子进程
	pid1 = fork();
	if(-1 == pid1)
	{	
		perror("fork 1 failed");
		return -1;
	}
	if(0 == pid1)	//子进程1 子进程1与子进程2循环等待父进程的信号
	{
		signal(SIGQUIT, fun1);
		while(1)
			pause();
	}
	if(0 < pid1)	//父进程
	{
		pid2 = fork();
		if(-1 == pid2)
		{	
			perror("fork 2 failed");
			return -1;
		}		
		if(0 == pid2)	//子进程2
		{
			signal(SIGQUIT, fun2);
			while(1)
				pause();
		}
		if(0 < pid2)	//父进程	//2,父进程注册捕捉信号 SIGALRM //3,父进程循环打印时间
		{
			signal(SIGALRM, fun);
			alarm(5);
			for(int i = 0; ; i++)
			{
				printf("time : %d\n", i);
				sleep(1);
			}
			wait(NULL);
			wait(NULL);
		}		
		
	}
	

	return 0;
}



3,-- 使用消息队列实现一个服务器转发功能
实现思路: 服务器循环从消息队列中接收类型1的消息。接收到消息就输出。
进程A的父进程A循环发送类型为1的消息,子进程a循环接收类型为2的消息,接收到就打印。
进程B的父进程B循环发送类型为1的消息,子进程B循环接收类型为3的消息,接收到就打印。
进程C的父进程C循环发送类型为1的消息,子进程c循环接收类型为4的消息,接收到就打印。
进程A,B,C任何一个想要发送广播数据(服务器转发给其他进程),那就给服务器发送类型为”BoardCast:xxxxx”;的数据,如果服务器接收到这种数据,那就把这个数据转发给进程A, B, C
进程A

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

#define MSQ_PATH "."
#define PROJ_ID  100

#define MTYPE_1  1		//消息类型1
#define MTYPE_2  2		//消息类型2
#define MTYPE_3  3		//消息类型3
#define MTYPE_4  4		//消息类型4

#define BUF_SIZE 1024

struct msgbuf {
	long mtype;       /* message type, must be > 0 */ //消息类型
	char mtext[BUF_SIZE];    /* message data */		//消息数据 ==> 自行设计大小
};

/*进程A*/
int main(int argc, char *argv[])
{
	struct msgbuf msgp;		//消息缓冲区
	
	//1, 获取消息队列key值
	key_t key = ftok(MSQ_PATH, PROJ_ID);
	if(-1 == key)
	{
		perror("ftok failed");
		return -1;
	}
	
	//2, 获取消息队列操作ID
	int msqid = msgget(key, IPC_CREAT | 0666);
	if(-1 == msqid)
	{
		perror("msgget failed");
		return -1;
	}	
	
	//3, 创建子进程 --> fork
	pid_t pid = fork();
	if(-1 == pid)
	{
		perror("fork failed");
		return -1;
	}	
	//父进程循环往消息队列发送类型为1的消息
	if(pid > 0)
	{
		for( ; ; )
		{
			memset(msgp.mtext, 0, sizeof(msgp.mtext));
			msgp.mtype = MTYPE_1;	
			fgets(msgp.mtext, sizeof(msgp.mtext), stdin);	//scanf
			msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0);	//发送消息			
		}
	}
	
	//子进程循环从消息队列中获取类型2的消息
	if(pid == 0)
	{
		for(; ;)
		{
			memset(msgp.mtext, 0, sizeof(msgp.mtext));
			msgrcv(msqid, &msgp, sizeof(msgp.mtext), MTYPE_2, 0);	//接收消息
			printf("%s", msgp.mtext);		
		}

	}
	
	return 0;
}

进程B

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

#define MSQ_PATH "."
#define PROJ_ID  100

#define MTYPE_1  1		//消息类型1
#define MTYPE_2  2		//消息类型2
#define MTYPE_3  3		//消息类型3
#define MTYPE_4  4		//消息类型4

#define BUF_SIZE 1024

struct msgbuf {
	long mtype;       /* message type, must be > 0 */ //消息类型
	char mtext[BUF_SIZE];    /* message data */		//消息数据 ==> 自行设计大小
};

/*进程B*/
int main(int argc, char *argv[])
{
	struct msgbuf msgp;		//消息缓冲区
	
	//1, 获取消息队列key值
	key_t key = ftok(MSQ_PATH, PROJ_ID);
	if(-1 == key)
	{
		perror("ftok failed");
		return -1;
	}
	
	//2, 获取消息队列操作ID
	int msqid = msgget(key, IPC_CREAT | 0666);
	if(-1 == msqid)
	{
		perror("msgget failed");
		return -1;
	}	
	
	//3, 创建子进程 --> fork
	pid_t pid = fork();
	if(-1 == pid)
	{
		perror("fork failed");
		return -1;
	}	
	//父进程循环往消息队列发送类型为1的消息
	if(pid > 0)
	{
		for( ; ;)
		{
			memset(msgp.mtext, 0, sizeof(msgp.mtext));
			msgp.mtype = MTYPE_1;	
			fgets(msgp.mtext, sizeof(msgp.mtext), stdin);	//scanf
			msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0);	//发送消息			
		}
	}
	
	//子进程循环从消息队列中获取类型3的消息
	if(pid == 0)
	{
		for(; ;)
		{
			memset(msgp.mtext, 0, sizeof(msgp.mtext));
			msgrcv(msqid, &msgp, sizeof(msgp.mtext), MTYPE_3, 0);	//接收消息
			printf("%s", msgp.mtext);		
		}

	}
	
	return 0;
}

进程C

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

#define MSQ_PATH "."
#define PROJ_ID  100

#define MTYPE_1  1		//消息类型1
#define MTYPE_2  2		//消息类型2
#define MTYPE_3  3		//消息类型3
#define MTYPE_4  4		//消息类型4

#define BUF_SIZE 1024

struct msgbuf {
	long mtype;       /* message type, must be > 0 */ //消息类型
	char mtext[BUF_SIZE];    /* message data */		//消息数据 ==> 自行设计大小
};

/*进程C*/
int main(int argc, char *argv[])
{
	struct msgbuf msgp;		//消息缓冲区
	
	//1, 获取消息队列key值
	key_t key = ftok(MSQ_PATH, PROJ_ID);
	if(-1 == key)
	{
		perror("ftok failed");
		return -1;
	}
	
	//2, 获取消息队列操作ID
	int msqid = msgget(key, IPC_CREAT | 0666);
	if(-1 == msqid)
	{
		perror("msgget failed");
		return -1;
	}	
	
	//3, 创建子进程 --> fork
	pid_t pid = fork();
	if(-1 == pid)
	{
		perror("fork failed");
		return -1;
	}	
	//父进程循环往消息队列发送类型为1的消息
	if(pid > 0)
	{
		for( ; ;)
		{
			memset(msgp.mtext, 0, sizeof(msgp.mtext));
			msgp.mtype = MTYPE_1;	
			fgets(msgp.mtext, sizeof(msgp.mtext), stdin);	//scanf
			msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0);	//发送消息			
		}
	}
	
	//子进程循环从消息队列中获取类型4的消息
	if(pid == 0)
	{
		for(; ;)
		{
			memset(msgp.mtext, 0, sizeof(msgp.mtext));
			msgrcv(msqid, &msgp, sizeof(msgp.mtext), MTYPE_4, 0);	//接收消息
			printf("%s", msgp.mtext);		
		}

	}
	
	return 0;
}

消息队列服务器

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

#define MSQ_PATH "."
#define PROJ_ID  100

#define MTYPE_1  1		//消息类型1
#define MTYPE_2  2		//消息类型2
#define MTYPE_3  3		//消息类型3
#define MTYPE_4  4		//消息类型4

#define BUF_SIZE 1024

struct msgbuf {
	long mtype;       /* message type, must be > 0 */ //消息类型
	char mtext[BUF_SIZE];    /* message data */		//消息数据 ==> 自行设计大小
};


/*消息队列服务器*/
int main(int argc, char *argv[])
{
	struct msgbuf msgp;		//消息缓冲区
	
	//1, 获取消息队列key值
	key_t key = ftok(MSQ_PATH, PROJ_ID);
	if(-1 == key)
	{
		perror("ftok failed");
		return -1;
	}
	
	//2, 获取消息队列操作ID
	int msqid = msgget(key, IPC_CREAT | 0666);
	if(-1 == msqid)
	{
		perror("msgget failed");
		return -1;
	}	

	//3,循环接收类型为1的消息,如果接收到以"BoardCast:"开头的消息,就转发给所有客户端
	for( ; ; )
	{
		memset(msgp.mtext, 0, sizeof(msgp.mtext));
		msgrcv(msqid, &msgp, sizeof(msgp.mtext), MTYPE_1, 0);	//接收消息
		printf("recv msg : %s", msgp.mtext);
		if( strncmp("BoardCast:", msgp.mtext, 10) == 0)	//如果按照广播格式发送消息到服务器,那就转发
		{
			msgp.mtype = MTYPE_2;	
			msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0);	//发送类型2消息		

			msgp.mtype = MTYPE_3;	
			msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0);	//发送类型3消息
			
			msgp.mtype = MTYPE_4;	
			msgsnd(msqid, &msgp, sizeof(msgp.mtext), 0);	//发送类型4消息
		}
		
	}
	
	
	
	
	return 0;
}

例程:父进程给两个子进程分别发送信号

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
/*父进程给两个子进程分别发送信号*/
int main(int argc, char *argv[])
{
	//1,父进程创建两个子进程
	pid_t pid1, pid2;
	pid1 = fork();  //创建进程
	if(pid1 > 0)	//父进程执行部分
	{
		pid2 = fork();  //创建进程
		if(pid2 > 0)	//父进程执行部分
		{
			sleep(3);   // 等待3S
			kill(pid1, 3);	//发送3号信号给子进程1
			printf("发送信号3给子进程1\n");
			sleep(2);
			kill(pid2, 1);	//发送1号信号给子进程2
			printf("发送信号1给子进程2\n");
			wait(NULL);
			wait(NULL);
			exit(0);     //退出进程
		}
		if(0 == pid2)	//子进程2执行部分
		{
			int time;
			for(time = 0; ; time++)
			{
				printf("child2: time:%d\n", time);
				sleep(1);
			}	
		}
	}
	if(0 == pid1)	//子进程1执行部分
	{
		while(1);
	}
	
	return 0;
}


例程:创建两个进程,父进程等待一个信号,如果父进程接收到信号就触发函数

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

pid_t pid1, pid2;

void fun(int arg)	//父进程信号触发函数
{
	kill(pid1, SIGHUP);		//给子进程1发送信号
	kill(pid2, SIGHUP);		//给子进程2发送信号
}

void fun1(int arg)
{
	printf("Child process 1 is killed by parent!\n");
}

void fun2(int arg)
{
	printf("Child process 2 is killed by parent!\n");
}


int main(int argc, char *argv[])
{
	//1,父进程创建两个子进程
	pid1 = fork();
	if(pid1 > 0)	//父进程
	{
		pid2 = fork();
		if(pid2 > 0)	//父进程
		{
			signal(SIGINT, fun);
			pause();	//挂起等待一个信号
			wait(NULL);
			wait(NULL);
			printf("Parent process exit!\n");
		}
		if(pid2 == 0)	//子进程2
		{
			signal(SIGHUP, fun2);
			pause();	
		}
		
	}
	if(pid1 == 0)	//子进程1
	{
		signal(SIGHUP, fun1);
		pause();	
	}
	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值