第15章 进程间通行 15.6 XSI IPC 15.7 消息队列

15.6 XSI IPC

(1)3种称作XSI IPC的IPC是:

    1)消息队列

    2)信号量

    3)共享存储器


(2)标识符和键

    1)标识符:是一个非负整数,用于引用IPC结构。是IPC对象的内部名。

    2)键:IPC对象的外部名。可使多个合作进程能够在同一IPC对象上汇聚。


(3)IPC_PRIVATE键:

    用于创建一个新的IPC结构。不能指定此键来引用一个现有的IPC结构。


(4)ftok函数:

    由一个路径名和项目ID产生一个键。


(5)ipc_perm结构体

    规定了ipc结构的权限和所有者。


(6)结构限制:

    XSI IPC结构都有内置限制,可通过重新配置内核来改变。

    1)sysctl命令:观察、修改内核配置参数。

    2)ipcs -l:显示ipc相关限制。


(7)IPC结构和管道、FIFO的区别:

    IPC结构在系统范围内起作用,且没有引用计数。


(8)IPC结构在文件系统中没有名字;IPC不使用文件描述符。


15.7 消息队列

(1)新的应用程序中不要使用消息队列。它们有缺点。(15.6.4)


(2)客户进程和服务器进程之间的双向数据流,可以使用消息队列或全双工管道。


15.8 信号量

(1)多个进程间共享一个资源,可以使用信号量、记录锁和互斥量中的一种来协调。


(2)共享存储中的互斥量速度最快,但作者依然喜欢使用记录锁的两个原因:

    1)<459>

    2)<459>


15.9 共享存储

(1)共享存储的作用:

    允许两个或多个进程共享一个给定的存储区。

    它是最快的一种IPC,因为数据不需要在客户进程和服务器进程之间复制。


(2)使用共享存储时要掌握的唯一诀窍:

    同步:写完再读取。(信号量、记录锁和互斥量


(3)

shmget函数:获得一个共享存储标识符。

shmctl函数:对共享存储段执行多种操作。

shmat函数:将共享存储段连接到进程的地址空间。

shmde函数:使调用进程分离共享存储段。


(4)实例:打印特定系统中存放各种类型的数据的位置信息。

#include "apue.h"
#include <sys/shm.h>

#define	ARRAY_SIZE	40000
#define	MALLOC_SIZE	100000
#define	SHM_SIZE	100000
#define	SHM_MODE	0600	/* user read/write */

char	array[ARRAY_SIZE];	/* uninitialized data = bss */

int
main(void)
{
	int		shmid;
	char	*ptr, *shmptr;

	printf("array[] from %p to %p\n", (void *)&array[0],
	  (void *)&array[ARRAY_SIZE]);//bss
	printf("stack around %p\n", (void *)&shmid);

	if ((ptr = malloc(MALLOC_SIZE)) == NULL)//堆
		err_sys("malloc error");
	printf("malloced from %p to %p\n", (void *)ptr,
	  (void *)ptr+MALLOC_SIZE);

	if ((shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE)) < 0)
		err_sys("shmget error");
	if ((shmptr = shmat(shmid, 0, 0)) == (void *)-1)
		err_sys("shmat error");
	printf("shared memory attached from %p to %p\n", (void *)shmptr,
	  (void *)shmptr+SHM_SIZE);

	if (shmctl(shmid, IPC_RMID, 0) < 0)
		err_sys("shmctl error");

	exit(0);
}


(5)C程序的存储空间布局<163>

1)正文段:CPU执行的机器指令部分

2)初始化数据段:(数据段),包含了程序中需明确地赋初值的变量。

3)未初始化数据段:(bss段),程序开始执行之前,内核将此段中的数据初始化为0或空指针。(未初始化的全局变量)

4)栈:保存自动变量以及每次函数调用时所需保存的信息。

5)堆:进行动态存储分配。


(6)实例:相关的进程的其它实现共享存储的技术。

1)/dev/zero的存储映射                  2)线程

#include "apue.h"
#include <fcntl.h>
#include <sys/mman.h>

#define	NLOOPS		1000
#define	SIZE		sizeof(long)	/* size of shared memory area */

static int
update(long *ptr)
{
	return((*ptr)++);	/* return value before increment */
}

