嵌入式Linux进程间的通信方式

【1】分类
早期进程间通信方式:
无名管道
有名管道
信号通信

system V
消息队列
共享内存
信号灯集

BSD
scoket(套接字)通信

【2】本质
任何一个进程在32位操作里面都会有4G的虚拟空间,包含1G内核空间和3G用户空间,进程间能够通信,就是在内核空间进行读写数据。

共享内存效率最高,它是直接获取到物理内存的地址,对物理内存直接操作

前六种进程间通信方式只能实现在同一台主机的多个进程之间通信,而套接字通信可以实现在不同的主机的进程之间通信

【3】无名管道
(1)性质
只能用于具有亲缘关系的进程之间的通信,半双工的通信模式,具有固定的读端和写端。

管道可以看成是一种特殊的文件,对于它的读写可以使用文件IO如read、write函数。

管道是基于文件描述符的通信方式
当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1]。
fd[0]固定用于读管道,fd[1]固定用于写管道。

(2)创建无名管道

#include
int pipe(int fd[2]);

功能:创建一个无名管道
参数
fd:操作无名管道的数组,有两个成员,第一个负责读,第二个负责写
返回值
成功:0
失败:-1

#include 
#include 
#include 

int main(int argc, const char *argv[])
{
	//创建一个无名管道
	int fd[2];
	if(pipe(fd) < 0)
	{
		perror("fail to pipe");
		exit(1);
	}

	//printf("fd[0] = %d\n", fd[0]);
	//printf("fd[1] = %d\n", fd[1]);
	
	//无名管道可以使用文件io的函数直接操作
	//read write
	
	//fd[1]负责写数据
	//往无名管道写数据,下一次写的数据会放在第一个写的后面,以追加的方式写数据
	char s1[32] = "hello world";
	char s2[32] = "nihao beijing";

	write(fd[1], s1, sizeof(s1));
	write(fd[1], s2, sizeof(s1));

	//fd[0]负责读数据
	//从无名管道读取数据,读取的数据会从无名管道里面删除
	char buf[32] = {};
	read(fd[0], buf, sizeof(buf));
	printf("buf = %s\n", buf);
	
	read(fd[0], buf, sizeof(buf));
	printf("buf = %s\n", buf);

	return 0;
}

(3)无名管道的读写规律
如果读写端都存在,如果只读不写,如果管道内有数据,会正常读取,但是如果没有数据,就会一直阻塞。

#include 
#include 
#include 

int main(int argc, const char *argv[])
{
	int fd[2];
	if(pipe(fd) < 0)
	{
		perror("fail to pipe");
		exit(1);
	}

	//如果读写端都存在,如果只读不写,如果管道内有数据,会正常读取,但是如果没有数据,就会一直阻塞
	char s[32] = "hello world";
	write(fd[1], s, sizeof(s));

	char buf[32] = {};
	read(fd[0], buf, sizeof(buf));
	printf("buf = %s\n", buf);
	
	read(fd[0], buf, sizeof(buf));
	printf("buf = %s\n", buf);

	return 0;
}

如果读写端都存在,如果只写不读,当管道写满是,就会阻塞,默认无名管道64k字节。

#include 
#include 
#include 

int main(int argc, const char *argv[])
{
	int fd[2];
	if(pipe(fd) < 0)
	{
		perror("fail to pipe");
		exit(1);
	}

	//如果读写端都存在,如果只写不读,当管道写满是,就会阻塞,默认无名管道64k字节
	char s[4] = "yes";
	int n = 0;
	while(1)
	{
		write(fd[1], s, 4);
		n++;
		printf("n = %d\n", n);
	}

	return 0;
}

如果只有读端没有写端,如果管道内有数据,则正常读取,如果没有数据,则read返回0。

#include 
#include 
#include 

int main(int argc, const char *argv[])
{
	int fd[2];
	if(pipe(fd) < 0)
	{
		perror("fail to pipe");
		exit(1);
	}

	//如果只有读端没有写端,如果管道内有数据,则正常读取,如果没有数据,则read返回0
	
	write(fd[1], "hello world", 32);

	close(fd[1]);

	char buf[32] = {};
	ssize_t bytes;
	bytes = read(fd[0], buf, sizeof(buf));
	printf("bytes = %d\n", bytes);
	printf("buf = %s\n", buf);	

	bytes = read(fd[0], buf, sizeof(buf));
	printf("bytes = %d\n", bytes);
	printf("buf = %s\n", buf);

	return 0;
}

