XSI 有 信号量 共享内存 还有 消息队列
system v 都有一个共同的特征就是 先需要获取相同的key值在用户态中(ftok(路径,设定一个共同的proc_id))
然后在通过key获取内核态的一个id 因为内核是通过id进行标记的
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
. proj_id
key 31-24 proj_id 低8位
key 23-16 st/_dev属性的低8位
key 15-0 st_ino属性的低16位
1。消息队列:首先 我们讲讲消息队列是如何一回事 消息队列呢 在我们的windows系统下 是一种实时处理消息的一种数据结构
那么我们在服务器开发阶段呢 为了产生多对多的程序 而且可以实时处理消息 使我们传输消息的负载担子减轻 所以我们采用消息队列
那么消息队列如何操作呢?
获取id 通过msgget 获取 需要设定mode IPC_CREAT|IPC_EXCL|MODE
msgctl 进行设定消息队列的属性
我们的system v的三种对象中 都有一个权限设定 我们的msgget就是设定的struct ipc_perm的属性 以下是我整理的一些笔记
struct ipc_perm
{
__kernel_key_t key;
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode;
unsigned short seq;
};
## XSI消息队列
* 对象:
struct msqid_ds
```c
struct msqid_ds {
struct ipc_perm msg_perm; /* Ownership and permissions */
time_t msg_stime; /* Time of last msgsnd(2) */
time_t msg_rtime; /* Time of last msgrcv(2) */
time_t msg_ctime; /* Time of last change */
unsigned long __msg_cbytes; /* Current number of bytes in
queue (nonstandard) */
msgqnum_t msg_qnum; /* Current number of messages
in queue */
msglen_t msg_qbytes; /* Maximum number of bytes
allowed in queue */
pid_t msg_lspid; /* PID of last msgsnd(2) */
pid_t msg_lrpid; /* PID of last msgrcv(2) */
struct msg * msq_first ;
struct msg * msq_last ;
};
int msgctl(int msqid,int cmd,struct msqid_ds *buf);
cmd:
IPC_RMID 删除
IPC_SET 设置ipc_perm参数
IPC_STAT 获取ipc_perm参数
IPC_INFO 获取ipc信息 和ipcs
* 使用对象:
1. 发送
int msgsnd(int msqid,const void *msg_ptr,size_t sz,int msgflg);
struct msgbuf
{
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
2. 接收
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
msgtyp 0 默认接收第一条消息
msgtyp > 0 接收第一条msgtyp消息
msgtyp < 0 接收小于msgtyp绝对值的消息
我们在进行发送和接收消息的时候都需要通过指定在消息队列里面的struct msg结构体里面的类型选定相同的类型进行发送和接收 msgsnd msgrcv
#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
#include<unistd.h>
#include<errno.h>
#include<sys/types.h>
#define CLIENT_TYPE 1
#define SERVER_TYPE 2
#define _SIZE_ 1024
struct msgbuf
{
long mtype;
char mtext[_SIZE_];
};
int recv_msg(int msg_id,int want,char out[],int out_len)
{
struct msgbuf _buf;
memset(&_buf,'\0',sizeof(_buf));
int ret = msgrcv(msg_id,&_buf,sizeof(_buf.mtext),want,0);
if(ret <= -1)
{
printf("%d:%s\n",errno,strerror(errno));
return -1;
}
memset(out,'\0',out_len);
strcpy(out,_buf.mtext);
return 0;
}
int main(int argc,char *argv[])
{
key_t key;
key=ftok(".",100);
int msgid;
msgid=msgget(key,IPC_CREAT|0600);
struct msgbuf mybuf;
char buf[1024];
while(1)
{
memset(&mybuf,'\0',sizeof(mybuf));
recv_msg(msgid,CLIENT_TYPE,buf,sizeof(buf)); //recv msg type=2
printf("recv msg:%s\n",buf);
}
}
2.信号量
信号量很简单 这个本身是用来控制同步进程间的同步通讯 而且他也在某种意义上具有一点通讯的功能
## XSI信号量
#### 概念:
* 实现进程间同步
* 表示系统可用资源的个数
#### 对象:
struct semid_ds {
struct ipc_perm sem_perm; /* Ownership and permissions /
time_t sem_otime; / Last semop time /
time_t sem_ctime; / Last change time /
unsigned long sem_nsems; / No. of semaphores in set */
struct sem * sem_base
};
struct sem{
int semval 信号量的值
int sempid 最近一个操作的进程号
}
#### 对象操作
* 创建对象:
int semget(key_t k,int n,int semflag);
* 设置对象:
int semctl(int sid,int semnum,int cmd,union semun a);
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) */
};
cmd 如果对于整个信号量集合,则有如下设置方法
IPC_STAT ipc_perm
IPC_SET ipc_perm
IPC_RMID
IPC_INFO 信号量的系统信息
int semctl(int sid,int semnum,int cmd,.......);
cmd 如果对于单个信号量集合,则有如下设置方法
GETPID 返回操作IPC的PID
GETVAL
GETALL
SETVAL
SETALL
* 使用对象
*
int semop(int s,struct sembuf *sb,size _t n);
struct sembuf{
unsigned short int sem_num;
short int sem_op;
short int sem_flg;};
sem_op
+ 表示进行加
- 表示进行减
0 表示测试是否为0
* 如果当前信号量的操作后的值为负数,则进行阻塞,一直到满足条件为止
sem_flg
IPC_NOWAIT 如果操作信号集合中任意一个失败,立即返回,并且不会对其他的信号量做操作
SEM_UNDO :进程退出后,该进程对sem进行的操作将被撤销
struct sembuf sops[4]
sops[0].sem_num =1
sops[0].sem_op =-1 / P操作/
sops[0].sem_flag = 0
sops[1].sem_num =2
sops[1].sem_op =1 / V操作/
sops[1].sem_flag =0
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <errno.h>
int sem_id;
void init()
{
key_t key;
key=ftok(".",'s');
sem_id=semget(key,2,IPC_CREAT|0644);
//printf("sem id is %d\n",sem_id);
}
#define produce 1
#define customer 0
#define P 1
#define V 0
int main(int argc,char *argv[])
{
struct sembuf sops[2];
sops[0].sem_num = 0;
sops[0].sem_op = 1;
sops[0].sem_flg = 0;
sops[1].sem_num = 1;
sops[1].sem_op = -1;
sops[1].sem_flg = 0;
init();
printf("this is customer\n");
while(1)
{
printf("\n\nbefore consume:\n");
printf("productor is %d\n",semctl(sem_id,0,GETVAL));
printf("space is %d\n",semctl(sem_id,1,GETVAL));
semop(sem_id,(struct sembuf *)&sops[P],customer); //get the productor to cusume
printf("now consuming......\n");
sleep(2);
semop(sem_id,(struct sembuf *)&sops[V],produce); //now tell the productor can bu produce
printf("\nafter consume\n");
printf("products number is %d\n",semctl(sem_id,0,GETVAL));
printf("space number is %d\n",semctl(sem_id,1,GETVAL));
sleep(3);
}
}
union semun {
int val; /* Value for SETVAL */ 信号量的值
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ 对于信号量的semid——ds 对象的链表
unsigned short* array; /* Array for GETALL, SETALL *//所有信号量的值 通过GETALL SETALL进行操控
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
注意信号量 里面的semop里面有一个对于cmd重要的设定就是 SEM_UNDO 这个可以在我们的其实一个进程结束之后 对于当前进程的信号量操控都会取消 比如子进程执行semop 里面的sops[1].sem_num =2
sops[1].sem_op =1 / V操作/
sops[1].sem_flag =SEM_UNDO; 这种情况下 那么我的子进程exit(0)之后 父进程呢 他semctl(sem_id,0,GETVAL) 获取的值 还是原来刚刚初始的值 不会改变
3.共享内存
就是说把内核中的某一块内存 注入到我们的当前进程页中 然后我们进行一系列的操控内存 其实和堆差不多 但是这个共享内存用作进程间的通讯 很有帮助 我们可以把响应的数据发送到这块内存 然后通过shmget shmctl shmat shmdt 进行响应的操控 还可以设定在shmctl 里面一个锁 用作同步操控 共享内存的大小一般4096比较好
而且我们的读和写不能同时进行 所以需要加锁 但是一般我们加入信号量就可以了 通过信号量进行控制同步操控 而且内存资源不会占用大
## XSI共享内存
* 数据量最大
* 传输最快
* mmap
#### 共享内存对象
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms /
int shm_segsz; / size of segment (bytes) /
__kernel_time_t shm_atime; / last attach time /
__kernel_time_t shm_dtime; / last detach time /
__kernel_time_t shm_ctime; / last change time /
__kernel_ipc_pid_t shm_cpid; / pid of creator /
__kernel_ipc_pid_t shm_lpid; / pid of last operator /
unsigned short shm_nattch; / no. of current attaches */
}
#### 创建共享内存
int shmget(key_t key, size_t size, int shmflg);
共享内存的权限
SHM_R
SHM_W
#### 设置共享内存
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
SHM_INFO
struct shm_info {
int used_ids; /* # of currently existing
segments /
unsigned long shm_tot; / Total number of shared
memory pages /
unsigned long shm_rss; / # of resident shared
memory pages /
unsigned long shm_swp; / # of swapped shared
memory pages /
unsigned long swap_attempts;
/ Unused since Linux 2.4 /
unsigned long swap_successes;
/ Unused since Linux 2.4 */
};
SHM_STAT
SHM_LOCK
SHM_UNLOCK
#### 使用对象
void *shmat(int shmid, const void *shmaddr, int shmflg);//第二个参数一般设定NULL 通过获取系统映射出来的逻辑地址 最后一个参数设定0 可读可写对于共享内存
char * buf
SHM_RDONLY
SHM_REMAP
SHM_EXEC
int shmdt(const void *shmaddr);
#### 注意
1.父子进程的共享内存约定 只要执行其中一个系统调用 那么当前的进程就会失去对共享内存的操控 共享内存就会脱离当前的进程 相当于shmdt
fork函数
exec执行一个新的程序 自动卸载
exit
总结一下 其实我们的system v呢 其实就是在进程间通讯提供一个同步控制还有一个增加效率的通讯方式 共享内存就是一种提供文件传输量大的通讯方式
消息队列就是提供一种多对多的一种通讯mode