首页 > 系统相关 >系统编程(进程通信--综合练习)

系统编程(进程通信--综合练习)

时间:2025-01-18 09:58:38浏览次数:3  
标签:练习 -- 编程 int mysem key 进程 input 共享内存

  1. 实现两个没有亲缘关系的进程之间通过共享内存实现一个小文件(小于10K)的数据拷贝。

(可申请文件大小的共享内存,一次性写入文件所有内容,读取共享内存的进程访问数据后,进行文件存储)

思路

要实现两个进程之间通过共享内存进行文件拷贝,可以按照以下步骤进行:

创建共享内存:进程A创建一个共享内存段,并将其映射到内存中,用于存储文件数据。

打开文件:进程A打开待拷贝的文件,并读取文件内容。

写入共享内存:进程A将文件内容写入到共享内存中。

打开共享内存:进程B打开之前创建的共享内存段,并将其映射到内存中。

读取共享内存:进程B从共享内存中读取数据。

存储文件:进程B将读取到的数据存储到文件中。

写入端

#include "head.h"
#define PATH "/home/st"
int main(int argc, char const *argv[])
{
    if (argc < 2)
    {
        perror("输入参数不够");
        return -1;
    }
    key_t key = ftok(PATH, 666);
    // 创建并赋读写权限
    int shmid = shmget(key, 1024 * 10, IPC_CREAT | SHM_R | SHM_W);
    if (shmid == -1)
    {
        perror("failed");
        return -1;
    }
    int fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC);
    // 内存映射
    void *p = shmat(shmid, NULL, 0);
    // 从内存中读取数据写入文件
    write(fd, p, 1024 * 10);

    sleep(10);
    shmdt(p);
    shmctl(shmid, IPC_RMID, NULL);
}

这段代码的功能是将共享内存中的数据写入文件。

代码分析如下:

引入头文件 “head.h”,该头文件应该包含了需要使用的函数和常量的声明。

使用#define宏定义了一个路径常量PATH,表示共享内存的路径。

进入main函数,首先判断命令行参数的数量。如果参数数量小于2,输出错误信息并返回-1。

使用ftok函数生成一个唯一的key,该key由共享内存的路径和一个序号确定。

使用shmget函数创建共享内存,传入key、内存大小和标志。如果创建失败,输出错误信息并返回-1。

使用open函数打开一个文件,传入参数argv[1]作为文件名,使用可写、创建和截断模式。如果打开失败,输出错误信息并返回-1。

使用shmat函数将共享内存与进程连接起来,参数为共享内存的id、连接地址和标志。函数返回一个指向共享内存的指针。

使用write函数将共享内存的数据写入文件,参数为文件描述符、写入数据的指针和要写入的字节数。这里写入的字节数为1024 * 10,即共享内存的大小。

使用sleep函数休眠10秒,等待其他进程对共享内存的操作完成。

使用shmdt函数将共享内存与进程分离。

使用shmctl函数删除共享内存,参数为共享内存的id和命令。

读取端

#include "head.h"
#define PATH "/home/st"
int main(int argc, char const *argv[])
{
    if (argc < 2)
    {
        perror("输入参数不够");
        return -1;
    }
    key_t key = ftok(PATH, 666);
    // 创建并赋读写权限
    int shmid = shmget(key, 1024 * 10, IPC_CREAT | SHM_R | SHM_W);
    if (shmid == -1)
    {
        perror("failed");
        return -1;
    }
    int fd = open(argv[1], O_RDONLY);
    // 内存映射
    void *p = shmat(shmid, NULL, 0);
    // 从内存中读取数据写入文件
    read(fd, p, 1024 * 10);

    sleep(10);
    shmdt(p);
    shmctl(shmid, IPC_RMID, NULL);
}

在这里插入图片描述

  1. 两个没有亲缘关系的进程之间通过共享内存传递文件内容(文件内容很大,可能无法申请对应大小的共享内存),需要多次写入共享内存,编程完成整个传递过程。

    提示:对共享内存进行同步访问,读写操作分别多次循环,但提前应该先写入文件总大小,以便读进程在获取文件大小的字节后退出循环。

思路

