命令:ipcs 查看共享内存、消息队列、信号量数组使用情况
ipcrm:删除 大写(根据指定的KEY删除) 小写(根据指定的id删除)
|共享内存 |消息队列|信号量数组
|-M|-Q|-S
|-m|-q|-s
三种通信方式键值KEY获取:
ftok()函数:key_t ftok(const char *pathname, int proj_id);
xxxget xxxopt xxxctl
消息队列
具有缓存消息的能力:ulimit -a 查看POSIX MESSAGE QUEUES参数
1.创建:int msgget(key_t key, int msgflg);
2.使用(双工): msgop:
发送 int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
接收 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
参数2:接收的内存空间位置,该空间定义为一个的结构体msg_st
参数3:接收的数据字节
参数4:是否挑选消息接收
参数5:有无特殊要求
3.控制:msgctl int msgctl(int msqid, int cmd, struct msqid_ds *buf); cmd参数来决定消息队列的控制命令
协议设置proto.h
#ifndef PROTO_H__
#define PROTO_H__
#define KEYPATH "/home/rubylee/Desktop/process/communication/msg"//key_t ftok中的路径参数
#define KEYPROJ 'g'//key_t ftok函数中整型数
#define NAMESIZE 32
struct msg_st//数据结构体
{
long mtype;//消息的类型
char name[NAMESIZE];
int math;
int chinese;
};
#endif
发送程序(主动端:先发包的一方)send.c
#include<stdio.h>
#include<stdlib.h>
#include "proto.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
int main()
{
key_t key=ftok(KEYPATH,KEYPROJ);//创建key值
if(key<0) {perror("ftok failed");exit(1);}
int msgid=msgget(key,0);//创建消息队列 得到msgid
if(msgid<0)
{
perror("msgget failed");
exit(1);
}
struct msg_st sbuf;
sbuf.mtype=1;
strcpy(sbuf.name,"rubylee");
sbuf.math=rand()%100;//随机设置值
sbuf.chinese=rand()%100;//随机设置值
if(msgsnd(msgid,&sbuf,sizeof(sbuf)-sizeof(long),0)<0)
{
perror("msgsnd failed");
exit(1);
}
puts("send ok!");
exit(0);
}
接收程序(被动端:先运行)rev.c
#include<stdlib.h>
#include "proto.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
int main()
{
struct msg_st rbuf;//接收数据缓存
key_t key=ftok(KEYPATH,KEYPROJ);
if(key<0)
{
perror("ftok failed");
exit(1);
}
int msgid=msgget(key,IPC_CREAT|0600);//IPC_CREAT创建 0600权限 第一位是gid或者uid 后面是owner,group,other的权限
if(msgid<0)
{
perror("msgget failed");
exit(1);
}
while(1){
if(msgrcv(msgid,&rbuf,sizeof(rbuf)-sizeof(long),0,0)<0)
sizeof(rbuf)-sizeof(long)减去了消息类型声明的long类型即:传入数据量大小
{perror("msgrcv failed");exit(1);}
printf("NAME=%s\n",rbuf.name);
printf("chinese=%d\n",rbuf.chinese);
printf("math=%d\n",rbuf.math);
//加入信号机制实现正常终止
}
msgctl(msgid,IPC_RMID,NULL);//销毁实例
exit(0);
}
信号量数组
本质:计数器 主要用来控制多个进程对共享资源的使用
1.创建函数semget int semid=semget(key_t key, int nsems, int semflg);
参数1:key值 设置为IPC_PRIVATE时 代表亲缘关系的进程通信
参数2:信号量数组长度
参数3:IPC_CREATE和权限
2.信号量控制函数semctl:
int semctl(int semid, int semnum, int cmd, …);
参数2:semnum的下标值
参数3cmd:
SETVAL 设置单个信号量的值
GETALL 返回信号量集合中所有信号量的值
IPC_RMID删除指定的信号量集合
3.semopt函数实现pv操作:
int semop(int semid, struct sembuf *sops, size_t nsops);
参数2:结构体数组地址
1.文件锁程序
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define PROCNUM 20
#define FNAME "/tmp/out"
#define LINESIZE 1024
static void func_add(void)
{
FILE *fp;
int fd;
char linebuf[LINESIZE];
fp=fopen(FNAME,"r+");
if(fp==NULL)
{
perror("fopen()");
exit(1);
}
fd=fileno(fp);//获得已经打开的文件描述符
lockf(fd,F_LOCK,0);//
//int lockf(int fd, int cmd, off_t
//off_t锁定的连续字节数
fgets(linebuf,LINESIZE,fp);//读取一行字符串
fseek(fp,0,SEEK_SET);//相对偏移量查找子节数 SEEK_SET 文件头
sleep(1);
fprintf(fp,"%d\n",atoi(linebuf)+1);//atoi字符串转整型
fflush(fp);
lockf(fd,F_ULOCK,0);
fclose(fp);
return;
}
int main()
{
pid_t pid;
for(int i=0;i<PROCNUM;i++)
{
pid=fork();
if(pid<0)
{
perror("fork()");
exit(1);
}
if(pid==0)
{
func_add();
exit(0);
}
}
for(int i=0;i<PROCNUM;i++)
wait(NULL);
exit(0);
}
2.采用信号量集对文件进行加锁和解锁
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<errno.h>
#define PROCNUM 20
#define FNAME "/tmp/out"
#define LINESIZE 1024
static int semid;
static void P(void)
{
struct sembuf op;
op.sem_num=0;
op.sem_op=-1;//资源量-1
op.sem_flg=0;//无特殊要求
while(semop(semid,&op,1)<0)
{
if(errno!=EINTR||errno!=EAGAIN)
{
perror("semop()");
exit(1);
}
}
}
static void V(void)
{
struct sembuf op;
op.sem_num=0;//信号数组下标
op.sem_op=1;//资源量+1
op.sem_flg=0;//无特殊要求
if(semop(semid,&op,1)<0)
{
perror("semop()");
exit(1);
}
}
static void func_add(void)
{
FILE *fp;
int fd;
char linebuf[LINESIZE];
fp=fopen(FNAME,"r+");
if(fp==NULL)
{
perror("fopen()");
exit(1);
}
P();//取资源量
fgets(linebuf,LINESIZE,fp);//读取一行字符串
fseek(fp,0,SEEK_SET);//相对偏移量查找子节数 SEEK_SET 文件头
sleep(1);
fprintf(fp,"%d\n",atoi(linebuf)+1);//atoi字符串转整型
fflush(fp);
V();//还资源量
fclose(fp);
return;
}
int main()
{
pid_t pid;
//key=ftok();//亲缘关系通信时 key值为ipc_private
semid=semget(IPC_PRIVATE,1,0600);
if(semid<0)
{
perror("semget()");exit(1);
}
/*设置下标0的互斥量的资源总量为1*/
if(semctl(semid,0,SETVAL,1) < 0)
{
perror("semctl()");
exit(1);
}
for(int i=0;i<PROCNUM;i++)
{
pid=fork();
if(pid<0)
{
perror("fork()");
exit(1);
}
if(pid==0)
{
func_add();
exit(0);
}
}
for(int i=0;i<PROCNUM;i++)
wait(NULL);
semctl(semid,0,IPC_RMID);//销毁信号量
exit(0);
}
结果
共享内存
创建:int shmid= shmget(key_t key, size_t size, int shmflg);
参数1:key值 匿名IPC时为IPC_PRIVATE(亲缘间进程通信)
参数2:共享内存大小
参数3:权限:0600 第一位是gid或者uid
内存映射: void *shmat(int shmid, const void *shmaddr, int shmflg);
参数2:映射到那块地址空间 NULL:自动分配
参数3:权限 只读? 只写? 读写? 无要求设置0
解除映射: int shmdt(const void *shmaddr);
参数:解除映射的地址空间
控制共享内存:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数2:IPC_RMID销毁共享内存
参数3:NULL
#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define MEMSIZE 1024
//#define BUFSIZE 1024
int main()
{
char *ptr;
int shmid=shmget(IPC_PRIVATE,MEMSIZE,0600);//匿名IPC
if(shmid<0)
{
perror("shmget()");exit(1);
}
pid_t pid=fork();
if(pid<0)
{
perror("pid()");exit(0);
}
if(pid==0)//child write
{
ptr=shmat(shmid,NULL,0);
if(ptr==(void*)-1)
{
perror("child shmat()");
exit(1);
}
strcpy(ptr,"hello!");//写入共享内存
shmdt(ptr);//解除映射
exit(0);
}
else//父进程读
{
wait(NULL);
ptr=shmat(shmid,NULL,0);
if(ptr==(void*)-1)
{
perror("parent shmat()");
exit(1);
}
puts(ptr);//读共享内存
shmdt(ptr);//解除映射
shmctl(shmid,IPC_RMID,NULL);//销毁共享内存
exit(0);
}