您的位置:首页 > 其它

System V IPC对象 共享内存、消息队列、信号灯集(6.7)

2015-06-08 20:22 531 查看
System V IPC对象 共享内存、消息队列、信号灯集

原理:

进程都是通过IPC对象唯一的名字,称为键key,找到IPC对象,但内核还是通过IPC对象的ID来找到它.

不同进程只要获得同一IPC对象的键key,就可以实现操作同一IPC对象,从而实际进程间通信

----------------------------------------------------------

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

功能:获得key值

参数:

pathname 一个已经存在的文件路径,(避免传".")

proj_id 只会使用它的低八位

----------------------------------------------------------

例:

key_t key = ftok("/home/will/",'i');

一 共享内存

1.申请一块共享内存

#include <sys/ipc.h>

#include <sys/shm.h>

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

功能:申请一块指定大小共享内存

参数:

key

获得key方法:

a.IPC_PRIVATE : 在亲缘间进程通信使用

b.ftok()函数获得key :可用于非亲缘关系的进程间

size 申请的大小,(分配的实际大小是4K的倍数,但可用的大小为申请大小)

shmflg :

IPC_CREAT | 0666 对应的共享内存段不存在,则创建, 存在直接返回ID

IPC_CREAT | IPC_EXCL | 0666

如果对应的共享内存段已经存在,此时shmget调用失败,错误码设为EEXIST

返回值: 成功返回共享内存id;失败返回-1

注意:

当key值对应的共享内存ID不存在,此时会申请一块新的共享内存区域

当key值对应的共享内存ID存在,此时直接返回已经存在的共享内存ID

---------------------------------------------------------------

查看IPC对象

ipcs -m/-q/-s

删除IPC对象

ipcrm -m/-q/-s ID

-m:memory

-q:queue

-s:semaphore

练习:探测共享内存是否存在,如果存在,报错,如果不存在,则创建

2.共享内存映射

#include <sys/types.h>

#include <sys/shm.h>

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

功能:映射共享内存到进程的私有地址空间

参数:

shmid 共享内存段的标识

shmaddr NULL 让系统自动完成映射

shmflg 0 映射可以读写;

SHM_RDONLY 映射后只能读

返回值:

成功返回映射后的地址;失败返回 (void *)-1,并置errno

注意:进程结束,映射自动撤销

3.撤销共享内存映射

int shmdt(const void *shmaddr);

返回值:成功返回0;失败返回-1,并置errno

注意给shmdt传的地址必须是shmat获得的!

4.删除共享内存

#include <sys/ipc.h>

#include <sys/shm.h>

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

功能:对共享内存进行控制

若cmd参数设置为IPC_RMID,可实现删除共享内存

注意:删除共享内存并不是只要有进程删除就立刻删除,

而是当映射次数为0时才会真正删除。

例:删除共享内存

shmctl(shmid,IPC_RMID,NULL);

二 消息队列

1.创建消息队列

int msgget(key_t key, int msgflg);

功能:创建消息队列

参数:

key IPC_PRIVATE 或 ftok()

msgflg IPC_CREAT | 0666 或 IPC_CREAT | IPC_EXCL | 0666

返回值:

成功返回ID,失败返回-1

2.发送消息

//封装消息结构体

typedef struct

{

//第一个字段必须是消息类型

long type;

//正文部分

...

}msg_t;

//消息正文大小

#define MSG_LEN (sizeof(msg_t) - sizeof(long))

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

功能:向消息队列中添加消息

参数:

msqid 消息队列的ID

msgp 消息存放的地址

msgsz 消息正文的大小--- sizeof(msg_t) - sizeof(long)

msgflg

0:阻塞的方式发送消息

IPC_NOWAIT:非阻塞发送消息(当消息队列中没有可用空间时,不阻塞,立即返回一个错误码EAGAIN)

返回值:

成功返回0,失败返回-1

例如:发送消息类型是100,消息内容"hello world"

typedef struct
{
long type;
char buf[1024];
#if 0/*{{{*/
time_t t;
pid_t pid;
#endif/*}}}*/
}msg_t;

msg_t msg;
msg_t *pmsg = (msg_t *)malloc(sizeof(msg_t));

