进程间的7种通信方式(含例程代码)

下面的实验由我和我的同学完成的,这是操作系统课程的一个小实验,可以帮助理解进程间的通信。

进程间的7种通信方式如下:

  1. 管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
  2. 命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
  3. 消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
  4. 共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
  5. 信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
  6. 套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
  7. 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。

1.匿名管道

见链接https://blog.csdn.net/qq_38048756/article/details/109207767

2.命名管道

特点:
有名管道fifo解决了pipe只能有关系的进程才能通信的问题
实现一个有名管道实际上就是实现一个FIFO文件,有名管道一旦建立,之后它的读,以及关闭操作都与普通管道完全相同。虽然FIFO文件的inode节点在磁盘上,但仅是一个节点而已,文件的数据还是存在内核缓冲页面上,和普通管道相同。
有名管道的文件仅仅是作为传输数据的通道,它并不存放传输的数据。
mkfifo
可以通过命令行的形式创建匿名管道:
mkfifo myPipe
echo “hello ” > myPipe
cat < myPipe
使用函数创建:
int mkfifo(const char * pathname,mode_t mode);

程序源码

//读进程
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define FIFO_PATH "myfifofile"
int main()
{
	int fd;
	char cont_r[255];
#创建命名管道
	if(mkfifo(FIFO_PATH,0666)<0 && errno!=EEXIST)
	{
	
		perror("create fifo failed");
		return -1;

	}else
	{
	  	printf("create fifo success\n");
    #打开文件进行读操作
		fd =open(FIFO_PATH,O_CREAT|O_RDONLY,0666);
		if(fd>0)
		{
			while(1){
				read(fd,cont_r,255);
				printf("read:%s\n",cont_r);
			}
			close(fd);
		}else
			perror("open failed");

	}
	return 0;
}
//写进程
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define FIFO_PATH "myfifofile"
int main()
{
        int fd;
        char cont_w[] = "hello sundy";

        if(mkfifo(FIFO_PATH,0666)<0 && errno!=EEXIST)
        {

                perror("create fifo failed");
                return -1;

        }else
        {
                printf("create fifo success\n");

                fd =open(FIFO_PATH,O_CREAT|O_WRONLY,0666);
                if(fd>0)
                {
                        while(1){
                                write(fd,cont_w,strlen(cont_w));
                                printf("write success\n");
				                   sleep(2);
                        }
                        close(fd);
                }else
                        perror("open failed");

        }
        return 0;
}

编译运行
打开两个终端
在这里插入图片描述
在这里插入图片描述

3.消息队列

特点
由于管道不适合进程间频繁地交换数据,消息队列则可以解决这个问题。A进程要给B进程发送消息,A进程把数据放到对应的消息队列之后就可以正常返回了,B进程需要的时候再去读取数据就可以。
如果没有释放消息队列或者关闭操作系统,消息队列会一直存在,而匿名管道是随进程的创建而建立,随进程的结束而销毁。
消息队列本质上是位于内核空间的链表,链表的每个节点都是一条消息。每一条消息都有自己的消息类型,消息类型用整数来表示,而且必须大于 0。每种类型的消息都被对应的链表所维护。
消息队列不适合比较大的数据的传输。

相关函数
在这里插入图片描述

消息数据格式:
在这里插入图片描述

程序源码

// 写进程 
#include <stdio.h> 
#include <sys/ipc.h> 
#include <sys/msg.h> 
  
// 消息队列数据结构
struct mesg_buffer { 
    long mesg_type; 
    char mesg_text[100]; 
} message; 
  
int main() 
{ 
    key_t key; 
    int msgid; 
  
    // ftok to generate unique key 
    key = ftok("progfile", 65); 
  
    // msgget creates a message queue 
    // and returns identifier 
    msgid = msgget(key, 0666 | IPC_CREAT); 
    message.mesg_type = 1; 
  
    printf("Write Data : "); 
    gets(message.mesg_text); 
  
    // msgsnd to send message 
    msgsnd(msgid, &message, sizeof(message), 0); 
  
    // display the message 
    printf("Data send is : %s \n", message.mesg_text); 
  
    return 0; 
}
// 读进程
#include <stdio.h> 
#include <sys/ipc.h> 
#include <sys/msg.h> 
  
