- 論壇徽章:
- 0
|
進(jìn)程通信之--共享內(nèi)存
共享內(nèi)存是最快的進(jìn)程通信的形式,因?yàn)橥ㄟ^(guò)它進(jìn)程間的通信可以不經(jīng)過(guò)內(nèi)核,而是在進(jìn)程的內(nèi)存空間中進(jìn)行。從而加快了訪問(wèn)內(nèi)存的速度。
共享內(nèi)存需要某種形式的同步;
共享內(nèi)存可以用于不同進(jìn)程間的信息交換。
1, 文件內(nèi)存映射
通過(guò)把文件映射到內(nèi)存區(qū),可以不用文件操作的I/O函數(shù)write,read等,而可以直接操作內(nèi)存。
(1)
/*
* incr1.c
* 說(shuō)明:該代碼達(dá)不到預(yù)期的目的
*/
#include "my_unpipc.h"
#define SEM_NAME "mysem1"
int count = 0;
int
main(int argc, char **argv)
{
int i, nloop;
sem_t *mutex;
if (argc != 2) {
fprintf(stderr, "usage: incr1 \n");
exit(0);
}
nloop = atoi(argv[1]);
/*
* create, initialize, and unlink semaphore
* mysem1 將會(huì)被創(chuàng)建在/dev/shm/sem.mysem1
*/
mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, FILE_MODE, 1);
if (mutex == SEM_FAILED) {
perror("sem_open()");
exit(0);
}
sem_unlink(SEM_NAME);
setbuf(stdout, NULL); /* stdout is unbuffered */
if (fork() == 0) { /* child */
for (i = 0; i nloop; i++) {
sem_wait(mutex);
printf("child: %d\n", count++);
sem_post(mutex);
}
exit(0);
}
/* parent */
for (i = 0; i nloop; i++) {
sem_wait(mutex);
printf("parent: %d\n", count++);
sem_post(mutex);
}
exit(0);
}
*運(yùn)行演示:
[nobody@linux shm]$ ./incr1 3
child: 0
child: 1
child: 2
parent: 0
parent: 1
parent: 2
...
*結(jié)果分析
該程序中父子進(jìn)程各自都有自己的count的拷貝,所以雖然各自可以互斥的對(duì)count進(jìn)行加1操作,但加的都是各自的count變量。
(2)
/*
* incr2.c
* 說(shuō)明:父子程序共享文件映射的內(nèi)存區(qū)塊。
* 所以父子進(jìn)程所作的改動(dòng)都會(huì)在文件映射的內(nèi)存中反應(yīng)出來(lái)。
*/
#include "my_unpipc.h"
#define SEM_NAME "mysem"
int
main(int argc, char **argv)
{
int fd, i, nloop, zero = 0;
int *ptr;
sem_t *mutex;
if (argc != 3) {
printf("usage: incr2 \n");
exit(0);
}
nloop = atoi(argv[2]);
/* open file, initialize to 0, map into memory */
fd = open(argv[1], O_RDWR | O_CREAT, FILE_MODE);
write(fd, &zero, sizeof(int));
/*注意mmap函數(shù)的參數(shù) MAP_SHARED, 具體參見(jiàn)man mmap */
ptr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
/* create, initialize, and unlink semaphore for mutex */
mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, FILE_MODE, 1);
if (mutex == SEM_FAILED) {
perror("sem_open");
exit(0);
}
/* 注意,退出時(shí)清理信號(hào)量文件 */
sem_unlink(SEM_NAME);
setbuf(stdout, NULL); /* stdout is unbuffered */
if (fork() == 0) { /* child */
for (i = 0; i nloop; i++) {
sem_wait(mutex);
printf("child: %d\n", (*ptr)++);
sem_post(mutex);
}
exit(0);
}
/* parent */
for (i = 0; i nloop; i++) {
sem_wait(mutex);
printf("parent: %d\n", (*ptr)++);
sem_post(mutex);
}
exit(0);
}
運(yùn)行效果:
[nobody@linux shm]$ ./incr2 mysem 3
child: 0
child: 1
child: 2
parent: 3
parent: 4
parent: 5
...
運(yùn)行結(jié)果分析:
由于父進(jìn)程打開(kāi)了文件,fork后,他們都各自有了該文件的描述符,文件被映射到內(nèi)存后,他們各自都有指針變量ptr,但該指針變量指向同一塊內(nèi)存。
(3) 把共享的變量放到一個(gè)結(jié)構(gòu)中
若把信號(hào)量變量放到進(jìn)程的共享內(nèi)存區(qū),那么多個(gè)進(jìn)程都可以看到它。這樣就可以使用sem_init而不使用sem_open在共享內(nèi)存區(qū)創(chuàng)建信號(hào)量,由于該信號(hào)量是共有的,而不是各自復(fù)制的,所以能夠達(dá)到互斥的目的。
#include "my_unpipc.h"
struct shared {
sem_t mutex; /* the mutex: a Posix memory-based semaphore */
int count; /* and the counter */
} shared;
int
main(int argc, char **argv)
{
int fd, i, nloop;
struct shared *ptr;
if (argc != 3) {
printf("usage: incr3 \n");
exit(0);
}
nloop = atoi(argv[2]);
/*
* 這里也可以用匿名的mmap,可以避免創(chuàng)建文件,還要?jiǎng)h除文件的操作。
* (1)
* ptr = mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
* 或
* (2)
* fd = open("/dev/zero", O_RDWR)
* ptr = mman(NULL, sizeof(int), PROT_READ | PROT_WRITE,
* MAP_SHARED, fd, 0)
* 然后用 (*ptr)++ ;
*/
/* open file, initialize to 0, map into memory */
fd = open(argv[1], O_RDWR | O_CREAT, FILE_MODE);
write(fd, &shared, sizeof(struct shared));
ptr = mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
close(fd);
/* initialize semaphore that is shared between processes */
sem_init(&ptr->mutex, 1, 1);
setbuf(stdout, NULL); /* stdout is unbuffered */
if (fork() == 0) { /* child */
for (i = 0; i nloop; i++) {
sem_wait(&ptr->mutex);
printf("child: %d\n", ptr->count++);
sem_post(&ptr->mutex);
}
exit(0);
}
/* parent */
for (i = 0; i nloop; i++) {
sem_wait(&ptr->mutex);
printf("parent: %d\n", ptr->count++);
sem_post(&ptr->mutex);
}
exit(0);
}
2, Posix 共享內(nèi)存
Posix共享內(nèi)存的作用和前面的文件的映射來(lái)共享內(nèi)存的作用是一樣的,只是調(diào)用的函數(shù)不一樣,也不需要調(diào)用open來(lái)打開(kāi)文件。
Posix共享內(nèi)存的創(chuàng)建分為以下幾步:
1) fd = shm_open()
2) ptr = mmap(..., fd, ...)
3) ftruncate(fd, length);
4) close(fd);
注意:posix共享內(nèi)存的生命周期是隨內(nèi)核的。所以,創(chuàng)建完成后會(huì)在系統(tǒng)中一直存在。
2.1 posix共享內(nèi)存用于存放互斥變量來(lái)實(shí)現(xiàn)多個(gè)進(jìn)程間的互斥
/*
* pxshm_incr3.c
* 用posix共享內(nèi)存實(shí)現(xiàn)進(jìn)程間的互斥。
*/
#include "my_unpipc.h"
struct shared {
sem_t mutex; /* the mutex: a Posix memory-based semaphore */
int count; /* and the counter */
} shared;
int
main(int argc, char **argv)
{
int fd, i, nloop;
struct shared *ptr;
if (argc != 3) {
printf("usage: incr3 \n");
exit(0);
}
nloop = atoi(argv[2]);
/*
* 這是為了避免命名沖突的權(quán)益之計(jì)
* 如果這里不做這一步,記得在程序退出的時(shí)候刪除共享內(nèi)存變量。
*/
shm_unlink(argv[1]);
/* create shm, set its size, map it, close descriptor */
fd = shm_open(argv[1], O_RDWR | O_CREAT | O_EXCL, FILE_MODE);
if (fd == -1) {
perror("shm_open");
exit(0);
}
ptr = mmap(NULL, sizeof(struct shared), PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
exit(0);
}
ftruncate(fd, sizeof(struct shared));
close(fd);
/* initialize semaphore that is shared between processes */
sem_init(&ptr->mutex, 1, 1);
setbuf(stdout, NULL); /* stdout is unbuffered */
if (fork() == 0) { /* child */
for (i = 0; i nloop; i++) {
sem_wait(&ptr->mutex);
printf("child: %d\n", ptr->count++);
sem_post(&ptr->mutex);
}
exit(0);
}
/* parent */
for (i = 0; i nloop; i++) {
sem_wait(&ptr->mutex);
printf("parent: %d\n", ptr->count++);
sem_post(&ptr->mutex);
}
exit(0);
}
執(zhí)行的效果如下所示:
[hover@jsdhover pxshm]$ ./pxshm_incr3 hover 3
child: 0
child: 1
child: 2
parent: 3
parent: 4
parent: 5
2.2 用于多(clients)對(duì)一(server)間的進(jìn)程通信
由于posix共享內(nèi)存是文件形式,所以對(duì)于沒(méi)有親緣關(guān)系的進(jìn)程也可以訪問(wèn),這樣我們就可以實(shí)現(xiàn)多個(gè)進(jìn)程間的通信。
3, system v共享內(nèi)存
v共享內(nèi)存在現(xiàn)在的編程中使用得比較多,于posix共享內(nèi)存相比,
posix : shm_open() --> mmap()
system v : shmget() ---> shmat()
System V共享內(nèi)存的使用中不創(chuàng)建臨時(shí)文件,使用起來(lái)比較方便?捎糜诙噙M(jìn)程和多線程間的同步。 下面看一個(gè)例子:
#define TEXT_SIZE 2048
struct shared_use_at {
int written_by_you;
char some_txt[TEXT_SIZE];
}
/*
* Producer
*/
#include "my_unpipc.h"
#include "shm_com.h"
int
main(void)
{
int running = 1;
void *shared_mem = (void *)0;
struct shared_use_at *shared_stuff;
char buffer[BUFSIZ];
int shmid;
/* 這里用0666權(quán)限創(chuàng)建一個(gè)共享內(nèi)存區(qū) */
shmid = shmget((key_t)KEY_INT, sizeof(struct shared_use_at), 0666 | IPC_CREAT);
if (shmid == -1) {
perror("shmget");
exit(0);
}
/* 把共享內(nèi)存的首地址連接到調(diào)用進(jìn)程的地址空間 */
shared_mem = shmat(shmid, (void *)0, 0);
if (shared_mem == (void *)-1) {
perror("shmat");
exit(0);
}
printf("Memory attached at %X\n", (int)shared_mem);
shared_stuff = (struct shared_use_at *)shared_mem;
/*
* Begin to produce.
*/
while (running) {
while (shared_stuff->written_by_you == 1) { /* waiting */
sleep(1);
printf("Waitring for client...\n");
}
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
strncpy(shared_stuff->some_text, buffer, TEXT_SIZE);
shared_stuff->written_by_you = 1;
if (strncmp(buffer, "end", 3) == 0)
running = 0;
}
/*
* Retrieve shared memory.
*/
if (shmdt(shared_mem) == -1) {
perror("shmdt");
exit(0);
}
/*
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl");
exit(0);
}
*/
exit(0);
}
/*
* consumer:
*/
#include "my_unpipc.h"
#include "shm_com.h"
int
main(void)
{
int running = 1;
void *shared_mem = (void *)0;
struct shared_use_at *shared_stuff;
int shmid;
srand((unsigned int)getpid());
shmid = shmget((key_t)KEY_INT, sizeof(struct shared_use_at), 0666 | IPC_CREAT);
if (shmid == -1) {
perror("shmget");
exit(0);
}
shared_mem = shmat(shmid, (void *)0, 0);
if (shared_mem == (void *)-1) {
perror("shmat");
exit(0);
}
printf("Memory attached at %X\n", (int)shared_mem);
shared_stuff = (struct shared_use_at *)shared_mem;
shared_stuff->written_by_you = 0;
while (running) {
if (shared_stuff->written_by_you) {
printf("You wrote : %s\n", shared_stuff->some_text);
sleep(rand() % 4);
shared_stuff->written_by_you = 0;
if (strncmp(shared_stuff->some_text, "end", 3) == 0)
running = 0;
}
}
if (shmdt(shared_mem) == -1) {
perror("shmdt");
exit(0);
}
if (shmctl(shmid, IPC_RMID, 0) == -1) {
perror("shmctl");
exit(0);
}
exit(0);
}
4, 遇到的問(wèn)題
(1) sem_open() 找不到文件或路徑的問(wèn)題
這是由于在內(nèi)核中,創(chuàng)建信號(hào)量的默認(rèn)路徑是/dev/shm。當(dāng)你要?jiǎng)?chuàng)建一個(gè)信號(hào)量/tmp/mysem時(shí),實(shí)際上是創(chuàng)建了一個(gè)/dev/shm/sem.tmp/mysem,而這里由于/dev/shm/tmp目錄根本就不存在,所以會(huì)出錯(cuò)。
解決方法:
*直接寫信號(hào)量文件的名字,將會(huì)創(chuàng)建在/dev/shm中:sem_open("mysem", ...)
*
(...)
本文來(lái)自ChinaUnix博客,如果查看原文請(qǐng)點(diǎn):http://blog.chinaunix.net/u/28197/showart_2158261.html |
|