您的位置:首页 > 运维架构 > Linux

linux多线程 & IPC【12】System V 共享内存

2013-05-13 22:13 302 查看
    System V的IPC包括System V消息队列,System V信号量和System共享内存。特征是使用key_t来标志,一般是一个32位的整数,正式说法是一个不小于32位的整数。

    没有亲缘关系的进程之间需要约定好一个文件或目录名(反正目录也是文件),以及一个整数值,然后调用ftok()函数返回一个key_t。

    key_t类型在<sys/types.h>中定义。

    函数ftok原型为:

#include <sys/ipc.h>
/* Generates key for System V style IPC.  */
key_t ftok (const char *pathname, int proj_id);

    得到key_t类型的IPC键后,就可以调用shmget函数获得共享内存标志符了。这个标志符是一个int,一般进程间通信,总有一个是相当于服务器的,他来创建这个共享内存,其他进程默认假定该共享内存已经存在。

POSIX 共享内存的大小可以用ftruncate随时修改,但System V的共享内存在shmget调用之后就确定了无法修改。
http://blog.csdn.net/zhangzhenghe/article/details/6838019

1.创建

(1)在服务器端,需要进行创建:

key_t kt = ftok("/home/administrator", 0);

int id = shmget(kt, 1024, IPC_CREAT | 0666);

关键的地方:1024为字节数,IPC_CREAT表示不存在则创建,存在则直接用;IPC_CREAT|IPC_EXCL表示不存在则创建,存在则返回-1退出;0666表示权限,注意前面的0,表示八进制,切记!

(2)在客户端,需要创建:

key_t kt = ftok("/home/administrator", 0);

int id = shmget(kt, 0,0666);

由于共享内存区已经存在,所以第二个参数必须为0,第三个参数也跟creat不相干,指定权限位即可。

2.绑定

key_t kt = ftok("/home/administrator", 0);
int id = shmget(kt, 0,0666);

p = shmat( id, NULL ,0);
p是一个指针,什么指针都行。

然后就可以进行读写了。

3.解除绑定

key_t kt = ftok("/home/administrator", 0);

int id = shmget(kt, 0,0666);

char *p=shmat(id,NULL,0);
shmdt(p);

4.删除

key_t kt = ftok("/home/administrator", 0);
int id = shmget(kt, 0,0666);

shmctl(id,IPC_RMID,NULL);

5.获得大小

key_t kt = ftok("/home/administrator", 0);
int id = shmget(kt, 0,0666);

struct shmid_ds buff;

shmctl(id,IPC_STAT,&buff);

printf("ize: %d\n", buff.shm_segsz);

System V 共享内存实现之前的系统日志:

包括4个文件:

mylog.h  sys_v_cle.c  sys_v_ser.c  mylog.c

头文件:

#ifndef _MYLOG_H_
#define _MYLOG_H_

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <sys/shm.h>

#define N_MSG 4			// message的个数
#define LEN_MSG 256		// 每个message的长度
//#define FILE_MODE S_IRUSR | S_IWUSR         //rwxrwxrwx
#define FILE_MODE 00600		//rwxrwxrwx
#define px_ipc_name(x) (x)	//空的宏定义,暂时没有用

struct msg_ctl_str {
sem_t mutex;
sem_t empty;
sem_t full;
int nput;			// 下一个可以由生产者放置log信息的index序号
int nread;			// 已经处理完毕的消息序号,初始为-1,这样,下一个要处理的就是0
char msgdata[N_MSG][LEN_MSG];
} msg_ctl_str;

void my_syslog (const char *msg, char *argv[]);	//给客户端的函数

#endif


sys_v_ser.c。服务器采用独占方式,如果已经存在,则将ftok的第二个参数+1,再次尝试。

#include "mylog.h"

int main(int argc, char *argv[])
{

key_t kt;
int id;
int n=0;
struct msg_ctl_str *p;
int i;

// 创建共享内存区
kt = ftok(argv[1], atoi(argv[2])+n );
id = shmget(kt, sizeof(struct msg_ctl_str), IPC_CREAT | IPC_EXCL|0666);
while (id == -1)
{
++n;
kt =ftok(argv[1], atoi(argv[2])+n );
id = shmget(kt, sizeof(struct msg_ctl_str), IPC_CREAT | IPC_EXCL|0666);
}
printf("server start! key_t: %s, %d\n", argv[1], atoi(argv[2])+n );

// 映射
p = shmat( id, NULL ,0);
if (p==-1)
{
printf("shmat fail!\n");
exit(2);
}

// 初始化
sem_init(&(p->mutex),1,1);
sem_init(&(p->empty),1,N_MSG);
sem_init(&(p->full),1,0);
p->nput=0;
p->nread=-1;
for(i=0;i<N_MSG;i++)
p->msgdata[i][0]='\0';

while(1)
{
sem_wait(& (p->full));
sem_wait(& (p->mutex));

p->nread++;
p->nread %= N_MSG;
printf("处理第 %d 条: %s\n", p->nread, p->msgdata[p->nread]);

sem_post(& (p->mutex));
sem_post(& (p->empty));
}

// 不会运行
shmdt(p); // 断开
shmctl(id, IPC_RMID, NULL); //

}


