1.使用管道进行通信
server 创建管道mypipe,并以只读方式打开.
client 以只写方式打开管道.
两者借助管道进行通信,若有一方退出,则另一方也退出。
server.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#define ERR_EXIT(m) \
do {\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
int main()
{
umask(0);
if(mkfifo("mypipe", 0644)< 0)
{
ERR_EXIT("mkfifo");
}
int rfd=open("mypipe",O_RDONLY);
if(rfd<0)
{
ERR_EXIT("open");
}
char buf[1024];
while(1)
{
printf("Please wait....\n");
ssize_t s=read(rfd,buf,sizeof(buf)-1);
if(s>0)
{
buf[s-1]=0;
printf("Client say# %s\n", buf);
}
else if(s==0)
{
printf("client quit,exit now!\n");
exit(EXIT_SUCCESS);
}
else
{
ERR_EXIT("read");
}
}
close(rfd);
return 0;
}
client.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#define ERR_EXIT(m)\
do{\
perror("m");\
exit(EXIT_FAILURE);\
}while(0)
int main()
{
int wfd=open("mypipe",O_WRONLY);
if(wfd<0)
{
ERR_EXIT("open");
}
char buf[1024];
while(1)
{
buf[0]=0;
printf("Please enter#");
fflush(stdout);
ssize_t s=read(0,buf,sizeof(buf)-1);
if(s>0)
{
buf[s]=0;
write(wfd, buf, strlen(buf));
}
else
{
ERR_EXIT("read");
}
}
close(wfd);
return 0;
}
result
管道内置了同步互斥的机制,读写操作互斥进行,保证了通信的可靠性。
2.使用消息队列进行通信
说明:
1.server 创建消息队列,并且等待client 发送消息,接收到client发的消息后,再向client发送消息。
client 得到消息队列, 向server发送消息,之后接受server的消息。
2. msgsnd和msgrcv系统调用函数都内置了同步互斥的机制,
如果没有可发送的消息msgsnd就会自己阻塞。
如果没有可接受的消息msgrcv就会自己阻塞。
3.关于msgget()中flag的说明:
server 的 flag:IPC_CREAT | IPC_EXCL | 0644
client 的 flag:IPC_CREAT
IPC_CREAT | IPC_EXCL 表示想要获得一个全新的消息队列标识符,如果对应的key已经被占用,则错误返回负数值。0644是权限设置。
IPC_CREAT表示如果对应的key已经被占用,就得到key对应的消息队列标识符,如何key未被占用就,创建新的消息队列。
由于client 中 flag 未对消息队列的权限做出设置,所以之后对消息队列的 msgsnd 和 msgrcv 操作会出现 “没有权限访问”的相关错误。
所以server必须首先运行,为获得一个全新的消息队列。
comm.h
#pragma once
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
#define SERVER_TYPE 1
#define CLIENT_TYPE 2
struct msgbufs
{
long mtype;
char mtext[1024];
};
int creatMsgQueue();
int getMsgQueue();
int destroyMsgQueue(int msgid);
int sendMsg(int msgid, int who ,char *msg);
int recvMsg(int msgid, int recvType, char out[]);
comm.c
#include"common.h"
//success > 0 failed==-1
static int commMsgQueue(int flags)
{
key_t _key=ftok(PATHNAME,PROJ_ID);
if(_key<0)
{
perror("ftok");
return -1;
}
int msgid=msgget(_key,flags);
if(msgid<0)
{
perror("msgget");
}
return msgid;
}
int creatMsgQueue()
{
return commMsgQueue (IPC_CREAT|IPC_EXCL|0666);
}
int getMsgQueue()
{
return commMsgQueue(IPC_CREAT);
}
int destroyMsgQueue(int msgid)
{
if(msgctl(msgid,IPC_RMID,NULL)<0)
{
perror("msgctl");
return -1;
}
return 0;
}
int sendMsg(int msgid,int who ,char *msg)
{
struct msgbufs buf;
buf.mtype=who;
strcpy(buf.mtext,msg);
if(msgsnd(msgid,(void*)&buf,sizeof(buf.mtext),0)<0)
{
perror("msgsnd");
return -1;
}
return 0;
}
int recvMsg(int msgid, int recvType,char out[])
{
struct msgbufs buf;
if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvType ,0)<0)
{
perror("msgrcv");
return -1;
}
strcpy(out,buf.mtext);
return 0;
}
server.c
#include<unistd.h>
#include"common.h"
int main()
{
int msgid=creatMsgQueue();
char buf[1024];
while(1)
{
buf[0]=0;
recvMsg(msgid,CLIENT_TYPE,buf);
printf("cilent# %s\n", buf);
printf("Please Enter#");
fflush(stdout);
ssize_t s= read(0,buf,sizeof(buf));
if(s>0)
{
buf[s-1]=0;
sendMsg(msgid,SERVER_TYPE,buf);
printf("send done,wait recv...\n");
}
}
destroyMsgQueue(msgid);
return 0;
}
client.c
#include<unistd.h>
#include"common.h"
int main()
{
int msgid=getMsgQueue();
char buf[1024];
while(1)
{
buf[0]=0;
printf("Please Enter#");
fflush(stdout);
ssize_t s=read(0,buf,sizeof(buf));
if(s>0)
{
buf[s-1]=0;
sendMsg(msgid,CLIENT_TYPE,buf);
printf("send some.wait recv...\n");
}
recvMsg(msgid,SERVER_TYPE,buf);
printf("server# %s\n",buf);
}
return 0;
}
result
3.共享内存通信
server创建共享内存,得到shmid标识符,并将共享内存通过shmat()函数挂接到server进程虚拟地址空间的共享内存区域。
server来打印共享内存中的内容。
client获取共享内存,得到shmid标识符,并将共享内存通过shmat()函数挂接到client进程虚拟地址空间的共享内存区域。
client向共享内存中写入数据。
此时client就和server通过共享内存进行通信了。
注意:
1.shmat后两个参数表示 在挂接过程中 共享内存区开始映射的起始地址的偏移量。
2.在程序退出之前,要调用 shmdt ()来取消进程虚拟地址空间月共享内存的映射关系。
comm.h
#pragma once
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#define PATHNAME "."
#define PROJ_ID 0x6666
int createShm(int size);
int destroyShm(int shmid);
int getShm(int size);
comm.c
#include"comm.h"
static int commShm(int size,int flags)
{
key_t _key=ftok(PATHNAME, PROJ_ID);
if(_key<0)
{
perror("shmget");
return -1;
}
int shmid=0;
if((shmid=shmget(_key,size,flags))<0)
{
perror("shmget");
return -2;
}
return shmid;
}
int destroyShm(int shmid)
{
if(shmctl(shmid,IPC_RMID,NULL)<0)
{
perror("shmctl");
return -1;
}
return 0;
}
int createShm(int size)
{
return commShm(size,IPC_CREAT|IPC_EXCL|0666);
}
int getShm(int size)
{
return commShm(size,IPC_CREAT);
}
server.c
include<unistd.h>
#include"comm.h"
int main()
{
int shmid=createShm(4096);
char *addr=(char*)shmat(shmid,NULL,0);
sleep(2);
int i=0;
while(i<26)
{
printf("client# %s \n",addr);
sleep(1);
i++;
}
shmdt(addr);
sleep(2);
destroyShm(shmid);
return 0;
}
client.c
#include<unistd.h>
#include"comm.h"
int main()
{
int shmid=getShm(4096);
sleep(1);
char *addr=shmat(shmid,NULL,0);
sleep(2);
int i=0;
while(i<26)
{
addr[i]='A'+i;
i++;
addr[i]=0;
sleep(1);
}
shmdt(addr);
sleep(2);
return 0;
}
与消息队列创建的原理相同 , server需要首先运行,创建共享内存。
result:
4.信号量的使用 与 静态库和动态库的打包。
1. 关于参数 nums 在不同函数中的意义不同:
1.在 semget()的中的第二个参数nums表示 :在这个即将创建的信号量集合中 要创建 几个信号量。
(一个信号量集合中可以创建多个信号量)------flag 为 ( IPC_CREAT | IPC_EXCL | 0666)
2.如果semget()的 flag 为 (IPC_CREAT ),表示要获得该信号量集合的id。
那么第二个参数 nums 必须要 小于等于 key所对应的信号量集合的nums,否则出错返回负数值。
3.在 semctl()的第二个参数中的nums 表示要对信号量集合中第几个信号量进行操作(信号量集中的信号量标号从 0 开始)
所以 nums 必须 小于 之前semget输入的nums 。
例如 在semget()中 nums为1,那么在 semctl 中 nums必须为 0。
2. semop()函数中 后两个参数的使用说明:
第二个参数 sembuf * buf 表示结构体数组的指针。
第三个参数表示结构体数组的大小 ,因为可能对多个信号量进行操作。结构体数组中的每一个元素,表示对一个信号量进行操作。
即 :第二个参数传递数组的指针 ,第三个参数传递数组的大小。
comm.h
#pragma once
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#define PATHAME "."
#define PROJ_ID 0x6666
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
int createSemSet(int nums);
int initSem(int semid,int nums, int initVal);
int getSemSet(int nums);
int P(int semid, int Who);
int V(int semid,int Who);
int destroySemSet(int semid);
comm.c
#include"comm.h"
static int commSemSet(int nums,int flags)
{
key_t _key=ftok(PATHAME,PROJ_ID);
if(_key<0)
{
perror("ftok");
return -1;
}
int semid=semget(_key,nums,flags);
if(semid<0)
{
perror("semget");
return -2;
}
return semid;
}
int createSemSet(int nums)
{
return commSemSet(nums,IPC_CREAT|IPC_EXCL|0666);
}
int getSemSet(int nums)
{
return commSemSet(nums,IPC_CREAT);
}
int initSem(int semid ,int nums,int initVal)
{
union semun _un;
_un.val=initVal;
if(semctl(semid, nums, SETVAL, _un)<0)
{
perror("semctl");
return -1;
}
return 0;
}
static int commPV(int semid, int who ,int op)
{
struct sembuf _sf;
_sf.sem_num=who;
_sf.sem_op=op;
_sf.sem_flg=0;
if(semop(semid, &_sf, 1)< 0)
{
perror("semop");
return -1;
}
return 0;
}
int P(int semid, int Who)
{
return commPV(semid,Who,-1);
}
int V(int semid,int who)
{
return commPV(semid,who,1);
}
int destroySemSet(int semid)
{
if(semctl(semid,0, IPC_RMID)<0)
{
perror("semctl");
return -1;
}
return 0;
}
test_sem.c
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdio.h>
#include"comm.h"
int main()
{
int semid=createSemSet(1);
initSem(semid,0,1);
pid_t id=fork();
if(id==0)
{
int _semid=getSemSet(0);
while(1)
{
P(_semid,0);
printf("A");
fflush(stdout);
usleep(123456);
printf("A");
fflush(stdout);
usleep(321456);
V(_semid,0);
}
}
else
{
while(1)
{
P(semid,0);
printf("B");
fflush(stdout);
usleep(223456);
printf("B");
fflush(stdout);
usleep(121456);
V(semid,0);
}
wait(NULL);
}
destroySemSet(semid);
return 0;
}
打包静态库:
1. 执行命令 gcc -c comm.c 生成 comm.o
2. 执行命令 ar cr libmypv.a comm.o 生成静态库
3.执行命令 gcc -o test1 test_sem.c -L. -lmypv (之后可以删除libmypv.a)
4.运行 test1
打包动态库:
1.删除之前的comm.o 。
2.执行命令 gcc -fPIC -c comm.c 生成与地址无关的 comm.o文件。
3.执行命令 gcc -shared -o libmypv.so comm.o 生成动态库文件。
4.执行命令 mv libmypv.so /usr/lib64/ 将动态库文件移入 /usr/lib64 系统索引库中。
5.执行命令 gcc -o test2 test_sem.c -L. -lmypv
6.运行test2
result:
5.关于系统中共享 资源的查看与删除
1.可以通过指令 ipcs 列出系统中所有 共享资源 的相关信息。
2.可以通过指令 ipcrm -q / -m / -s + 资源id 分别删除对 消息队列、共享内存、信号量 共享资源进行删除。
6.对生产者与消费者模型与同步互斥的理解
生产者:写入信息到共享的资源中。
在写入操作之前,生产者会检查 :
1 .共享资源是否被写满 ?如果共享资源已经被写满,生产者就阻塞的等待,直到有消费者读取了其中的资源后,再进行写入。
2. 是否有消费者对该共享资源进行读取? 如果有就阻塞的等待直到到消费者读取完毕。如果没有就直接写入。
消费者:读取信息从共享的资源中。
再读取操作之前,消费者会检查:
1.共享资源是否为空? 如果共享资源为空,消费者就阻塞的等待,直到有生产者对共享资源写入后,再进行读取。
2.是否有生成者对其写入? 如果有,就阻塞的等待直到生成者写入完毕,再进行读取。