如果只有写端没有读端,当运行到write时,就会产生SIGPIPE(管道破裂),当前信号默认的处理方式是结束整个进程,所以程序直接退出。

#include 
#include 
#include 

int main(int argc, const char *argv[])
{
	int fd[2];
	if(pipe(fd) < 0)
	{
		perror("fail to pipe");
		exit(1);
	}

	//如果只有写端没有读端,当运行到write时,就会产生SIGPIPE(管道破裂),
	//当前信号默认的处理方式是结束整个进程,所以程序直接退出
	
	close(fd[0]);

	char s[4] = "yes";
	int n = 0;
	ssize_t bytes;
	while(1)
	{
		bytes = write(fd[1], s, 4);
		printf("bytes = %d\n", bytes);
		n++;
		printf("n = %d\n", n);
	}

	return 0;
}

以下是父子进程,通过无名管道实现互相通信:

#include 
#include 
#include 
#include 

#define N 32

int main(int argc, const char *argv[])
{
	int fd1[2], fd2[2];
	if(pipe(fd1) < 0)
	{
		perror("fail to pipe");
		exit(1);
	}

	if(pipe(fd2) < 0)
	{
		perror("fail to pipe");
		exit(1);
	}

	pid_t pid;
	if((pid = fork()) < 0)
	{
		perror("fail to fork");
		exit(1);
	}
	else if(pid > 0) //父进程
	{
		//父进程先往管道写入数据
		char buf[N] = {};
		while(1)
		{
			fgets(buf, N, stdin);
			buf[strlen(buf) - 1] = '\0';

			write(fd1[1], buf, N);
			if(strncmp(buf, "quit", 4) == 0)
			{
				exit(0);
			}
			else 
			{
				read(fd2[0], buf, N);
				if(strncmp(buf, "quit", 4) == 0)
				{
					exit(0);
				}
				else 
				{
					printf("from child: %s\n", buf);
				}
			}
		}
	}
	else //子进程
	{
		//子进程先读取管道的数据
		char s[N] = {};
		while(1)
		{
			read(fd1[0], s, N);

			if(strncmp(s, "quit", 4) == 0)
			{
				exit(0);
			}
			else 
			{
				printf("from parent: %s\n", s);

				fgets(s, N, stdin);
				s[strlen(s) - 1] = '\0';

				write(fd2[1], s, N);
				if(strncmp(s, "quit", 4) == 0)
				{
					exit(0);
				}
			}
		}
	}

	return 0;
}

【4】有名管道
(1)性质
无名管道只能用于具有亲缘关系的进程之间,这就限制了无名管道的使用范围。有名管道可以使互不相关的两个进程互相通信。有名管道可以通过路径名来指出,并且在文件系统中可见。

进程通过文件IO来操作有名管道,有名管道遵循先进先出规则,不支持如lseek() 操作 。

有名管道还是在内核空间开辟区域,然后在本地创建一个管道文件用于标识内核空间的区域,对当前管道文件的操作就是对内核空间的操作。

(2)创建有名管道
方法1:使用mkfifo命令
mkfifo fifoname

方法2:使用mkfifo函数创建有名管道

	#include 
	#include 
	int mkfifo(const char *pathname, mode_t mode);

功能:创建一个有名管道,产生一个管道文件
参数
pathname:文件名
mode:操作权限,一般为八进制数组成,例如0664
返回值
成功:0
失败:-1

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define FIFONAME "myfifo"

int main(int argc, const char *argv[])
{
	//创建一个有名管道
	if(mkfifo(FIFONAME, 0664) < 0)
	{
		//printf("errno = %d\n", errno);
		if(errno != EEXIST)
		{
			perror("fail to mkfifo");
			exit(1);
		}
	}

	//对有名管道进行操作
	//使用文件io对有名管道进行操作
	
	//使用open函数打开文件获取文件描述符
	int fd;
	if((fd = open(FIFONAME, O_RDWR)) < 0)
	{
		perror("fail to open");
		exit(1);
	}

	//向有名管道写入数据
	write(fd, "hello world", 11);

	char s[32] = {};
	read(fd, s, 32);
	printf("s = %s\n", s);
	
	read(fd, s, 32);
	printf("s = %s\n", s);

	return 0;
}

(3)有名管道的读写规律
如果读写端都存在,如果只读不写,如果管道内有数据,会正常读取,但是如果没有数据,就会一直阻塞。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define FIFONAME "myfifo"

