进程管道

本文详细介绍了Linux下的进程间通信(IPC)机制,包括管道、有名管道、信号量、消息队列、信号、共享内存及套接字等。每种机制的特点、应用场景及基本使用方法均有涉及。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Read:

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


#define PATHtang "./tang.txt"
#define PATHsu "./su.txt"


int main()
{
int ret1 ,fd1 ;
char buf1[100] = {0};
ret1 = mkfifo(PATHsu , O_CREAT | O_EXCL | 0666);
if(-1 == ret1 )
{
perror("mkfifo");
exit(1);
}


fd1 = open(PATHsu , O_RDONLY);
if(-1 == fd1)
{
perror("open");
exit(2);
}


while(1)
{
ret1 = read(fd1 , buf1 , sizeof(buf));
if(-1 == ret1)
{
perror("read");
exit(3);
}
printf("Read From suTxt :%s\n",buf1);
if(!strncmp(buf , "end" ,3))
{
break;
}
memset(buf , 0 , sizeof(buf));
}
close(fd1);
unlink(PATHsu;
////////////////////////
int ret2 , fd2;
char buf[100] = {0};
fd2 = open(PATHtang , O_WRONLY);
if(-1 == fd2)
{
perror("open");
exit(1);
}


while(1)
{
scanf("%s",buf);
ret2 = write(fd2 , buf , strlen(buf));
if(-1 == ret2)
{
perror("write");
exit(2);
}
if(!strncmp(buf , "end" , 3))
{
break;
}
memset(buf , 0 ,sizeof(buf));
}
close(fd2);
return 0;
}

Write:

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


#define PATHtang "./tang.txt"
#define PATHsu "./su.txt"


int main()
{
int ret2 , fd1;
char buf[100] = {0};
fd = open(PATHsu , O_WRONLY);
if(-1 == fd1)
{
perror("open");
exit(1);
}


while(1)
{
scanf("%s",buf);
ret = write(fd1 , buf , strlen(buf));
if(-1 == ret)
{
perror("write");
exit(2);
}
if(!strncmp(buf , "end" , 3))
{
break;
}
memset(buf , 0 ,sizeof(buf));
}
close(fd1);
///////////////////
int ret2 ,fd2 ;
char buf[100] = {0};
ret2 = mkfifo(PATH , O_CREAT | O_EXCL | 0666);
if(-1 == ret2 )
{
perror("mkfifo");
exit(1);
}


fd = open(PATHtang , O_RDONLY);
if(-1 == fd2)
{
perror("open");
exit(2);
}


while(1)
{
ret2 = read(fd2 , buf , sizeof(buf));
if(-1 == ret2)
{
perror("read");
exit(3);
}
printf("Read From suTxt :%s\n",buf);
if(!strncmp(buf , "end" ,3))
{
break;
}
memset(buf , 0 , sizeof(buf));
}
close(fd2);
unlink(PATH);
return 0;
}

msgsnd:

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


#define key 1234


struct msgbuf
{
long mtype;
char mbuf[100];
};


int main()
{
int msgid,ret;
struct msgbuf buf;
msgid = msgget(key , IPC_CREAT | IPC_EXCL);
if(-1 == msgid)
{
perror("msgget");
exit(1);
}


while(1)
{
scanf("%s",buf.mbuf);
buf.mtype = 1;


ret = msgsnd(msgid , &buf , sizeof(buf.mbuf), 0);
if(-1 == ret)
{
perror("msgsnd");
exit(2);
}
if(!strncmp(buf.mbuf , "end" , 3))
{
break;
}
memset(buf.mbuf , 0 , sizeof(buf.mbuf));
}


msgctl(msgid , IPC_RMID , NULL);
return 0;
}

1. Linux下进程间通信方式有有那些?

1.1管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。

 

1.2有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

 

1.3信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

 

1.4消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

 

1.5信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

 

1.6共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。

 

1.7套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。

 

 

 

2.何为管道?

管道是进程间通信中最古老的方式,它包括无名管道有名管道两种,前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信。

 

3.如何创建一个无名管道?

无名管道由pipe()函数创建:

#include <unistd.h>

int pipe(int filedis[2])

参数filedis返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。

1. 何为消息队列?

 

消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法。  每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构。我们可以通过发送消息来避免命名管道的同步和阻塞问题。

但是消息队列与命名管道一样,每个数据块都有一个最大长度的限制。

 

2. 消息队列的该如何应用?

 

消息队列是链表队列,它通过内核提供一个struct msqid_ds *msgque[MSGMNI]向量维护内核的一个消息队列列表,因此linux系统支持的最大消息队列数由msgque数组大小来决定,每一个msqid_ds表示一个消息队列,并通过msqid_ds.msg_firstmsg_last维护一个先进先出的msg链表队列,当发送一个消息到该消息队列时,把发送的消息构造成一个msg结构对象,并添加到msqid_ds.msg_firstmsg_last维护的链表队列,同样,接收消息的时候也是从msg链表队列尾部查找到一个msg_type匹配的msg节点,从链表队列中删除该msg节点,并修改msqid_ds结构对象的数据。

 

3. 消息队列的结构定义:

 

struct msqid_ds *msgque[MSGMNI]向量:

  msgque[MSGMNI]是一个msqid_ds结构的指针数组,每个msqid_ds结构指针代表一个系统消息队列,msgque[MSGMNI]的大小为MSGMNI=128,也就是说系统最多有MSGMNI=128个消息队列。

 

4. 如何创建和使用一个消息队列?

 

int msgget(key_t, key, int msgflg);  

msgflg是一个权限标志,表示消息队列的访问权限,它与文件的访问权限一样。msgflg可以与IPC_CREAT做或操作,表示当key所命名的消息队列不存在时创建一个消息队列,如果key所命名的消息队列存在时,IPC_CREAT标志会被忽略,而只返回一个标识符。

 

 

5. 如何将消息添加到消息队列中?

int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);  

 