// structure for message queue 
struct mesg_buffer { 
    long mesg_type; 
    char mesg_text[100]; 
} message; 
  
int main() 
{ 
    key_t key; 
    int msgid; 
  
    // ftok to generate unique key 
    key = ftok("progfile", 65); 
  
    // msgget creates a message queue 
    // and returns identifier 
    msgid = msgget(key, 0666 | IPC_CREAT); 
  
    // msgrcv to receive message 
    msgrcv(msgid, &message, sizeof(message), 1, 0); 
  
    // display the message 
    printf("Data Received is : %s \n",  
                    message.mesg_text); 
  
    // to destroy the message queue 
    msgctl(msgid, IPC_RMID, NULL); 
  
    return 0; 
}

编译运行
在这里插入图片描述
在这里插入图片描述
读进程会先运行会发生阻塞,等待写进程发送数据

4.共享内存

特点:
消息队列的读取和写入的过程,都会发生用户态与内核态之间的消息拷贝过程。共享内存很好的解决了这一问题。
每个进程都会维护一个从内存地址到虚拟内存页面之间的映射关系。尽管每个进程都有自己的内存地址,不同的进程可以同时将同一个内存页面映射到自己的地址空间中,从而达到共享内存的目的。
所有进程共享同一块内存,共享内存在各种进程间通信方式中具有最高的效率。访问共享内存区域和访问进程独有的内存区域一样快,并不需要通过系统调用或者其它需要切入内核的过程来完成。同时它也避免了对数据的各种不必要的复制。
共享内存的几乎可以认为没有上限,它也是不局限与父子进程,采用跟消息队列类似的定位方式,因为内存是共享的,不存在任何单向的限制,最大的问题就是需要应用程序自己做互斥。

相关函数
在这里插入图片描述
shmget:申请共享内存
shmat:建立用户进程空间到共享内存的映射
shmdt:解除映射关系
shmctl:回收共享内存空间

程序源码

//写进程
#include <sys/ipc.h> 
#include <sys/shm.h> 
#include <stdio.h> 
  
int main() 
{ 
    // ftok to generate unique key 
    key_t key = ftok("shmfile",65); 
  
    // shmget returns an identifier in shmid 
    int shmid = shmget(key,1024,0666|IPC_CREAT); 
  
    // shmat to attach to shared memory 
    char *str = (char*) shmat(shmid,(void*)0,0); 
  
    gets(str); 
  
    printf("Data written in memory: %s\n",str); 
      
    //detach from shared memory  
    shmdt(str); 
  
    return 0; 
}
//读进程
#include <sys/ipc.h> 
#include <sys/shm.h> 
#include <stdio.h> 

  
int main() 
{ 
    // ftok to generate unique key 
    key_t key = ftok("shmfile",65); 
  
    // shmget returns an identifier in shmid 
    int shmid = shmget(key,1024,0666|IPC_CREAT); 
  
    // shmat to attach to shared memory 
    char *str = (char*) shmat(shmid,(void*)0,0); 
  
    printf("Data read from memory: %s\n",str); 
      
    //detach from shared memory  
    shmdt(str); 
    
    // destroy the shared memory 
    shmctl(shmid,IPC_RMID,NULL); 
     
    return 0; 
}

编译运行
在这里插入图片描述
在这里插入图片描述
先读的话读进程并不会发生阻塞,等待写进程。

5.信号

见链接https://blog.csdn.net/qq_38048756/article/details/109207813

6.信号量

见链接https://blog.csdn.net/qq_38048756/article/details/109207586

7.socket

socket即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。也因为这样,套接字明确地将客户端和服务器区分开来。
在这里插入图片描述