int main(int argc, const char *argv[])
{
	if(mkfifo(FIFONAME, 0664) < 0)
	{
		//printf("errno = %d\n", errno);
		if(errno != EEXIST)
		{
			perror("fail to mkfifo");
			exit(1);
		}
	}

	int fd;
	if((fd = open(FIFONAME, O_RDWR)) < 0)
	{
		perror("fail to open");
		exit(1);
	}

	//如果读写端都存在,如果只读不写,如果管道内有数据,会正常读取,但是如果没有数据,就会一直阻塞

	write(fd, "hello world", 11);

	char s[32] = {};
	read(fd, s, 32);
	printf("s = %s\n", s);
	
	read(fd, s, 32);
	printf("s = %s\n", s);

	return 0;
}

如果读写端都存在,如果只写不读,当管道写满是,就会阻塞,默认有名管道64k字节。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define FIFONAME "myfifo"

int main(int argc, const char *argv[])
{
	if(mkfifo(FIFONAME, 0664) < 0)
	{
		//printf("errno = %d\n", errno);
		if(errno != EEXIST)
		{
			perror("fail to mkfifo");
			exit(1);
		}
	}

	int fd;
	if((fd = open(FIFONAME, O_RDWR)) < 0)
	{
		perror("fail to open");
		exit(1);
	}

	//如果读写端都存在,如果只写不读,当管道写满是,就会阻塞,默认有名管道64k字节

	char s[1024] = {};
	int n = 0;

	while(1)
	{
		write(fd, s, 1024);
		n++;
		printf("n = %d\n", n);
	}

	return 0;
}

如果在一个进程里面,只有读端没有写端,则会在open函数的位置阻塞

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define FIFONAME "myfifo"

int main(int argc, const char *argv[])
{
	if(mkfifo(FIFONAME, 0664) < 0)
	{
		//printf("errno = %d\n", errno);
		if(errno != EEXIST)
		{
			perror("fail to mkfifo");
			exit(1);
		}
	}

	//如果在一个进程里面,只有读端没有写端,则会在open函数的位置阻塞

	int fd;
	if((fd = open(FIFONAME, O_RDONLY)) < 0)
	{
		perror("fail to open");
		exit(1);
	}

	printf("******************************\n");

	char s[32] = {};
	read(fd, s, 32);
	printf("s = %s\n", s);
	
	read(fd, s, 32);
	printf("s = %s\n", s);

	return 0;
}

如果在一个进程里面,只有写端没有读端,则会在open函数的位置阻塞

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define FIFONAME "myfifo"

int main(int argc, const char *argv[])
{
	if(mkfifo(FIFONAME, 0664) < 0)
	{
		//printf("errno = %d\n", errno);
		if(errno != EEXIST)
		{
			perror("fail to mkfifo");
			exit(1);
		}
	}

	int fd;
	if((fd = open(FIFONAME, O_WRONLY)) < 0)
	{
		perror("fail to open");
		exit(1);
	}

	printf("*****************************************\n");

	char s[1024] = {};
	int n = 0;

	while(1)
	{
		write(fd, s, 1024);
		n++;
		printf("n = %d\n", n);
	}

	return 0;
}

如果一个进程只负责读,另一个进程只负责写,如果关闭写端,则读端会返回0,如果关闭读端,写端只要运行到write函数,则机会产生SIGPIPE信号,使得进程结束。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define FIFONAME "myfifo"
#define N 128

//当前进程向管道写入数据

int main(int argc, const char *argv[])
{
	if(mkfifo(FIFONAME, 0664) < 0)
	{
		if(errno != EEXIST)
		{
			perror("fail to open");
			exit(1);
		}
	}

	int fd;
	if((fd = open(FIFONAME, O_WRONLY)) < 0)
	{
		perror("fail to open");
		exit(1);
	}

	printf("*************************\n");

	char buf[N] = {};

	while(1)
	{
		fgets(buf, N, stdin);
		buf[strlen(buf) - 1] = '\0';

		write(fd, buf, N);

		if(strncmp(buf, "quit", 4) == 0)
		{
			exit(0);
		}
	}
	
	return 0;
}

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define FIFONAME "myfifo"
#define N 128

//当前进程从管道读取数据

int main(int argc, const char *argv[])
{
	if(mkfifo(FIFONAME, 0664) < 0)
	{
		if(errno != EEXIST)
		{
			perror("fail to open");
			exit(1);
		}
	}

	int fd;
	if((fd = open(FIFONAME, O_RDONLY)) < 0)
	{
		perror("fail to open");
		exit(1);
	}
	
	printf("*************************\n");

	char buf[N] = {};

	while(1)
	{
		read(fd, buf, N);

		if(strncmp(buf, "quit", 4) == 0)
		{
			exit(0);
		}
		else 
		{
			printf("buf = %s\n", buf);
		}
	}
	
	return 0;
}

【5】信号通信
(1)性质
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式

信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利
用它来通知用户空间进程发生了哪些系统事件。

如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程
恢复执行再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被
延迟,直到其阻塞被取消时才被传递给进程 。

信号是由当前系统已经定义好的一些标识,每一个标识都会在特定的场合产生,并且都会对进程有一定的影响,当信号产生时,会对当前进程做出相应的操作。

(2)信号的默认处理方式
当信号产生时,对进程默认的操作方式
方式1:结束整个进程
方式2:忽略,当产生当前信号时,进程不做任何操作
方式3:停止一个进程
方式4:让停止的进程恢复执行

(3)用户对信号的处理方式
默认的方式(缺省)
忽略
用户自定义(捕捉)

注意:SIGKILL和SIGSTOP这个两个信号只能以默认的方式处理

【4】常用的信号
kill -l 查看系统中所有的信号
1 – SIGKILL 9
性质:当产生当前信号时,进程结束,不能忽略也不能自定义操作
默认方式:结束一个进程
2 – SIGSTOP 19
性质:当产生当前信号时,进程会停止,不能忽略也不能自定义操作
默认方式:停止一个进程
3 – SIGINT 2
性质:当键盘输入ctrl+c,产生当前信号
默认方式:结束一个进程
4 – SIGQUIT 3
性质:当键盘输入ctrl+\,产生当前信号
默认方式:结束一个进程
5 – SIGTSTP 20
性质:当键盘输入ctrl+z,产生当前信号
默认方式:停止一个进程
6 – SIGCONT 18
性质:可以使得一个停止的进程恢复执行
默认方式:停止的进程回复执行
7 – SIGPIPE 13
性质:管道在操作的时候,如果关闭读端,当执行到写操作时,就会产生当前信号
默认方式:结束一个进程
8 – SIGALRM 14
性质:当使用alarm函数设定的时间到达时,会产生当前信号
默认方式:结束一个进程
9 – SIGCHLD 17
性质:当创建子进程后,如果子进程的状态改变(退出)时,就会产生当前信号
默认方式:忽略
10 – SIGUSR1 10 、 SIGUSR2 12
性质:用户自定义的信号,有当前进程自己主动产生当前信号
默认方式:忽略

(5)信号处理
1 – signal( )

	#include 
	void (*signal(int sig, void (*func)(int)))(int);
			-->
			typedef void (*sighandler_t)(int);
			sighandler_t signal(int signum, sighandler_t handler);

功能:处理一个信号
参数
sig:指定的信号
func:信号的处理方式
SIG_DFL 以默认的方式处理
SIG_IGN 以忽略的方式处理
func:信号处理函数,用户自定义的方式处理
返回值
成功:当前信号默认的处理方式
失败:SIG_ERR

#include 
#include 
#include 

void handler(int sig)
{
	if(sig == SIGINT)
	{
		printf("SIGINT handler is running\n");
	}
	else if(sig == SIGQUIT)
	{
		printf("SIGQUIT handler is running\n");	
	}
	else 
	{
		printf("SIGTSTP handler is running\n");	
	}
}

int main(int argc, const char *argv[])
{
	//使用signal函数改变进程对信号的响应方式
	
	//以默认的方式处理
	//signal(SIGINT, SIG_DFL);
	//signal(SIGTSTP, SIG_DFL);
	
	//以忽略的方式处理
	//signal(SIGINT, SIG_IGN);
	//signal(SIGTSTP, SIG_IGN);
	
#if 0
	//SIGKILL和SIGSTOP这两个信号不能被忽略,也不能自定义处理
	if(signal(SIGKILL, SIG_IGN) == SIG_ERR)
	{
		perror("fail to signal");
		exit(1);
	}
#endif

	//以自定义方式处理
	signal(SIGINT, handler);
	signal(SIGQUIT, handler);
	signal(SIGTSTP, handler);

	while(1)
	{
		printf("hello world\n");
		sleep(1);
	}
	
	return 0;
}

#include 
#include 
#include 

void *ret;

void handler(int sig)
{
	printf("SIGINT handler is running\n");

	signal(SIGINT, ret);
}

int main(int argc, const char *argv[])
{
	ret = signal(SIGINT, handler);

	while(1)
	{
		printf("hello world\n");
		sleep(1);
	}
	
	return 0;
}

(6)进程间发生信号