sys_v_cle.c:

#include "mylog.h"

int main(int argc, char *argv[])
{
char *s="I am client";
my_syslog(s, argv);
return 0;
}
sys_v_cle.c调用了mylog.c中的函数。mylog.c:

#include "mylog.h"

void
my_syslog (const char *msg, char *argv[])
{
key_t kt;
int n=0;
int id;
struct msg_ctl_str *p;
// printf("%s %s\n", argv[1], argv[2]);
//打开
kt = ftok( argv[1], atoi(argv[2]+n) );
id = shmget(kt,0,0666);
while (id == -1)
{
++n;
kt = ftok( argv[1], atoi(argv[2]+n) );
id=shmget(kt,0,0666);
}
printf("client key_t: %s, %d--", argv[1], atoi(argv[2])+n );

//映射
p = (struct msg_ctl_str *)shmat(id,NULL,0);
if (p==-1)
{
printf("shmat fail!\n");
exit(2);
}

//干正事
sem_wait (&(p->empty));	//等待有消息的到来,没有消息的话一直阻塞在这里
sem_wait (&(p->mutex));	//取得互斥

// 处理!就是打印到标准输出而已
strncpy (p->msgdata[p->nput], msg, 255);
printf ("process %d add a msg to log (%d, %s)\n", getpid (), p->nput,
p->msgdata[p->nput]);
p->msgdata[p->nput][255] = '\0';
p->nput++;
p->nput = p->nput % N_MSG;
//

sem_post (&(p->mutex));
sem_post (&(p->full));	//empty的位置增加了一个
}


开启两个终端,第一个终端启动服务器:

administrator@ubuntu:~/test/sys_v$ ./ss /home/administrator 2 &     <--启动
[4] 5936
administrator@ubuntu:~/test/sys_v$ server start! key_t: /home/administrator, 4     <--打出来的信息。客户端的参数应使用4

administrator@ubuntu:~/test/sys_v$ jobs
[3]-  运行中               ./log_server &  (工作目录:~/test/log_test)
[4]+  运行中               ./ss /home/administrator 2 &


第二个终端启动客户端:

administrator@ubuntu:~/test/sys_v$ ./cc /home/administrator 4
client key_t: /home/administrator, 4--process 5937 add a msg to log (0, I am client)
administrator@ubuntu:~/test/sys_v$ ./cc /home/administrator 4
client key_t: /home/administrator, 4--process 5938 add a msg to log (1, I am client)
administrator@ubuntu:~/test/sys_v$ ./cc /home/administrator 4
client key_t: /home/administrator, 4--process 5939 add a msg to log (2, I am client)
administrator@ubuntu:~/test/sys_v$ ./cc /home/administrator 4
client key_t: /home/administrator, 4--process 5940 add a msg to log (3, I am client)
administrator@ubuntu:~/test/sys_v$ ./cc /home/administrator 4
client key_t: /home/administrator, 4--process 5941 add a msg to log (0, I am client)
administrator@ubuntu:~/test/sys_v$ ./cc /home/administrator 4
client key_t: /home/administrator, 4--process 5942 add a msg to log (1, I am client)
administrator@ubuntu:~/test/sys_v$ ./cc /home/administrator 4
client key_t: /home/administrator, 4--process 5943 add a msg to log (2, I am client)
administrator@ubuntu:~/test/sys_v$
客户端一共运行了7次,写了7条消息。第一个终端中的服务器收到消息,就进行处理,把消息相关的信息打出来:

administrator@ubuntu:~/test/sys_v$ 处理第 0 条: I am client
处理第 1 条: I am client
处理第 2 条: I am client
处理第 3 条: I am client
处理第 0 条: I am client
处理第 1 条: I am client
处理第 2 条: I am client
共7条。此后服务器再次进行等待。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息