int
main(void)
{
	int		fd, i, counter;
	pid_t	pid;
	void	*area;

	if ((fd = open("/dev/zero", O_RDWR)) < 0)
		err_sys("open error");
	if ((area = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
	  fd, 0)) == MAP_FAILED)
		err_sys("mmap error");
	close(fd);		/* can close /dev/zero now that it's mapped */

	TELL_WAIT();

	if ((pid = fork()) < 0) {
		err_sys("fork error");
	} else if (pid > 0) {			/* parent */
		for (i = 0; i < NLOOPS; i += 2) {
			if ((counter = update((long *)area)) != i)
				err_quit("parent: expected %d, got %d", i, counter);

			TELL_CHILD(pid);
			WAIT_CHILD();
		}
	} else {						/* child */
		for (i = 1; i < NLOOPS + 1; i += 2) {
			WAIT_PARENT();

			if ((counter = update((long *)area)) != i)
				err_quit("child: expected %d, got %d", i, counter);

			TELL_PARENT(getppid());
		}
	}

	exit(0);
}

1)open以读写方式打开/dev/zero设备

2)调用mmap函数映射存储区fd----->void *。(优点:调用此函数创建映射之前,无需存在一个实际的文件)

3)close关闭fd

4)父子进程使用TELL_CHILD(),TELL_PARENT(),WAIT_CHILD(),WAIT_PARENT()同步(这些函数通过SIGUSR1和SIGUSR2信号进行同步)。

#include "apue.h"

static volatile sig_atomic_t sigflag; /* set nonzero by sig handler */
static sigset_t newmask, oldmask, zeromask;

static void
sig_usr(int signo)	/* one signal handler for SIGUSR1 and SIGUSR2 */
{
	sigflag = 1;
}

void
TELL_WAIT(void)
{
	if (signal(SIGUSR1, sig_usr) == SIG_ERR)
		err_sys("signal(SIGUSR1) error");
	if (signal(SIGUSR2, sig_usr) == SIG_ERR)
		err_sys("signal(SIGUSR2) error");
	sigemptyset(&zeromask);
	sigemptyset(&newmask);
	sigaddset(&newmask, SIGUSR1);
	sigaddset(&newmask, SIGUSR2);

	/* Block SIGUSR1 and SIGUSR2, and save current signal mask */
	if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
		err_sys("SIG_BLOCK error");
}

void
TELL_PARENT(pid_t pid)
{
	kill(pid, SIGUSR2);		/* tell parent we're done */
}

void
WAIT_PARENT(void)
{
	while (sigflag == 0)
		sigsuspend(&zeromask);	/* and wait for parent */
	sigflag = 0;

	/* Reset signal mask to original value */
	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
		err_sys("SIG_SETMASK error");
}

void
TELL_CHILD(pid_t pid)
{
	kill(pid, SIGUSR1);			/* tell child we're done */
}

void
WAIT_CHILD(void)
{
	while (sigflag == 0)
		sigsuspend(&zeromask);	/* and wait for child */
	sigflag = 0;

	/* Reset signal mask to original value */
	if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
		err_sys("SIG_SETMASK error");
}

1)

int sigsuspend(const sigset_t *mask);

    临时改变信号掩码并挂起,直到某信号到达。

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

    此处用于改回信号掩码。

kill函数:发送信号给进程。


(7)实例:匿名存储映射。

匿名:不通过一个文件描述符与一个路径名相结合,并且创建了一个可与后代进程共享的存储区。


(8)在两个无关进程之间使用共享存储段的方法:

1)XSI共享存储函数。

2)mmap将同一文件映射至它们的地址空间。


15.10 POSIX信号量

(1)POSIX信号量接口意在解决XSI信号量接口的几个缺陷。


(2)两种形式:

1)未命名信号量:只存在于内存中,并要求能使用信号量的进程必须可以访问内存。(同一进程的线程或已映射内存)

2)命名信号量:可以通过名字访问,可以被任何已知它们名字的进程中的线程使用。


(3)POSIX信号量的Linux实现将文件映射到了进程地址空间,并且没有使用系统调用来操作各自的信号量。

(性能明显好于XSI信号量)


15.12 小结

(1)建议:使用管道和FIFO;考虑全双工管道和记录锁以避免消息队列和信号量的使用。

转载于:https://my.oschina.net/u/2463708/blog/521386

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值