msg.type = 100;
strcpy(msg.buf,"hello world");
sprintf(msg.buf,"hello world");

if(msgsnd(msgid,&msg,MSG_LEN,0) < 0)
{
....
}
3.接收消息

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

功能:接收指定类型的消息

参数:

msgid 消息队列ID

msgp 接收的消息存放的地址

msgsz 消息正文的大小

msgtyp 接收的消息的类型

msgflg 0(阻塞方式调用) 或 IPC_NOWAIT (没有指定类型消息,不阻塞,立即返回)

返回值:

成功返回接收正文的大小,失败返回-1

例如:接收消息类型为100的消息

msg_t msg;

if(msgrcv(msgid,&msg,MSG_LEN,100,0) < 0)
{
.....
}

printf("------------------------------\n");
printf("TYPE : %ld.\n",msg.type);
printf("MTXT : %s.\n",msg.buf);
printf("------------------------------\n");
4.删除消息队列

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

功能:对消息队列进行控制

若cmd参数设置为IPC_RMID,可实现立即删除消息队列

例:msgctl(msqid,IPC_RMID,NULL);

练习:通过消息队列让A终端上父子进程和B终端上子父进程聊天

要求一边输入quit,所有进程结束.

三 信号灯集

(1)创建一个信号灯集

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

功能:创建或获得信号灯集

参数:

key IPC_PRIVATE 或 ftok()

nsems 信号灯集中信号灯的个数(注意:信号灯的编号从0开始)

semflg IPC_CREAT | 0666 或 IPC_CREAT | IPC_EXCL | 0666

返回值:

成功返回id,失败返回-1

(2)初始化信号灯集中的信号灯的值

int semctl(int semid, int semnum, int cmd, .../*union semun arg*/);

功能:对信号灯集中信号灯进行控制

参数:

semid :信号灯集ID

semnum :操作的信号灯编号,注意信号灯集中的信号灯编号从0开始

cmd :控制命令 SETVAL:设置信号灯值 GETVAL:获得信号灯值 都需第四个共用体类型参数

IPC_RMID:立即删除信号灯集

arg :cmd参数中若指定SETVAL需要保存要设置的信号灯值,或GETVAL

例如:初始化信号灯集中0号信号灯的值为1

union semun {
int		val;	/* Value for SETVAL */

struct semid_ds *buf;	/* Buffer for IPC_STAT, IPC_SET */
unsigned short  *array;	/* Array for GETALL, SETALL */
struct seminfo  *__buf;	/* Buffer for IPC_INFO(Linux-specific) */
};

union semun sem_val;

sem_val.val = 1;

if(semctl(semid,0,SETVAL,sem_val) < 0)
{
...
}
(3)封装p/v操作

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

//参数:
semid  信号灯集id
sops

struct sembuf  //其中部分成员
{
//操作信号灯集中信号灯序号
unsigned short sem_num;  /* semaphore number */

//sem_op  -1:p操作  1:v操作 0:等待
short	  sem_op;   /* semaphore operation */

//0:阻塞 IPC_NOWAIT:非阻塞  SEM_UNDO:进程结束的时候若未释放共享资源,由内核释放申请的资源
short	  sem_flg;  /* operation flags */

}

nsops 操作的信号灯个数

//->p操作:
//      信号灯集的id     操作具体的信号灯编号(从0开始)
void P(int semid,       int s_num)
{
struct sembuf sem;

sem.sem_num = s_num;
sem.sem_op  = -1;
/*sem.sem_flg = 0;*/
sem.sem_flg = SEM_UNDO;

if(semop(semid,&sem,1) < 0)
{
perror("fail to semop");
exit(EXIT_FAILURE);
}

return;
}

//->v操作:
//               操作信号灯集中信号灯序号
void V(int semid,int s_num)
{
struct sembuf sem;

sem.sem_num = s_num;
sem.sem_op  = 1;
sem.sem_flg = SEM_UNDO;

if(semop(semid,&sem,1) < 0)
{
perror("fail to semop");
exit(EXIT_FAILURE);
}

return;
}
(4)删除信号灯集

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

//如果cmd是IPC_RMID,不需要第四个参数
if(semctl(semid,0,IPC_RMID) < 0)
{
perror("fail to semctl");
exit(exit_failure);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: