进程间的通信:
(1) 基于文件的通信:
1. 普通文件(io/mmap)
2.有名管道文件
3.匿名管道文件
4.socket
(2)基于内存的通信:
1.信号量 :用于管理对资源的访问。
2.共享内存:用于进程之间高校的地共享数据。
3.消息队列:在程序之间传递数据的一种简单方法。
使用到的命命令:
—————————————————————————
$ ipcs
$ ipcs -s --------- 查看信号量
$ ipcs -m ---------- 共享内存
$ ipcs -q ----------- 消息队列
$ ipcrm -q 编号ID ---------删除消息队列
——————————————————————————
一.信号量
1.semget函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, /// 为一个整数值,
int nsems, 为信号量数目,通常取值为1.
int semflg);/// 一组标志,通常使用 IPC_CREAT | IPC_EXCL 来创建新的,唯一的信号量。
说明:创建一个信号量,获得一个信号量ID。
如果成功:
返回信号量标识符。
如果失败:
返回-1.
2. semop函数
int semop(int semid, 有semget返回的信号量标识符。
struct sembuf *sops, 指向结构体数组的指针
unsigned nsops); ///
说明: 用于改变信号量的值
第二个参数指向的结构体数组中,每个数组元素至少包含以下成员
struct sembuf
{
unsigned short sem_num; /* semaphore number */ 信号编号,除非使用一组信号,否则一般为0
short sem_op; /* semaphore operation */信号操作,一般有两种取值:-1(p操作,等待信号变为可用); +1(v操作,发送信号表示信号量现在已可用)
short sem_flg; /* operation flags */通常设置为SEM_UNDO,
};
3. semctl 函数
int semctl(int semid, ///有semget返回的信号量标识符
int semnum, /// 信号量编号,一般取值为0(表示是唯一信号量)
int cmd, ...); /// 采取的行动
说明: 直接控制信号量信息。
cmd有两个最常用的值:
SETVAL: 用来把信号量初始化为一个已知的值。
IPC_RMID: 用于删除一个已知无需继续使用的信号量标识符。
二.共享内存
1.shmget函数
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, /// ftok 函数可以取得唯一的key值
size_t size, /// 共享内存容量
int shmflg);/// 9个比特的权限标志
说明: 创建共享内存,返回共享内存标识符。
第三个参数的常见两种方式:
创建: (IPC_CREAT | IPC_EXCL)
打开: 0
2.shmat 函数
void *shmat(int shmid,/// 由shmget返回的共享内存标识符。
const void *shmaddr, ///
int shmflg);
说明: 根据ID得到共享,访问内存数据。
3.shmdt函数
int shmdt(const void *shmaddr);
说明: 将共享内存从当前进程中分离,参数为shmat返回的地址指针。
4. shmctl 函数
int shmctl(int shmid, /// shmget返回的共享内存标识符
int cmd, /// 要采取的动作
struct shmid_ds *buf); /// 指向包含共享内存模式和访问权限的当前关联值
第二个参数cmd的可取值:
————————————————————————————
IPC_STAT ------
IPC_SET ------
IPC_RMID --------删除共享内存段
第三个参数的struct shmid_ds 结构体:
——————————————————————————————————
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};
——————————————————————————————————————
编程模型:
(1) 创建共享内存,得到一个ID;
(2)把ID映射成虚拟地址(挂载);
(3)使用虚拟地址访问内核共享内存
(4)卸载(分离)虚拟地址, shmdt
(5) 删除共享内存 shctl(修改/获取共享内存的属性)
案例:
shmA.c
/// shmA.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/shm.h>
#include <sys/ipc.h>
key_t key;
int shmid;
int *p;
int i = 0;
void deal(int s)
{
if(s==SIGINT)
{
//4.卸载共享内存shmdt
shmdt(p);
//5.删除共享内存shctl
shmctl(shmid,IPC_RMID,0);
exit(0);
}
}
main()
{
signal(SIGINT,deal);
//1.创建共享内存, shmget
key = ftok("/tmp",255);
if(key == -1) printf("ftok error:%m\n"),exit(-1);
shmid = shmget(key,4,IPC_CREAT | IPC_EXCL |0666);
if(shmid == -1) printf("get error:%m\n"),exit(-1);
//2.挂载共享内存shmat
p = shmat(shmid,0,0);
if(p == (int*)-1) printf("get error:%m\n"),exit(-1);
//3.访问共享内存
while(1)
{
*p = i;
sleep(1);
++i;
}
}
shmB.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/shm.h>
#include <sys/ipc.h>
key_t key;
int shmid;
int *p;
void deal(int s)
{
if(s==SIGINT)
{
//4. 卸载共享内存, shmdt
shmdt(p);
exit(0);
}
}
main()
{
signal(SIGINT,deal);
//1.创建共享内存
key = ftok("/tmp",255);
if(key == -1) printf("ftok error:%m\n"),exit(-1);
shmid = shmget(key,4,0);
if(shmid == -1) printf("get error:%m\n"),exit(-1);
//2.挂载共享内存shmat
p = shmat(shmid,0,0);
if(p == (int*)-1) printf("ar error:%m\n"),exit(-1);
//3.访问共享内存
while(1)
{
sleep(1);
printf("%d\n",*p);
}
}
补充:
——————————————————
$ ipcs -m ------ 查看共享内存
$ ipcrm -m 555****(文件ID) ------ 删除共享内存(这个文件ID是随机的)
三. 消息队列
消息队列是在两个不相关进程之间传递数据的,优势是独立于发送和接收进程而存在。
1. msgget函数-----------------创建消息队列函数
int msgget(key_t key, int msgflg);
2. msgsnd函数-------------------发送消息
int msgsnd(int msqid, 消息队列标识符
const void *msgp, /// 要发送的消息
size_t msgsz, 消息的长度
int msgflg); / 发送消息的方式,建议为0
3.msgrcv函数 ---------------从消息队列中获取消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
4.msgctl 函数
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
编程模型:
创建消息队列
使用消息队列
删除队列
msgA.c 发射部分
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/msg.h>
#define MAX_TEXT 512
struct msgbuf
{
long mtype;
char mtext[MAX_TEXT];
};
int main()
{
int flag = 1;
key_t key;
struct msgbuf msg;
int msgid;
char buffer[BUFSIZ];
// 1.创建消息队列
key = ftok(".",200);
if(key == -1) printf("frok err:%m\n"),exit(-1);
printf("key=%d\n",key);
msgid = msgget(key,0666 | IPC_CREAT);
if(msgid == -1) printf("msgget err:%m\n"),exit(-1);
while(flag)
{
printf("请输入您要发送的字符串(以“#”结束输入):\n");
fgets(buffer,BUFSIZ,stdin);
msg.mtype = 1;
bzero(msg.mtext,sizeof(msg.mtext));
strcpy(msg.mtext,buffer);
if(msgsnd(msgid,&msg,MAX_TEXT,0) == -1) printf("msgsnd err:%m\n") ,exit(-1);
if(strncmp(buffer,"#",1) == 0)
flag = 0;
}
msgctl(msgid,IPC_RMID,0); // 删除队列
exit(0);
}
msgB.c 接收部分
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/msg.h>
struct msgbuf
{
long mtype;
char mtext[BUFSIZ];
};
int main()
{
int flag = 1;
key_t key;
int msgid;
struct msgbuf msg;
key = ftok(".",200);
if(key == -1) printf("ftok err:%m\n"),exit(-1);
printf("key=%d\n",key);
msgid = msgget(key,0666 | IPC_CREAT);
if(msgid == -1) printf("msgget err:%m\n"),exit(-1);
while(flag)
{
bzero(msg.mtext,sizeof(msg.mtext));
if(msgrcv(msgid,(void *)&msg,BUFSIZ,0,0) == -1)
printf("msgrcv err:%m\n"),exit(-1);
printf("你接收到的数据是:%s\n",msg.mtext);
if(strncmp(msg.mtext,"#",1) ==0 )
flag = 0;
}
// 删除消息队列
msgctl(msgid,IPC_RMID,0);
}