首先,我们需要创建一个共享内存区,用于存储文件内容。由于文件内容很大,可能无法申请对应大小的共享内存,我们可以分块多次写入。

确定每个块的大小,例如每个块的大小为N字节。这样,文件总大小为S字节时,需要写入共享内存的块数为ceil(S/N)。
确定每个进程的读取和写入起始位置:
写入进程从文件的起始位置开始,每次写入一个块的数据。
读取进程从共享内存的起始位置开始,每次读取一个块的数据。
具体实现过程如下:

写入进程:

打开文件,获取文件总大小S。
创建共享内存区,大小为N字节 * 块数。
将S写入共享内存的开头位置,作为文件总大小的标记。
将文件内容按块写入共享内存,直到写入完成。每次写入时,先检查当前写入位置加上块的大小是否超过了共享内存的大小,如果超过了,则只写入剩余共享内存大小的数据;否则,写入一个块的数据。
关闭文件。
读取进程:

创建共享内存区,大小为N字节 * 块数。
读取共享内存开头位置的文件总大小S,作为文件总大小的标记。
循环读取共享内存中的数据,直到读取完成或者读取的字节数等于文件总大小S。每次循环读取一个块的数据。
关闭共享内存。

写入端

#include "mysem.h"
#define PATH "/home/st"
int main(int argc, char const *argv[])
{
    if (argc < 2)
    {
        puts("参数不够");
        return -1;
    }
    key_t key = ftok(PATH, 666);
    int exit = mysem_exit(key, 2);

    int semid = mysem_create(key, 2);
    mysem_init(semid, 1, 0);
    mysem_init(semid, 0, 1);
    int shmid = shmget(key, 1024, IPC_CREAT | SHM_R | SHM_W);
    int fa = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC);

    void *p = shmat(shmid, NULL, 0);
    int i = 0;
    mysem_p(semid, 0);
    memcpy(&i, p, sizeof(int));
    mysem_v(semid, 1);

    printf("需要接收%d次\n", i);
    int n = 0;
    while (i--)
    {
        mysem_p(semid, 0);
        char buf[1024] = {0};
        memcpy(buf, p, 1024);
        write(fa, buf, strlen(buf));
        printf("read第%d次\n", ++n);
        memset(p, 0, 1024);
        mysem_v(semid, 1);
    }
    shmdt(p);
    shmctl(shmid, IPC_RMID, NULL);

    mysem_destory(semid);
    return 0;
}

这段代码的功能是从共享内存中读取数据并写入到指定文件中。

代码的主要逻辑包括:

检查命令行参数数量,如果参数数量不够,则输出错误提示并返回-1。
使用ftok函数将路径和标识符转换为一个key值。
通过mysem_exit函数获取指定key值的信号量标识符。
使用mysem_create函数创建一个新的信号量并获得其标识符。
使用mysem_init函数初始化信号量,将第一个信号量的值设为1,第二个信号量的值设为0。
使用shmget函数创建或获取共享内存,在这里指定的key值和大小为1024字节。
打开指定文件,如果文件不存在则创建,如果文件已存在则清空原有内容。
使用shmat函数将共享内存连接到当前进程的地址空间,并返回指向共享内存的指针。
使用mysem_p函数对第一个信号量进行P操作,以获取读取次数。
通过memcpy函数将共享内存中的整数值读取到变量i中。
使用mysem_v函数对第二个信号量进行V操作,以释放其他进程对共享内存的访问。
打印需要接收的次数i。
在循环中,通过mysem_p函数对第一个信号量进行P操作,以获取共享内存中的数据。
将共享内存中的数据复制到缓冲区buf中。
将缓冲区buf中的数据写入到指定文件中。
打印读取次数n。
使用memset函数将共享内存清空。
使用mysem_v函数对第二个信号量进行V操作,以释放其他进程对共享内存的访问。
使用shmdt函数将共享内存与当前进程分离。
使用shmctl函数删除共享内存。
使用mysem_destory函数销毁信号量。

读取端