msgid是由msgget函数返回的消息队列标识符。

msg_ptr是一个指向准备发送消息的指针,但是消息的数据结构却有一定的要求,指针msg_ptr所指向的消息结构一定要是以一个长整型成员变量开始的结构体,接收函数将用这个成员来确定消息的类型。所以消息结构要定义成这样:

struct my_message{  

    long int message_type;  

    /* The data you wish to transfer*/  

};

msg_szmsg_ptr指向的消息的长度,注意是消息的长度,而不是整个结构体的长度,也就是说msg_sz是不包括长整型消息类型成员变量的长度。

msgflg用于控制当前消息队列满或队列消息到达系统范围的限制时将要发生的事情。

如果调用成功,消息数据的一分副本将被放到消息队列中,并返回0,失败时返回-1.

 

 

6. 如何从一个消息队列中获取消息?

int msgrcv(int msgid, void *msg_ptr, size_t msg_st, long int msgtype, int msgflg);

msgid, msg_ptr, msg_st的作用也函数msgsnd函数的一样。

msgtype可以实现一种简单的接收优先级。如果msgtype0,就获取队列中的第一个消息。如果它的值大于零,将获取具有相同消息类型的第一个信息。如果它小于零,就获取类型等于或小于msgtype的绝对值的第一个消息。

msgflg用于控制当队列中没有相应类型的消息可以接收时将发生的事情。

调用成功时,该函数返回放到接收缓存区中的字节数,消息被复制到由msg_ptr指向的用户分配的缓存区中,然后删除消息队列中的对应消息。失败时返回-1.

 

7.  共享内存

共享内存是运行在同一台机器上的进程间通信最快的方式,因为数据不需要在不同的进程间复制。通常由一个进程创建一块共享内存区,其余进程对这块内存区进行读写。

得到共享内存有两种方式:映射/dev/mem设备和内存映像文件。前一种方式不给系统带来额外的开销,但在现实中并不常用,因为它控制存取的将是实际的物理内存,在Linux系统下,这只有通过限制Linux系统存取的内存才可以做到,这当然不太实际。常用的方式是通过shmXXX函数族来实现利 用共享内存进行存储的。

  

 

8. 函数是shmget有什么作用?

函数:int shmget(key_t key, int size, int flag);

这个函数有点类似大家熟悉的malloc函数,系统按照请求分配size大小的内存用作共享内存。Linux系统内核中每个IPC结构都有的一个非负整数 的标识符,这样对一个消息队列发送消息时只要引用标识符就可以了。这个标识符是内核由IPC结构的关键字得到的,这个关键字,就是上面第一个函数的key。数据类型key_t是在头文件sys/types.h中定义的,它是一个长整形的数据。在我们后面的章节中,还会碰到这个关键字。

所以,函数shmget,它的作用是可以获得一个共享存储标识符。

 

9.当共享内存创建后,其余进程该如何将共享存储标识符接到自身的地址空间中?

当共享内存创建后,其余进程可以调用shmat()将其连接到自身的地址空间中。

  

void *shmat(int shmid, void *addr, int flag);

  

shmidshmget函数返回的共享存储标识符,addrflag参数决定了以什么方式来确定连接的地址,函数的返回值即是该进程数据段所连接的实际地址,进程可以对此进程进行读写操作。

使用共享存储来实现进程间通信的注意点是对数据存取的同步,必须确保当一个进程去读取数据时,它所想要的数据已经写好了。通常,信号量被要来实现对共享存储数据存取的同步,另外,可以通过使用shmctl函数设置共享存储内存的某些标志位如SHM_LOCKSHM_UNLOCK等来实现。

 

10. 何为信号量?

  信号量又称为信号灯,它是用来协调不同进程间的数据对象的,而最主要的应用是前一节的共享内存方式的进程间通信。本质上,信号量是一个计数器,它用来记录对某个资源(如共享内存)的存取状况。

 

11.一般说来,为了获得共享资源,进程可以进行那些操作?

  (1) 测试控制该资源的信号量。

  (2) 若此信号量的值为正,则允许进行使用该资源。进程将信号量减1

  (3) 若此信号量为0,则该资源目前不可用,进程进入睡眠状态,直至信号量值大于0,进程被唤醒,转入步骤(1)。

  (4) 当进程不再使用一个信号量控制的资源时,信号量值加1。如果此时有进程正在睡眠等待此信号量,则唤醒此进程。

 

12.何为套接口?

套接口(socket)编程是实现Linux系统和其他大多数操作系统中进程间通信的主要方式之一。我们熟知的WWW服务、FTP服务、TELNET服务 等都是基于套接口编程来实现的。除了在异地的计算机进程间以外,套接口同样适用于本地同一台计算机内部的进程间通信。

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值