#include 
int kill(pid_t pid, int sig);

功能:向一个进程发送信号
参数
pid:绝对接收信号的进程
>0 指定进程号等于当前参数的进程接收信号
0 将信号发送给发送者所在进程组里面所有的进程
-1 发送给所有的进程
<-1 发送给当前参数的绝对值的进程组内的所有的进程
sig:指定的信号
返回值
成功:0
失败:-1

#include 
#include 
#include 

void handler(int sig)
{
	printf("handler...\n");
}

int main(int argc, const char *argv[])
{
	pid_t pid;
	if((pid = fork()) < 0)
	{
		perror("fail to fork");
		exit(1);
	}
	else if(pid > 0) //父进程
	{
		signal(SIGINT, handler);

		while(1)
		{
			printf("This is parent process\n");
			sleep(1);
		}
	}
	else //子进程
	{
		//子进程给父进程发送信号

		printf("This is child process\n");
	
		sleep(5);

		kill(getppid(), SIGINT);
	}
	
	return 0;
}

#include 
int raise(int sig);

功能:给自己发送信号

raise(sig) <==> kill(getpid(), sig)

(7)alarm( )

#include 

unsigned int alarm(unsigned int seconds);

功能:定时器,当时间到达时会产生SIGALRM信号,使得进程结束
参数
seconds:设定的秒数
返回值
成功:上一个alarm函数剩余的时间,如果没有则返回0

#include 
#include 
#include 

void handler(int sig)
{
	printf("闹钟响了!!!\n");
}

int main(int argc, const char *argv[])
{
	signal(SIGALRM, handler);

	//alarm设定的时间,如果一个程序里面有多个alarm函数,则以最后一个时间为基准
	int n = 0;

	n = alarm(8);
	printf("n = %d\n", n);

	sleep(2);

	n = alarm(3);
	printf("n = %d\n", n);

	while(1)
	{
		printf("hello world\n");
		sleep(1);
	}
	
	return 0;
}

(8)pause()

#include 
int pause(void);

功能:阻塞等待一个信号的产生
参数

返回值
成功:具体的信号
失败:-1


#include 
#include 
#include 

void handler1(int sig)
{
	pause();
}

void handler2(int sig)
{

}

int main(int argc, const char *argv[])
{	
	signal(SIGINT, handler1);
	signal(SIGQUIT, handler2);

	while(1)
	{
		printf("hello world\n");
		sleep(1);
	}
	
	return 0;
}

【6】消息队列
(1)性质
消息队列是IPC对象的一种

消息队列由消息队列ID来唯一标识

消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。

消息队列可以按照类型来发送/接收消息

(2)函数
1 – msgget( )

	#include 
	#include 
	#include 
	int msgget(key_t key, int msgflg);

功能:创建或者打开一个消息队列
参数
key:键值,相同的键值确定唯一的消息队列
键值的设置方式:
第一种:任意赋值
第二种:ftok函数获取
msgflg:访问权限,
一般为 IPC_CREAT | IPC_EXCL | 0777
返回值
成功:消息队列id
失败:-1

2 – ftok( )

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

功能:获取键值,当前获取的键值是由文件信息和第二个参数一起决定
参数
pathname:路径名
proj_id:任意一个值,一般为0 - 127
返回值
成功:键值
失败:-1

3 – msgctl( )

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

功能:控制操作一个消息队列
参数
msqid:消息队列id
cmd:具体的操作
IPC_STAT 获取消息队列的信息
IPC_SET 设置消息队列的信息
IPC_RMID 删除一个消息队列
buf:消息队列信息结构体
返回值
成功:0
失败:-1

#include 
#include 
#include 
#include 
#include 

int main(int argc, const char *argv[])
{
	key_t key;
	int msgid;

	//获取键值
	if((key = ftok(".", 100)) < 0)
	{
		perror("fail to ftok");
		exit(1);
	}
	
	//printf("key = %#x\n", key);

	//打开或者创建一个消息队列
	if((msgid = msgget(key, IPC_CREAT | 0777)) < 0)
	{
		perror("fail to msgget");
		exit(1);
	}
	
	//printf("msgid = %d\n", msgid);
	
	system("ipcs -q");

#if 0
	//删除一个消息队列
	if(msgctl(msgid, IPC_RMID, NULL) < 0)
	{
		perror("fail to msgctl");
		exit(1);
	}

	system("ipcs -q");
#endif

	return 0;
}

4 – msgsnd( )

	#include 
	int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