#include "mysem.h"
#define PATH "/home/st"
int main(int argc, char const *argv[])
{
    if (argc < 2)
    {
        puts("参数不够");
        return -1;
    }
    key_t key = ftok(PATH, 666);
    int exit = mysem_exit(key, 2);

    int semid = mysem_create(key, 2);
    mysem_init(semid, 1, 0);
    mysem_init(semid, 0, 1);
    int shmid = shmget(key, 1024, IPC_CREAT | SHM_R | SHM_W);
    int fa = open(argv[1], O_RDONLY);
    off_t size = lseek(fa, 0, SEEK_END);
    lseek(fa, 0, SEEK_SET);
    int i = size / 1024 + 1;
    printf("循环次数为%d", i);

    void *p = shmat(shmid, NULL, 1);
    mysem_p(semid, 0);
    memcpy(&i, p, sizeof(int));
    mysem_v(semid, 0);

    printf("需要接收%d次\n", i);
    int n = 0;
    while (i--)
    {
        mysem_p(semid, 1);

        char buf[1024] = {0};
        ssize_t bytes_size = read(fa, buf, 1024);
        if (bytes_size > 0)
        {
            memcpy(p, buf, bytes_size);
        }
        printf("写第%d次\n", ++n);
        mysem_v(semid, 0);
    }
    shmdt(p);
    shmctl(shmid, IPC_RMID, NULL);

    mysem_destory(semid);
    return 0;
}

在这里插入图片描述

  1. 两个没有亲缘关系的进程之间通过消息队列实现简单的聊天。在进程在发送“bye”数据,或者接收到“bye”数据后,结束进程,完成聊天过程。

思路

实现两个没有亲缘关系的进程之间通过消息队列实现简单的聊天可以分为以下几个步骤:

创建消息队列:在操作系统中创建一个消息队列,用于进程间通信。

创建进程1和进程2:可以使用fork()函数创建两个子进程,一个作为发送消息的进程,一个作为接收消息的进程。

进程1发送消息:进程1通过消息队列将需要发送的消息发送给进程2,可以使用msgsnd()函数实现。

进程2接收消息:进程2通过消息队列接收来自进程1发送的消息,可以使用msgrcv()函数实现。

判断消息内容并结束进程:进程2在接收到消息后,判断消息的内容是否为"bye",如果是,则结束进程2;进程1也可以在发送消息前判断是否为"bye",如果是,则结束进程1。

关闭消息队列:在聊天结束后,关闭消息队列,可以使用msgctl()函数实现。

写入端

#include "head.h"

#define PATH_NAME "/home/st/msgfile"
#define MSG_TYPE 1

typedef struct
{
    long type;
    char data[100];
} msg_t;

int main(int argc, char const *argv[])
{
    key_t key = ftok(PATH_NAME, 123);
    int msgid = msgget(key, IPC_CREAT | 0666);
    if (msgid == -1)
    {
        perror("msgget failed");
        return -1;
    }

    msg_t send_msg, recv_msg;
    bool sending = false; // 初始状态为接收消息
    char input[100];

    while (1)
    {
        if (sending)
        {
            // 发送消息模式
            printf("输入要发送的消息(键入“rec”退出发送模式) ");
            fgets(input, sizeof(input), stdin);
            input[strcspn(input, "\n")] = '\0'; // 移除换行符

            strcpy(send_msg.data, input);
            send_msg.type = MSG_TYPE;

            if (msgsnd(msgid, &send_msg, sizeof(send_msg.data), 0) == -1)
            {
                perror("msgsnd failed");
                msgctl(msgid, IPC_RMID, NULL); // 清理资源
                return -1;
            }
            if (strcmp(input, "bye") == 0)
            {
                break; // 收到'bye'消息,退出聊天
            }

            if (strcmp(input, "rec") == 0)
            {
                sending = false; // 退出发送模式,进入接收模式
            }
        }
        else
        {
            // 接收消息模式
            if (msgrcv(msgid, &recv_msg, sizeof(recv_msg), MSG_TYPE, 0) == -1)
            {
                perror("msgrcv failed");
                msgctl(msgid, IPC_RMID, NULL); // 清理资源
                return -1;
            }

            printf("Received: %s\n", recv_msg.data);

            if (strcmp(recv_msg.data, "bye") == 0)
            {
                break; // 收到'bye'消息,退出聊天
            }

            // 在这里可以添加逻辑来切换到发送模式,或者保持接收模式
            // 为了简化,我们让用户通过发送'send'命令来切换到发送模式
            printf("输入 'send' 切换到发送模式,或输入任何其他键继续接收: ");
            fgets(input, sizeof(input), stdin);
            input[strcspn(input, "\n")] = '\0'; // 移除换行符

            if (strcmp(input, "send") == 0)
            {
                sending = true; // 切换到发送模式
            }
        }
    }

    msgctl(msgid, IPC_RMID, NULL); // 聊天结束,删除消息队列
    return 0;
}

