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

本文详细介绍了Linux系统中进程间通信的两种方式:管道(包括无名管道和有名管道FIFO)以及消息队列。管道提供半双工通信,适用于父子进程间通信;有名管道通过mkfifo创建,允许不相关进程间的通信。消息队列则是存储在内核中的消息链表,提供灵活的消息传递,并可通过ftok和msgget等函数进行操作。文中给出了各种示例代码,展示了如何使用这些通信机制。
摘要由CSDN通过智能技术生成

进程间通信:

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

进程间方式:

  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;
}

运行结果:

在这里插入图片描述

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值