功能:给消息队列发送数据
参数
msqid:消息队列id
msgp:保存要发送的数据结构体(自己定义)
struct mymsg {
long mtype; /* Message type. / 消息类型,必须执行
char mtext[1]; /
Message text. */ 消息正文
}
msgsz:消息正文所占的空间大小
msgflg:标志位
0 阻塞
IPC_NOWAIT 非阻塞
返回值
成功:0
失败:-1

5 – msgrcv( )

	#include 
	ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

功能:从消息队列里面读取数据
参数
msqid:消息队列id
msgp:保存接收到的数据的结构体(自己定义)
struct mymsg {
long mtype; /* Message type. / 消息类型,必须执行
char mtext[1]; /
Message text. */ 消息正文
}
msgsz:消息正文所占的空间大小
msgtyp:根据设置的消息的类型接收
0 按照写入消息的顺序获取第一个消息
>0 获取第一个消息类型为当前参数的信息
<0 获取当前参数的绝对值以内消息类型最小的消息
msgflg:标志位
0 阻塞
IPC_NOWAIT 非阻塞
返回值
成功:接收的字节数
失败:-1

#include 
#include 
#include 
#include 
#include 

#define N 128

typedef struct{
	long type;
	char text[N];
}MSG;

#define TEXT_SIZE (sizeof(MSG) - sizeof(long))

int main(int argc, const char *argv[])
{
	key_t key;
	int msgid;

	//获取键值
	if((key = ftok(".", 100)) < 0)
	{
		perror("fail to ftok");
		exit(1);
	}
	
	//打开或者创建一个消息队列
	if((msgid = msgget(key, IPC_CREAT | 0777)) < 0)
	{
		perror("fail to msgget");
		exit(1);
	}
	
	//向消息队列发送消息
	MSG msg1 = {3, "3: hello world"};
	MSG msg2 = {1, "1: hello kitty"};
	MSG msg3 = {2, "2: nihai beijing"};
	MSG msg4 = {5, "5: welcome to hqyj"};
	MSG msg5 = {4, "4: bye bye"};

	msgsnd(msgid, &msg1, TEXT_SIZE, 0);
	msgsnd(msgid, &msg2, TEXT_SIZE, 0);
	msgsnd(msgid, &msg3, TEXT_SIZE, 0);
	msgsnd(msgid, &msg4, TEXT_SIZE, 0);
	msgsnd(msgid, &msg5, TEXT_SIZE, 0);

	system("ipcs -q");

#if 0
	//删除一个消息队列
	if(msgctl(msgid, IPC_RMID, NULL) < 0)
	{
		perror("fail to msgctl");
		exit(1);
	}

	system("ipcs -q");
#endif

	return 0;
}

#include 
#include 
#include 
#include 
#include 

#define N 128

typedef struct{
	long type;
	char text[N];
}MSG;

#define TEXT_SIZE (sizeof(MSG) - sizeof(long))

int main(int argc, const char *argv[])
{
	key_t key;
	int msgid;

	//获取键值
	if((key = ftok(".", 100)) < 0)
	{
		perror("fail to ftok");
		exit(1);
	}
	
	//打开或者创建一个消息队列
	if((msgid = msgget(key, IPC_CREAT | 0777)) < 0)
	{
		perror("fail to msgget");
		exit(1);
	}
	
	//从消息队列接收消息
	//如果消息队列里面没有相应的数据,则会阻塞
	MSG msg;
	msgrcv(msgid, &msg, TEXT_SIZE, -2, 0);
	printf("--> %s\n", msg.text);

	system("ipcs -q");

#if 0
	//删除一个消息队列
	if(msgctl(msgid, IPC_RMID, NULL) < 0)
	{
		perror("fail to msgctl");
		exit(1);
	}

	system("ipcs -q");
#endif

	return 0;
}

【7】共享内存
(1)性质
共享内存是一种最为高效的进程间通信方式,进程可以直接读写物理内存,而不需要任何数据的拷贝。

为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间。

进程就可以直接读写这一内存区而不需要进行数据的拷贝,从而大大提高的效率。

由于多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等

(2)函数
1 – shmget( )

	#include 
	#include 
	int shmget(key_t key, size_t size, int shmflg);

功能:创建或者打开一个共享内存
参数
key:键值
size:共享内存的大小
shmflg:访问权限
一般为IPC_CREAT | IPC_EXCL | 0777
返回值
成功:共享内存的id
失败:-1

2 – shmctl( )

	#include 
	#include 
	int shmctl(int shmid, int cmd, struct shmid_ds *buf);

