Linux 3.进程间通信(IPC)(pipe 无名管道、mkfifo 有名管道、ftok、msgget、msgrcv、msgsnd、msgctl 消息队列)

进程间通信:

不同进程之间传数据、交换信息。

进程间方式:

  1. 管道(无名管道和命名管道)
  2. 消息队列
  3. 信号量
  4. 共享内存
  5. socket
  6. steams等。

其中socket 和 steams 支持不同主机上的两个进程通信。

pipe 管道(无名管道)

通常指无名管道,是Unix 系统 IPC最古老的形式。

头文件及原型

#include <unistd.h>

int pipe(int pipefd[2]);

特点

  1. 半双工,只能要么接收数据,要么传输数据。一个读,一个写。
  2. 只能用于具有亲缘关系的进程通信,比如:父子进程。
  3. 可以看成是特殊文件,它读写可以使用 read、write等函数,但它不是普通文件和系统文件,它是只存在内存中的。

pipe 示例

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>

int main()
{
	int fd[2];
	char readBuf[128]={0};
	
	if(pipe(fd)==-1)
	{
		printf("pipe fail!\n");
	}
	else
	{
		printf("pipe successfully!\n");
	}
	
	int retpid=fork();
	
	if(retpid>0)
	{
		sleep(3);
		printf("this is father print...\n");
		close(fd[0]);
		write(fd[1],"hello word",strlen("hello word"));
		wait(NULL);
	}
	else if(retpid==0)
	{
		printf("this is child print...\n");
		close(fd[1]);
		read(fd[0],readBuf,128);
		printf("readBuf=%s\n",readBuf);
		exit(0);	
	}
	else
	{
		printf("fork fail!\n");
	}
	return 0;
}

运行结果:

在这里插入图片描述

FIFO(有名管道)

有名管道也叫命名管道,在文件系统目录中存在一个管道文件。
管道文件仅仅是文件系统中的标示,并不在磁盘上占据空间。在使用时,在内存上开辟空间,作为两个进程数据交互的通道。

管道文件的创建

  1. 在shell中使用 mkfifo 命令
mkfifo filename 
  1. mkfifo 函数 (在代码中使用其创建管道文件)

mkfifo 头文件及原型

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *filename,mode_t mode);

mkfifo 参数

pathname:文件名/文件路径

mode:权限 比如 0600 就是可读可写。

mkfifo 返回值

成功:则返回 0 
失败:返回 -1 , 错误原因存于 errno 中.

mkfifo 创建有名管道

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>

int main()
{
    int mk=mkfifo("./fifo",0600);
    if(mk==0)
    {
        printf("mkfifo successfully!\n");
    }
    else if(mk==-1)
    {
        printf("mkfifo fail!\n");
        perror("why");
    }

    return 0;
}

运行结果:

在这里插入图片描述

注意:如果已经存在这个文件,创建会失败的。

FIFO 使用示例

fifoRead.out

#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>

int main()
{
    char readBuf[32]={0};

    int mk=mkfifo("./fifo",0600);
    if(mk==0 && errno!=EEXIST)
    {
        printf("mkfifo successfully!\n");
    }

    int fd=open("./fifo",O_RDONLY);
    
    int n_read=0;
    while(1)
    {
        n_read=read(fd,readBuf,32);
        printf("read %d from writeFifo,readBuf=\n%s\n",n_read,readBuf);
        memset(readBuf,'\0',sizeof(readBuf));

        if(n_read==0)
        {
            break;
        }
    }

    close(fd);
    return 0;
}

fifoWrite.out

#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>

int main()
{
    char* str="Yinyuer is a pretty girl!";

    int mk=mkfifo("./fifo",0600);
    if(mk==0 && errno!=EEXIST)
    {
        printf("mkfifo successfully!\n");
    }

    int fd=open("./fifo",O_WRONLY);
    
    int i=0;

    for(i=0;i<5;i++)
    {
        write(fd,str,strlen(str));
        sleep(3);
    }

    close(fd);
    return 0;
}

运行结果:

在这里插入图片描述

在这里插入图片描述

消息队列