这段代码是一个简单的聊天程序,使用Linux的消息队列来实现进程间通信。

主要步骤如下:

使用ftok函数生成一个键值,用于获取消息队列的ID。
调用msgget函数创建或获取消息队列,如果失败则报错并返回。
定义两个msg_t类型的变量,用于发送和接收消息。
使用一个循环来不断接收和发送消息。
如果处于发送模式,提示用户输入消息并发送给消息队列。
如果输入的消息是"bye",则退出聊天。
如果输入的消息是"rec",则切换为接收模式。
如果处于接收模式,调用msgrcv函数接收消息。
如果接收到的消息是"bye",则退出聊天。
提示用户输入"send"切换为发送模式,否则继续保持接收模式。
循环结束后,调用msgctl函数删除消息队列。

读取端

#include "head.h"

#define PATH_NAME "/home/st/msgfile"
#define MSG_TYPE 1

typedef struct
{
    long type;
    char data[100];
} msg_t;

int main(int argc, char const *argv[])
{
    key_t key = ftok(PATH_NAME, 123);
    int msgid = msgget(key, IPC_CREAT | 0666);
    if (msgid == -1)
    {
        perror("msgget failed");
        return -1;
    }

    msg_t send_msg, recv_msg;
    bool sending = false; // 初始状态为接收消息
    char input[100];

    while (1)
    {
        if (sending)
        {
            // 发送消息模式
            printf("输入要发送的消息(键入“rec”退出发送模式) ");
            fgets(input, sizeof(input), stdin);
            input[strcspn(input, "\n")] = '\0'; // 移除换行符

            strcpy(send_msg.data, input);
            send_msg.type = MSG_TYPE;

            if (msgsnd(msgid, &send_msg, sizeof(send_msg.data), 0) == -1)
            {
                perror("msgsnd failed");
                msgctl(msgid, IPC_RMID, NULL); // 清理资源
                return -1;
            }
            if (strcmp(input, "bye") == 0)
            {
                break; // 收到'bye'消息,退出聊天
            }

            if (strcmp(input, "rec") == 0)
            {
                sending = false; // 退出发送模式,进入接收模式
            }
        }
        else
        {
            // 接收消息模式
            if (msgrcv(msgid, &recv_msg, sizeof(recv_msg), MSG_TYPE, 0) == -1)
            {
                perror("msgrcv failed");
                msgctl(msgid, IPC_RMID, NULL); // 清理资源
                return -1;
            }

            printf("Received: %s\n", recv_msg.data);

            if (strcmp(recv_msg.data, "bye") == 0)
            {
                break; // 收到'bye'消息,退出聊天
            }

            // 在这里可以添加逻辑来切换到发送模式,或者保持接收模式
            // 为了简化,我们让用户通过发送'send'命令来切换到发送模式
            printf("输入 'send' 切换到发送模式,或输入任何其他键继续接收: ");
            fgets(input, sizeof(input), stdin);
            input[strcspn(input, "\n")] = '\0'; // 移除换行符

            if (strcmp(input, "send") == 0)
            {
                sending = true; // 切换到发送模式
            }
        }
    }

    msgctl(msgid, IPC_RMID, NULL); // 聊天结束,删除消息队列
    return 0;
}

在这里插入图片描述

标签:练习,--,编程,int,mysem,key,进程,input,共享内存
From: https://blog.csdn.net/weixin_69851948/article/details/145213287