功能:控制操作一个共享内存
参数
msqid:共享内存列id
cmd:具体的操作
IPC_STAT 获取共享内存的信息
IPC_SET 设置共享内存的信息
IPC_RMID 删除一个共享内存
buf:共享内存信息结构体
返回值
成功:0
失败:-1

#include 
#include 
#include 
#include 
#include 

int main(int argc, const char *argv[])
{
	key_t key;
	int shmid;

	//获取键值
	if((key = ftok(".", 100)) < 0)
	{
		perror("fail to ftok");
		exit(1);
	}
	
	//打开或者创建一个共享内存
	if((shmid = shmget(key, 500, IPC_CREAT | 0777)) < 0)
	{
		perror("fail to shmget");
		exit(1);
	}

	system("ipcs -m");

#if 0
	//删除一个共享内存
	if(shmctl(shmid, IPC_RMID, NULL) < 0)
	{
		perror("fail to shmctl");
		exit(1);
	}
	
	system("ipcs -m");
#endif
	return 0;
}

3 – shmat( )

	#include 
	void *shmat(int shmid, const void *shmaddr, int shmflg);

功能:映射共享内存
参数
shmid:共享内存id
shmaddr:获取地址的方式
NULL:系统随机分配
shmflg:权限
0 可读可写
SHM_RDONLY 只读
返回值
成功:获取映射的首地址
失败:-1

4 – shmdt( )

	#include 
	int shmdt(const void *shmaddr);

功能:解除共享内存的映射
参数
shmaddr:shmat的返回值
返回值
成功:0
失败:-1

#include 
#include 
#include 
#include 
#include 
#include 

typedef struct{
	int a;
	int b;
	char c;
}MSG;

int main(int argc, const char *argv[])
{
	key_t key;
	int shmid;

	//获取键值
	if((key = ftok(".", 100)) < 0)
	{
		perror("fail to ftok");
		exit(1);
	}
	
	//打开或者创建一个共享内存
	if((shmid = shmget(key, 500, IPC_CREAT | 0777)) < 0)
	{
		perror("fail to shmget");
		exit(1);
	}

	//映射共享内存
	//char *s;
	//MSG *s;
	double *s;
	s = shmat(shmid, NULL, 0);

	//strcpy(s, "hello wrold");
	
	//s->a = 100;
	//s->b = 200;
	//s->c = 'w';

	while(1)
	{
		*s = drand48();
		printf("%lf\n", *s);
	}

	//解除映射
	shmdt(s);

	system("ipcs -m");

#if 0
	//删除一个共享内存
	if(shmctl(shmid, IPC_RMID, NULL) < 0)
	{
		perror("fail to shmctl");
		exit(1);
	}
	
	system("ipcs -m");
#endif
	return 0;
}

#include 
#include 
#include 
#include 
#include 
#include 

typedef struct{
	int a;
	int b;
	char c;
}MSG;

int main(int argc, const char *argv[])
{
	key_t key;
	int shmid;

	//获取键值
	if((key = ftok(".", 100)) < 0)
	{
		perror("fail to ftok");
		exit(1);
	}
	
	//打开或者创建一个共享内存
	if((shmid = shmget(key, 500, IPC_CREAT | 0777)) < 0)
	{
		perror("fail to shmget");
		exit(1);
	}

	//映射共享内存
	//char *s;
	//MSG *s;
	double *s;
	s = shmat(shmid, NULL, 0);

	//printf("s = %s\n", s);

	//printf("%d -- %d -- %c\n", s->a, s->b, s->c);

	while(1)
	{
		printf("%lf\n", *s);
		sleep(3); 
	}

	//解除映射
	shmdt(s);

	system("ipcs -m");

#if 0
	//删除一个共享内存
	if(shmctl(shmid, IPC_RMID, NULL) < 0)
	{
		perror("fail to shmctl");
		exit(1);
	}
	
	system("ipcs -m");
#endif
	return 0;
}

【8】信号灯集
(1)性质
信号灯(semaphore),也叫信号量。它是不同进程间或一个给定进程
内部不同线程间同步的机制

(2)本质
PV操作,P为减操作,V为加操作,当信号灯的值为0时,P操作会阻塞

(3)函数
1 – semget( )

	#include 
	#include 
	#include 
	int semget(key_t key, int nsems, int semflg);

功能:创建或者打开一个信号灯集
参数
key:键值
nsems:信号灯的个数,至少为1
semflg:权限
一般为IPC_CREAT | IPC_EXCL | 0777
返回值
成功:信号灯集的id
失败:-1