消息队列是消息的链接表,是存在内核中的,一个消息队列是由一个标识符(ID号)来标识。

ftok

ftok 功能

系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。

ftok 头文件及原型

#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok( const char * fname, int id );

ftok 参数

fname:指定的文件名(已经存在的文件名),一般使用当前目录,如:
key_t key;
key = ftok(".", 1); 这样就是将fname设为当前目录。
id:子序号。虽然是int类型,但是只使用8bits(1-255)。

ftok 返回值

成功:返回key_t值(即IPC 键值)

出错:-1,错误原因存于error中

msgget

msgget 功能

用于创建一个新的或打开一个已经存在的消息队列,此消息队列与key相对应。

msgget 头文件及原型

 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>

int msgget(key_t key, int msgflg);

msgget 参数

key: 函数ftok的返回值(ID号)或IPC_PRIVATE。

msgflag

IPC_CREAT:创建新的消息队列。
IPC_EXCL:与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误。 
IPC_NOWAIT:读写消息队列要求无法满足时,不阻塞。返回值: 调用成功返回队列标识符,否则返回-1.

msgget 返回值

成功:返回消息队列的标识符

出错:-1,错误原因存于error中

msgrcv

msgrcv 功能

从标识符为msqid的消息队列读取消息并存于msgp中,读取后把此消息从消息队列中删除。

msgrcv 头文件及原型

 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

msgrcv 参数

msqid:消息队列的识别码(ID)。
msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下。
sgsz:消息的大小。
msgtyp: 消息类型,这个typ和结构体msgbuf内的msgtyp是一样的,
如果需要接受到想要的消息,则msgsnd发送的结构体中msgtyp应该和msgrcv的msgtyp相同。

在这里插入图片描述

msgflg:这个参数依然是控制函数行为的标志,取值可以是:0,表示忽略。

在 msgrcv 函数中,最后一个参数 flag 控制着接收消息的行为。这个参数的值可以是 0 或者一些其他的标志,它们影响着函数的阻塞行为以及消息的接收方式。

当 flag 参数为 0 时,表示接收消息的行为是阻塞的。也就是说,如果当前消息队列中没有符合条件的消息可供接收,msgrcv 函数会一直等待,直到有合适的消息到达为止。这样的阻塞模式通常用于需要等待消息到达的场景,使得进程能够在没有消息时挂起,直到有消息可用。

除了 0 之外,还有其他的一些标志可以传递给 flag 参数,例如:

IPC_NOWAIT: 表示非阻塞模式,如果没有符合条件的消息可供接收,函数会立即返回,并返回一个错误码(例如 -1),而不会等待消息到达。
MSG_NOERROR: 表示在接收消息时,如果消息长度超过了缓冲区的大小,则截断消息而不会产生错误。
其他一些标志,具体取决于系统的实现和特性。
因此,当 flag 参数为 0 时,表示 msgrcv 函数以阻塞模式接收消息,直到有消息到达为止。

msgrcv 返回值

成功:实际读取到的消息数据长度

出错:-1,错误原因存于error中

msgsnd

msgsnd 功能

将msgp消息写入到标识符为msqid的消息队列。

msgsnd 头文件及原型

 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/msg.h>

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

msgsnd 参数

msqid:消息队列标识符

msgp:发送给队列的消息。msgp可以是任何类型的结构体,但第一个字段必须为long类型,即表明此发送消息的类型,msgrcv根据此接收消息。msgp定义的参照格式如下:

struct s_msg{ /*msgp定义的参照格式*/
	long type; /* 必须大于0,消息类型 */
	char mtext[256]; /*消息正文,可以是其他任何类型*/
} msgp;

msgsz:要发送消息的大小,不含消息类型占用的4个字节,即mtext的长度

msgflg

0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列

IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回

IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程。

msgsnd 返回值

成功:0

出错:-1,错误原因存于error中

msgctl

msgctl 功能

获取和设置消息队列的属性。

msgctl 头文件原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgctl(int msqid, int cmd, struct msqid_ds *buf)

msgctl 参数

msqid:消息队列标识符

cmd