相关文章

  • Notion隐藏技巧:Edu订阅解锁无限可能!
    作为一款功能强大的笔记和知识管理工具,Notion受到了越来越多人的喜爱。但你是否知道,通过Edu教育邮箱,你可以免费申请Notion的教育订阅,解锁更多高级功能?本文将带你深入了解Notion的教育订阅福利,并分享一个鲜为人知的Notion小技巧,助你提升工作效率。Edu订阅:Notion免费升级Edu订......
  • 你有用过弹性布局吗?说说你对它的理解
    当然,弹性布局(Flexbox)是前端开发中常用的一种布局方式,它提供了一种更加灵活和高效的方式来创建复杂的布局结构,特别是当你的设计不仅仅是基于简单的块级或行内文本流时。以下是我对弹性布局的理解:基本概念:弹性布局是一种CSS布局模式,它允许你设计复杂的布局结构,而无需使用浮动或定......
  • Python 进阶 - 多线程(一)
    Python进阶-多线程相关概念解释器GILthreading方法属性threading.enumerate()threading.active_count()threading.current_thread()threading.get_ident()threading.main_thread()threading.stack_size([size])threading.get_native_id()threading.TIMEOUT_MAX线程......
  • 服务器被攻击,为什么硬防不起作用?
    当服务器遭受攻击时,即使配备了硬件防护设备(如100G硬防),仍然可能出现性能下降或无法访问的情况。以下是详细的解释和解决方案:理解硬件防护的作用:硬件防护设备主要用于抵御大流量攻击(如DDoS攻击),通过清洗恶意流量来保护服务器。然而,它并不能完全消除所有类型的攻击。当攻击......
  • Android Audio基础(53)——PCM逻辑设备Write数据
    1.前言本文,我们将以回放(Playback,播放音频)为例,讲解PCMData是如何从用户空间到内核空间,最后传递到Codec硬件。在ASoC音频框架简介中,我们给出了回放(Playback)PCM数据流示意图。:对于Linux来说,由于分为userspace和kernelspace,而且两者之间数据不能随便互相访问。因此用......
  • 【ESP 乐鑫相关】ESP32-S3启动流程
    转载自:https://blog.itpub.net/70040860/viewspace-3053923/ESP32-S3启动流程    本文将会介绍ESP32-S3从上电到运行app_main函数中间所经历的步骤(即启动流程)。从宏观上,该启动流程可分为如下3个步骤。    ①:一级引导程序,它被固化在ESP32-S3内部的ROM中,它会从flas......
  • 毕业论文-基于Android的个性化推荐外卖点餐APP系统设计与实现
    内容涵盖详细视频演示文章底部名片,联系我获取更详细的演示视频系统演示截图技术框架后端框架支持:Java(SpringBoot)、Python、PHP、ASP等都可以作为后端支持,具体需求PHP:PHP是一种广泛应用于Web开发的服务器端脚本语言,因其简单易学和强大的生态系统而受到许多开发......
  • 你上家公司有写日报、周报或者月报吗?说说你对写日(周、月)这事的理解
    在我之前的公司,我们确实有写日报、周报和月报的习惯。这些报告不仅用于记录我们的工作进展,还是与团队成员和上级进行沟通的重要工具。以下是我对写日(周、月)报这件事的理解,特别是在前端开发领域:一、日报日报主要用于记录当天的工作内容和遇到的问题。对于前端开发者来说,日报可以......
  • 毕业论文-基于Android的智能考勤系统设计与实现
    内容涵盖详细视频演示文章底部名片,联系我获取更详细的演示视频系统演示截图技术框架后端框架支持:Java(SpringBoot)、Python、PHP、ASP等都可以作为后端支持,具体需求PHP:PHP是一种广泛应用于Web开发的服务器端脚本语言,因其简单易学和强大的生态系统而受到许多开发......
  • 你有使用过figure标签吗?说说你对它的认识,有哪些应用场景?
    对figure标签的认识:figure标签是HTML5中引入的一个新标签,它用于表示网页中一块独立的内容,这块内容可以是图像、图表、照片、代码等。该标签的一个重要特点是,将其从网页上移除后,不会对网页上的其他内容产生影响。此外,figure标签还可以配合figcaption标签使用,为媒体内容添加标题或......