进程间的通信(IPC)InterProcess Communication
两个或多个进程之间的交换数据的过程
当多个进程协同工作高效率完成任务时,因为每个进程都是一个独立的个体(资源单位),进程之间就需要通信
进程之间通信方式
管道是UNIX系统最古老的进程间通信方式(基本不再使用),历史上的管道通常是半双工的(只允许单向的数据流动),现在的大部分系统都可以全双工,数据可以双向流动
命令:mkfifo
函数:int mkfifo(const char *pathname, mode_t mode);
进程A | 进程B | |
---|---|---|
创建管道 | mkfifo | … |
打开管道 | open | 打开管道 |
读写数据 | read/write | 读写数据 |
关闭管道 | close | 关闭管道 |
删除管道 | unlink | … |
a.c
#include
#include
#include
#include
#include
#include
int main(){
//1.创建管道,一般在/temp目录下创建
if(mkdifo("/tmp/fifo",0644)){
perror("mkfifo");
return -1;
}
//2.打开管道
int fd = open("/tmp/fifo",O_WRONLY);//对方不打开管道就不会返回,程序停在这里
if(0 > fd){
perror("open");
return -1;
}
//3.读写数据
char buf[1024] = {};
for(;;){
printf(">");
gets(buf);
write(fd,buf,strlen(buf)+1);
if(0 == strcmp("quit",buf)){
printf("通信完成!\n");
break;
}
}
//4.关闭管道
close(fd);
//5.删除管道
unlink("/tmp/fifo");
}
b.c
#include
#include
#include
#include
#include
#
int main(){
//1.打开管道
int fd = open("/tmp/fifo",O_RDONLY);
if(0 > fd){
preror("open");
return -1;
}
//2.读写数据
char buf[1024] = {};
for(;;){
read(fd,buf,sizeof(buf));
printf("read:%s\n",buf);
if(0 == strcmp(buf,"quit")){
printf("通信结束\n");
break;
}
}
//3.关闭管道
close(fd);
}
int pipe(int pipefd[2]);
#include
#include
#include
int main(){
int pipefd[2] = {};
//1.打开无名管道
if(pipe(pipefd)){
perror("pipe");
return -1;
}
//2.创建进程
pid_t id = fork();
//3.父进程写
if(id){
//关闭读
close(pipefd[0]);
char buf[1024] = {};
printf("我是父进程%u,我要和子进程%u通信了\n",getpid(),id);
for(;;){
printf(">");
gets(buf);
write(pipefd[1],buf,strlen(buf)+1);
if(0 == strcmp("quit",buf)){
printf("父进程通信结束!");
close(pipefd[1]);
return 0;
}
}
}else{//子进程读
//关闭写
close(pipefd[1]);
char buf[1024] = {};
printf("我是子进程%u,我要和父进程%u通信了\n",getpid(),getppid());
for(;;){
read(piprfd[0],buf,sizeof(buf));
printf("read:%s\n",buf);
if(0 == strcmp("quit",buf)){
printf("子进程通信完成!\n");
close(pipefd[0]);
return 0;
}
}
}
}
练习1 使用有名管道进程通信,管道创建者读,对方写
homework
homework练习2 使用无名管道进行通信,父进程读,子进程写
homework
X/open组织为UNIX系统设计的一套进程间通信机制,有共享内存,消息队列,信号量
IPC标识
IPC键值
key_t ftok(const char *pathname, int proj_id);
IPC对象的创建
IPC对象销毁/控制用到的宏
内核中开辟的一块由IPC对象管理的内存,进程A和进程B都可以用自己的虚拟地址与它进程映射,这样进程A与进程B就共享同一块内核中的内存
特点
编程模型
进程A | 进程B | ||
---|---|---|---|
生成IPC键值 | ftok | 生成IPC键值 | ftok |
创建IPC对象(共享内存) | shmget | 创建IPC对象(共享内存) | shmget |
映射共享内存 | shmat | 映射共享内存 | shmat |
使用共享内存 | *ptr | 使用共享内存 | *ptr |
取消映射 | shmdt | 取消映射 | shmdt |
删除共享内存 | shmctl | … |
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
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 映射次数*/
...
};
struct ipc_perm {
key_t __key; /* Key supplied to shmget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions + SHM_DEST and
SHM_LOCKED flags */
unsigned short __seq; /* Sequence number */
};
a.c
#include
#include
#include
#include
#include
#include
#include
int main(){
//创建IPC键
key_t key = ftok(".",110);
//创建共享内存
int shmid = shmget(key,4096,IPC_CREAT);
if(0 > shmid){
perror("shmget");
retunr -1;
}
//映射
char* str = shmat(shmid,NULL,SHM_RND);
if((void)*-1 == str){
perror("shmat");
return -1;
}
pid_t pid = 0;
printf("请输入要通信的进程号:");
scanf("%u",&pid);
//使用
for(;;){
printf(">");
gets(str);
kill(pid,SIGINT);
if(0 == strcmp("quit",str)){
printf("通信结束\n");
break;
}
}
//取消映射
if(0 > shmdt(str)){
perror("shmdt");
return -1;
}
//删除
if( 0 > shmctl(shmid,IPC_RMID,NULL)){
perror("shmctl");
return -1;
}
}
b.c
#include
#include
#include
#include
#include
#include
#include
#include
int main(){
printf("我是进程%u\n",getpid());
//创建IPC键
key_t key = ftok(".",110);
//获取共享内存
int shmid = shmget(key,0,0);
if(0 > shmid){
perror("shmget");
return -1;
}
//映射
char* str = shmat(shmid,NULL,SHM_RND);
if((void*)-1 == str){
perror("shmat");
return -1;
}
//使用
void sigint(int sig){
printf("read:%s\n",str);
if(0 == strcmp("quit",str)){
printf("通信结束\n");
//取消映射
if(0 > shmdt(str)){
perror("shmdt");
exit(0);
}
}
}
signal(SIGINT,sigint);
pause();
}
由内核管理的管道,可以按顺序发送消息包(消息类型+消息内容),可以全双工工作
int msgget(key_t key, int msgflg);
int msgsnd(int msqid, const void *msgp, size_t msgs_z, int msgflg);
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
sizeof(msgbuf)-4
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
int msgctl(int msqid, int cmd, struct msqid_ds *buf));
删除消息队列,设置或获取消息队列的属性
msqid 消息队列标识符msgget函数的返回值
cmd
buf
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) */
};
返回值 成功0 失败-1
a.c
#include
#include
#include
#include
#include
#include
#include
#include "struct.h"
int main(){
//创建消息队列
int msgid = msgget(ftok(".",119),IPC_CREAT|IPC_EXCL|0644);
if(0 > msgid){
perror(msgget);
return -1;
}
Msg msg = {666};
for(;;){
printf(">");
gets(msg.data);
msgsnd(msgid,&msg,sizeof(Msg)-sizeof(msg.type),0)
if(0 == strcmp("quit",msg.data)){
printf("通信结束\n");
break;
}
}
if(0 > msgctl(msgid,IPC_RMID,NULL)){
perror("msgctl");
return -1;
}
Msg msg = {};
for(;;){
msgrcv(msgid,&msg,sizeof(Msg),555,0);
printf("read:%s\n",msg.data);
if(0 == strcmp("quit",msg.data)){
printf("通信结束\n");
break;
}
}
}
b.c
#include
#include
#include
#include
#include
#include
#include
#include "struct.h"
int main(){
//获取消息队列
int msgid = msgget(ftok(".",119),0);
if(0 > msgid){
perror("msgget");
return -1;
}
//
}
struct.h
typedef struct Msg{
long type;
char data[256];
}Msg;
msqid 消息队列标识符msgget函数的返回值
cmd
buf
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) */
};
返回值 成功0 失败-1
a.c
#include
#include
#include
#include
#include
#include
#include
#include "struct.h"
int main(){
//创建消息队列
int msgid = msgget(ftok(".",119),IPC_CREAT|IPC_EXCL|0644);
if(0 > msgid){
perror(msgget);
return -1;
}
Msg msg = {666};
for(;;){
printf(">");
gets(msg.data);
msgsnd(msgid,&msg,sizeof(Msg)-sizeof(msg.type),0)
if(0 == strcmp("quit",msg.data)){
printf("通信结束\n");
break;
}
}
if(0 > msgctl(msgid,IPC_RMID,NULL)){
perror("msgctl");
return -1;
}
Msg msg = {};
for(;;){
msgrcv(msgid,&msg,sizeof(Msg),555,0);
printf("read:%s\n",msg.data);
if(0 == strcmp("quit",msg.data)){
printf("通信结束\n");
break;
}
}
}
b.c
#include
#include
#include
#include
#include
#include
#include
#include "struct.h"
int main(){
//获取消息队列
int msgid = msgget(ftok(".",119),0);
if(0 > msgid){
perror("msgget");
return -1;
}
//
}
struct.h
typedef struct Msg{
long type;
char data[256];
}Msg;