2 – semctl( )

	#include 
	#include 
	#include 
	int semctl(int semid, int semnum, int cmd, ...);

功能:操作一个信号灯
参数
semid:信号灯集的id
semnum:要操作的信号灯的编号,从0开始
cmd:具体的操作
IPC_RMID 删除一个信号灯集
SETVAL 设置信号灯的初始值(自己定义共用体)
union semun{
int val;
}
返回值
成功:0
失败:-1

#include 
#include 
#include 
#include 
#include 

int main(int argc, const char *argv[])
{
	key_t key;
	int semid;

	//获取键值
	if((key = ftok(".", 100)) < 0)
	{
		perror("fail to ftok");
		exit(1);
	}

	//创建或者打开一个信号灯集
	if((semid = semget(key, 3, IPC_CREAT | 0777)) < 0)
	{
		perror("fail to semget");
		exit(1);
	}

	system("ipcs -s");

#if 0
	//删除信号灯集
	if(semctl(semid, 1, IPC_RMID) < 0)
	{
		perror("fail to semctl");
		exit(1);
	}

	system("ipcs -s");
#endif	

	return 0;
}

3 – semop( )

	#include 
	#include 
	#include 
	int semop(int semid, struct sembuf *sops, unsigned nsops);

功能:执行PV操作
参数
semid:信号灯集的id
sops:要操作的信号灯的结构体数组
struct sembuf{
unsigned short sem_num; 信号灯的编号,从0开始
short sem_op; 具体操作
>0 V操作
<0 P操作
short sem_flg; 标志位
0 阻塞
IPC_NOWAIT 非阻塞
}
nsops:要操作的信号灯的个数
返回值
成功:0
失败:-1

#include 
#include 
#include 
#include 
#include 
#include 
#include 

union semun{
	int val;
};

int main(int argc, const char *argv[])
{
	key_t key;

	//获取键值
	if((key = ftok(".", 100)) < 0)
	{
		perror("fail to ftok");
		exit(1);
	}
	
	//**************************共享内存******************************

	//打开或者创建一个共享内存
	int shmid;
	if((shmid = shmget(key, 500, IPC_CREAT | 0777)) < 0)
	{
		perror("fail to shmget");
		exit(1);
	}

	//映射共享内存
	char *s;
	s = shmat(shmid, NULL, 0);

	//**************************信号灯集******************************

	//创建或者打开一个信号灯集
	int semid;
	if((semid = semget(key, 1, IPC_CREAT | 0777)) < 0)
	{
		perror("fail to semget");
		exit(1);
	}

	//设置信号灯的初始值
	union semun myun;
	myun.val = 0;
	semctl(semid, 0, SETVAL, myun);

	//V操作
	struct sembuf buf = {0, 1, 0};

	while(1)
	{
		fgets(s, 128, stdin);
		s[strlen(s) - 1] = '\0';

		//先执行的进程执行完毕后执行V操作
		semop(semid, &buf, 1);

		if(strncmp(s, "quit", 4) == 0)
		{
			shmdt(s);
			exit(0);
		}
	}

	return 0;
}

#include 
#include 
#include 
#include 
#include 
#include 
#include 

union semun{
	int val;
};

int main(int argc, const char *argv[])
{
	key_t key;

	//获取键值
	if((key = ftok(".", 100)) < 0)
	{
		perror("fail to ftok");
		exit(1);
	}
	
	//**************************共享内存******************************

	//打开或者创建一个共享内存
	int shmid;
	if((shmid = shmget(key, 500, IPC_CREAT | 0777)) < 0)
	{
		perror("fail to shmget");
		exit(1);
	}

	//映射共享内存
	char *s;
	s = shmat(shmid, NULL, 0);

	//**************************信号灯集******************************
	
	//创建或者打开一个信号灯集
	int semid;
	if((semid = semget(key, 1, IPC_CREAT | 0777)) < 0)
	{
		perror("fail to semget");
		exit(1);
	}

	//设置信号灯的初始值
	union semun myun;
	myun.val = 0;
	semctl(semid, 0, SETVAL, myun);

	//P操作
	struct sembuf buf = {0, -1, 0};

	while(1)
	{
		//后执行的进程执行P操作
		semop(semid, &buf, 1);

		if(strncmp(s, "quit", 4) == 0)
		{
			shmdt(s);
			shmctl(shmid, IPC_RMID, NULL);
			semctl(semid, 0, IPC_RMID);
			exit(0);
		}
		else 
		{
			printf("--> %s\n", s);	
		}
	}

	return 0;
}

你可能感兴趣的:(Linux-I/O进程)