IPC_STAT:读取消息队列的数据结构msqid_ds,并将其存储在b u f指定的地址中。

IPC_SET:设置消息队列的数据结构msqid_ds中的ipc_perm元素的值。这个值取自buf参数。

IPC_RMID:从系统内核中移走消息队列。

buf:消息队列管理结构体,请参见消息队列内核结构说明部分。

msgctl 返回值

成功:0

出错:-1,错误原因存于error中

消息队列 示例

msgrcv.out

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>

struct msgbuf
{
    long mtype;       /* message type, must be > 0 */
    char mtext[128];    /* message data */
};

int main()
{
    key_t key=ftok(".",66);
    printf("key=%x\n",key);
    
//    int msgget(key_t key, int msgflg);
    int msgId=msgget(key,IPC_CREAT|0777);
    if(msgId==-1)
    {
        printf("msgget fail!\n");
    }

    struct msgbuf readBuf;
//    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);
    printf("msg from msgsnd:\nreadBuf=%s\n",readBuf.mtext);
    
    struct msgbuf sendBuf={666,"Yeah!I agree that!"};
    msgsnd(msgId,&sendBuf,sizeof(sendBuf.mtext),0);
	
	msgctl(msgId,IPC_RMID,NULL);
	
    return 0;
}

msgsnd.out

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>

struct msgbuf
{
    long mtype;       /* message type, must be > 0 */
    char mtext[128];    /* message data */
};

int main()
{
    key_t key=ftok(".",66);
    printf("key=%x\n",key);
    
//    int msgget(key_t key, int msgflg);
    int msgId=msgget(key,IPC_CREAT|0777);
    if(msgId==-1)
    {
        printf("msgget fail!\n");
    }

    struct msgbuf writeBuf={888,"Yinyuer is a pretty girl!"};
//    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
    msgsnd(msgId,&writeBuf,sizeof(writeBuf.mtext),0);
    printf("send over!\n");
    
    struct msgbuf readBuf;
    msgrcv(msgId,&readBuf,sizeof(readBuf),666,0);
    printf("return from msgrcv:\n%s\n",readBuf.mtext);
	
	msgctl(msgId,IPC_RMID,NULL);
    
    return 0;
}

运行结果:

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
1. 无名管道pipe)和有名管道(named pipe)都是一种进程间通信的方式,它们都是单向的,即只能一端写入数据,另一端读取数据。无名管道只适用于有亲缘关系的进程间通信,而有名管道则可以用于没有亲缘关系的进程间通信。 在使用无名管道时,我们需要调用 pipe() 函数创建管道,然后使用 fork() 函数创建子进程,将子进程的标准输出重定向到管道的写端,父进程则将管道的读端作为标准输入。这样,在父子进程之间就建立了一个管道,父进程可以通过读取管道中的数据来获取子进程的输出。 使用有名管道时,我们需要先调用 mkfifo() 函数创建管道文件,然后分别在读端和写端打开该文件,并进行读写操作。有名管道也可以用于多个进程之间的通信。 2. 消息队列(message queue)是一种进程间通信的方式,它实现了进程间的异步通信,即发送方可以将消息放入消息队列中,而不必等待接收方的响应。消息队列可以用于多个进程之间的通信。 在使用消息队列时,我们需要先调用 msgget() 函数创建消息队列,然后分别在发送方和接收方调用 msgsnd() 和 msgrcv() 函数进行消息的发送和接收。发送方需要指定消息类型和数据,接收方则可以根据消息类型来选择接收哪些消息。 3. 共享内存(shared memory)是一种进程间通信的方式,它可以让多个进程共享同一块物理内存,从而实现高效的数据交换。共享内存适用于需要高速传输大量数据的场合。 在使用共享内存时,我们需要先调用 shmget() 函数创建共享内存区域,然后使用 shmat() 函数将该内存区域映射到进程的虚拟地址空间中。多个进程可以同时访问该内存区域,可以通过修改共享内存中的数据来进行进程间通信。使用完毕后,我们需要调用 shmdt() 函数将共享内存从进程的虚拟地址空间中解除映射,最后调用 shmctl() 函数删除共享内存区域。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值