程序源码

 //服务器
    #include <unistd.h>  
    #include <sys/types.h>  
    #include <sys/socket.h>  
    #include <netinet/in.h>  
    #include <signal.h>  
    #include <stdio.h>  
    #include <stdlib.h>  
     int main()  
    {  
        int server_sockfd = -1;  
        int client_sockfd = -1;  
        int client_len = 0;  
        struct sockaddr_in server_addr;  
        struct sockaddr_in client_addr;  
        //创建流套接字  
        server_sockfd = socket(AF_INET, SOCK_STREAM, 0);  
        //设置服务器接收的连接地址和监听的端口  
        server_addr.sin_family = AF_INET;//指定网络套接字  
        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//接受所有IP地址的连接  
        server_addr.sin_port = htons(9736);//绑定到9736端口  
        //绑定(命名)套接字  
        bind(server_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));  
        //创建套接字队列,监听套接字  
        listen(server_sockfd, 5);  
        //忽略子进程停止或退出信号  
        signal(SIGCHLD, SIG_IGN);  
          
        while(1)  
        {  
            char ch = '\0';  
            client_len = sizeof(client_addr);  
            printf("Server waiting\n");  
            //接受连接,创建新的套接字  
            client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_addr, &client_len);  
      
            if(fork() == 0)  
            {  
                //子进程中,读取客户端发过来的信息,处理信息,再发送给客户端  
                read(client_sockfd, &ch, 1);  
                sleep(5);  
                ch++;  
                write(client_sockfd, &ch, 1);  
                close(client_sockfd);  
                exit(0);  
            }  
            else  
            {  
                //父进程中,关闭套接字  
                close(client_sockfd);  
            }  
        }  
    }  
 //客户端
    #include <unistd.h>  
    #include <sys/types.h>  
    #include <sys/socket.h>  
    #include <netinet/in.h>  
    #include <arpa/inet.h>  
    #include <stdio.h>  
    #include <stdlib.h>  
      
    int main()  
    {  
        int sockfd = -1;  
        int len = 0;  
        struct sockaddr_in address;  
        int result;  
        char ch = 'A';  
        //创建流套接字  
        sockfd = socket(AF_INET, SOCK_STREAM, 0);  
        //设置要连接的服务器的信息  
        address.sin_family = AF_INET;//使用网络套接字  
        address.sin_addr.s_addr = inet_addr("127.0.0.1");//服务器地址  
        address.sin_port = htons(9736);//服务器所监听的端口  
        len = sizeof(address);  
        //连接到服务器  
        result = connect(sockfd, (struct sockaddr*)&address, len);  
      
        if(result == -1)  
        {  
            perror("ops:client\n");  
            exit(1);  
        }  
        //发送请求给服务器  
        write(sockfd, &ch, 1);  
        //从服务器获取数据  
        read(sockfd, &ch, 1);  
        printf("char form server = %c\n", ch);  
        close(sockfd);  
        exit(0);  
    }  

编译运行
在这里插入图片描述

  • 7
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,以下是一个使用QT共享内存方式进程通信例程: 首先,在发送进程,我们需要创建一个共享内存对象,并将数据写入共享内存: ```cpp #include <QSharedMemory> #include <QBuffer> // 创建共享内存对象 QSharedMemory sharedMemory("my_shared_memory"); // 打开共享内存 if (!sharedMemory.create(sizeof(int))) { qDebug() << "Cannot create shared memory segment."; return; } // 将数据写入共享内存 QBuffer buffer; QDataStream out(&buffer); out << static_cast<int>(42); const QByteArray data = buffer.data(); memcpy(sharedMemory.data(), data.constData(), qMin(sharedMemory.size(), data.size())); ``` 然后,在接收进程,我们需要打开共享内存,并从共享内存读取数据: ```cpp #include <QSharedMemory> #include <QBuffer> // 创建共享内存对象 QSharedMemory sharedMemory("my_shared_memory"); // 打开共享内存 if (!sharedMemory.attach()) { qDebug() << "Cannot attach shared memory segment."; return; } // 从共享内存读取数据 QBuffer buffer; const char *data = static_cast<const char*>(sharedMemory.constData()); buffer.setData(data, sharedMemory.size()); int value; QDataStream in(&buffer); in >> value; qDebug() << "Received value:" << value; ``` 以上例程,我们创建了一个名为“my_shared_memory”的共享内存对象,然后在发送进程将一个整数值写入共享内存,接着在接收进程打开共享内存,并从读取整数值并输出。 需要注意的是,共享内存方式进程通信需要保证多个进程都能够访问同一个共享内存对象,因此需要使用相同的共享内存名称。此外,需要在访问共享内存时行同步控制,以避免数据竞争问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱学习的贝塔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值