第15章 進程間通行 15.6 XSI IPC 15.7 消息隊列

15.6 XSI IPC服務器

(1)3種稱做XSI IPC的IPC是:app

    1)消息隊列
函數

    2)信號量
佈局

    3)共享存儲器性能


(2)標識符和鍵ui

    1)標識符:是一個非負整數,用於引用IPC結構。是IPC對象的內部名。
atom

    2)鍵:IPC對象的外部名。可以使多個合做進程可以在同一IPC對象上匯聚。
spa


(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;考慮全雙工管道和記錄鎖以免消息隊列和信號量的使用。

相關文章
相關標籤/搜索