Linux之进程间通信
概述:
进程间的通信是指在不同进程之间传播或者交换信息。
IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中Socket和Streams支持以Linux中的C语言编程为例。
一、 管道(pipe)
管道,通常指无名管道,是UNIX系统IPC最古老形式。
1、 特点
a、 无名管道是半双工(即数据只能在一个方向流动),具有固定的读端和写端。
b、 它只能用于具有亲缘关系的进程之间的通信(也就是父子或者兄弟进程之间)。
c、它可以看做是一种特殊的文件,对于读写操作可以用write、read等函数,但不属于普通文件,也不属于任何文件系统,只存在于内存中。
d、
#include <unistd.h>
int pipe(int pipefd[2]); 返回值,成功返回0,失败则-1。
当建立一个管道,它会创建两个文件描述符:fd(0)为读而打开 fd(1)为写而打开。(关闭管道只需将两个文件描述符即可)
demo:
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int fd[2];
int pid;
char *p = "my name is cheng xu yuan!";
char readbuf[128];
//int pipe(int pipefd[2]);
if( pipe(fd) == -1){
printf("creat pipe fail\n");
}
pid = fork();
//先让父进程堵塞,子进程写完退出,父进程接着走。
if( pid > 0){
wait();
printf("Tis is father:\n");
close(fd[1]); //先关闭读端
read(fd[0],readbuf,128);//操作写端
printf("father pipe:%s\n",readbuf);
}else if(pid == 0){
system("pause");
return 0;
}
**运行显示:**
```c
CLC@Embed_Learn:~$ ./pipe
Tis is childenr
Tis is father:
father pipe:my name is cheng xu yan!
sh: 1: pause: not found
二、 命名管道(FIFO)
1、 特点
1、 FIFO可以在无关进程之间交换数据,与无名管道不同。
2、 FIFO 有路径名与之相关联,它似一种特殊设备文件形式存在于文件系统。
3、返回值:
mkfifo()去创建命名管道成功返回0,则失败返回-1,若文件存在返回EEXIST。
用errno == EEXIST 判断失败的返回值的原因
errno 包含头文件 #include <errno.h> ,errno 用来捕捉报错反回的原因;
避免存在报错判断打印一些无关紧要信息,可以这样做:
if ( mkfifo("./file",0600) ==-1 && errno !=EEXIST){
printf("creat fifo fail!\n");
perror("why");
}
4、原型
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo (const char *pathname,mode_t mode)
pathname 路径名字, mode_t mode 权限(可读、可写)
5、其中mode参数与open函数中的mode相同,创建一个FIFO就可以用一般的文件IO函数操作。
当open一个FIFO时,是否设置非阻塞标志(O_NONBLOCK)的区别:
a、 如果没指定O_NONBLOCK (默认阻塞),只读open要阻塞到某个为写而打开次FIFO,类似的,只写open要阻塞到某个进程为读而打开它。
b、如果指定O_NONBLOCK,则只读open立即返回,而只写open将出错返回-1,如果没有进程已经为读而打开FIFO,其errno 置ENXIO。
6、demo:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
// int mkfifo(const char *pathname, mode_t mode);
int fd;
char readbuf[128];
if ( mkfifo("./file",0600) ==-1 && errno !=EEXIST){
printf("creat fifo fail!\n");
perror("why");
}
fd = open("./file",O_RDWR);
read(fd,readbuf,128);
printf("read:%s\n",readbuf);
close(fd);
system("pause");
return 0;
}
三、消息队列
消息队列是消息的链接表,存放在内核中,一个消息队列由一个标识符(队列ID)
标识。
1、特点
a、消息队列面是向记录的,其中的消息具有特定的格式以及特定的优先级。
b、消息队列独立于发送与接收进程,进程终止时,消息队列及其内容并不会被删除。
c、消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。
2、原型及参数说明:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
//创建/打开消息队列
key 打开队列索引值
msgflg打开队列方式IPC_CREAT|0777
创建/打开消息队列:成功返回队列ID,失败返回-1
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//发送消息
msqid 消息ID
msgp :buf
msgsz: buf的大小
msgflg:0
添加消息:成功返回0,失败返回-1
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
//接受收消息
读取消息:成功返回0,失败返回-1
msgp :buf
msgsz: buf的大小
msgtyp:消息协议 (99)
msgflg:0
int msgctl(int msqid, int cmd, struct msqid_ds *buf)
//消息队列移除
msqid: id
cmd : IPC_RMID 移除掉消息队列
buf : NULL
//提供一个结构体:
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[1]; /* message data */
};
在一下两种情况下,msgget 将创建一个新的消息队列:
1、如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志位
2、key参数为IPC_CREAT。
3、键值获取ftok()函数
参数msgrcv在读取消息队列是,type参数有下面几种情况:
1、type ==0,返回列队中第一个消息
2、type > 0,返回队列中消息类型为type的第一个消息。
3、type < 0,返回队列中消息类型值小于等于type绝对值消息,也可以把type看做优先级权限;
demo:
接受程序getsend.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
// msgbuf sendbuf ={520,"holle!..."}
struct msgbuf getbuf ={0,'\0'};
struct msgbuf sendbuf ={1314,"thank you!"};
//创建消息队列
int msgid = msgget(1234,IPC_CREAT|0777);
if(msgid == -1){
printf("creat msgget fail!");
}
//向消息队列接为“520”接受消息
msgrcv(msgid,&getbuf,sizeof(getbuf.mtext),520,0);
printf("send:%s\n",getbuf.mtext);
//向消息队列接为“1314”发送消息
msgsnd(msgid,&sendbuf,sizeof(sendbuf.mtext),0);
system("psuse");
return 0;
}
发送程序demo:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
//int msgget(key_t key, int msgflg);
//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);
int main()
{
struct msgbuf sendbuf = {520,"holle!..."};
struct msgbuf readbuf;
int msgid = msgget(1234,IPC_CREAT|0777);
if(msgid == -1){
printf("creat msgget fail!\n");
}
msgsnd(msgid,&sendbuf,sizeof(sendbuf.mtext),0);
msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),1314,0);
printf("get:%s\n",readbuf.mtext);
system("psuse");
return 0;
}
运行演示
两套整合demo:
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
struct msgbuf {
long mtype; /* message type, must be > 0 */
char mtext[128]; /* message data */
};
int main()
{
struct msgbuf sendbuf = {520,"plese connet..."};
struct msgbuf sendbufcd = {1314,"connet susses!"};
struct msgbuf readbuf;
key_t key;
key = ftok(".",1);
printf("key:%x",key);
int msgid = msgget(key,IPC_CREAT|0777);
if(msgid == -1){
printf("creat msgget fail!\n");
}
int pid = fork();
if(pid > 0){
while(1){
msgsnd(msgid,&sendbuf,sizeof(sendbuf.mtext),0);
sleep(3);
msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),1314,0);
printf("client:%s\n",readbuf.mtext);
printf("\n");
}
}else if(pid == 0){
while(1){
msgrcv(msgid,&readbuf,sizeof(readbuf.mtext),520,0);
printf("servor:%s\n",readbuf.mtext);
sleep(3);
msgsnd(msgid,&sendbufcd,sizeof(sendbufcd.mtext),0);
}
}
system("psuse");
return 0;
}
运行效果:
CLC@Embed_Learn:~$ ./getsend
key:1055bb6
servor:plese connet...
key:1055bb6
client:connet susses!
servor:plese connet...
client:connet susses!
四、共享内存
1、步骤:
打开\创建共享内存—映射—数据操作—释放共享内存
Ipcs -m 查看系统中有哪些共享内存
lpcrm -m +id 删除共享内存
2、原型:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget (key_t key, size_t size, int shmflg);
//创建共享内存
成功返回id,失败返回-1
size: 共享内存空间(至少1M对齐)
shmflg:打来方式 IPC_CREAT|0666
void *shmat (int shmid, const void *shmaddr, intshmflg);
//映射共享内存
shmaddr:0
shmflg:0
int shmdt (const void *shmaddr);
//释放共享内存
int shmctl (int shmid, int cmd, struct shmid_ds *buf);
//挂掉共享内存
cmd:IPC_RMID
*buf:0
往共享内存写数据demo:
int main()
{ char *shmaddr = (char*)malloc(sizeof(char)*1024*2);
int shmid;
key_t key;
//创建键值
key = ftok(".",11);
//创建共享内存
shmid = shmget(key, 1024*2, IPC_CREAT|0666);
if(shmid == -1){
printf("shmaddr fail!\n");
exit(-1);
}
//映射共享内存
shmaddr= shmat(shmid,0,0);
//往共享内存写数据
strcpy(shmaddr,"my name is cheng xu yuan!");
sleep(5);
shmdt(shmaddr);
//释放共享内存
shmctl(shmid,IPC_RMID , 0);
//销毁共享内存
system("pause");
return 0;
}
往共享内存读数据demo:
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 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 main()
{
char *shmaddr = (char*)malloc(sizeof(char)*1024*2);
int shmid;
key_t key;
//创建键值
key = ftok(".",11);
//创建共享内存
shmid = shmget(key, 1024*2,0666);
if(shmid == -1){
printf ("shmaddr fail\n");
exit(-1);
}
//映射共享内存
shmaddr= shmat(shmid,0,0);
printf("shmr:%s\n",shmaddr);
shmdt(shmaddr);
// shmctl(shmid,IPC_RMID , 0);
system("pause");
return 0;
}
效果:
其实,共享内存并不达到两个进程间通讯的效果,因为共享内存资源是共享的两个进程间都可以读写操作,这时未免导致信息有误的问题,所以想要避免以上的问题,就需要对两个进程或多个进程之间对共享内存操作时加以约束的条件了;解决方法:共享内存配合信号量的应用。