Linux下,使用c语言实现共享内存的文件传输(文件拷贝)

4 篇文章 0 订阅
1 篇文章 0 订阅

目录

1、什么是共享内存?

2、实现思路

3、upload和download代码

3.1、头文件

3.2、upload

3.3、download

4、运行结果


1、什么是共享内存?

共享内存本质上就是内存中的一块区域,用于进程间通信使用。所有访问这块空间的程序都能访问改数据。

2、实现思路

3、upload和download代码

3.1、头文件

#ifndef __COMMON_H__
#define __COMMON_H__

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

#define IPC_PATH "./"  // IPC路径
#define IPC_MASK 1     // IPC标识
#define MSG_LEN 256    // 消息长度
#define PAGE_SIZE 4096 // 一页的大小,共享内存设置大小时,最好以当前系统的整数页好



typedef struct demo_inf
{
    int shm_id;      // 共享内存id
    char *shm_p;     // 共享内存地址
    char *file_path; // upload file path
    long other_id;   // download id
} SDI, *P_SDI;


#endif

3.2、upload

#include "common.h"

P_SDI Upload_Init(char *file_path);
int Running_upload(P_SDI p_sdi);
void get_file_size(int signum);
int Upload_Free(P_SDI p_sdi);

int Upload_Free(P_SDI p_sdi)
{
    free(p_sdi);
    printf("free 成功!\n");
    return 0;
}

void get_file_size(int signum)
{
    printf("get_file_size signal\n");
}

void file_upload_continue(int signum);

void file_upload_continue(int signum)
{
    if (signum == SIGUSR2)
    {
        printf("file download continue!\n");
    }
    if (signum == SIGALRM)
    {
        printf("the last download\n");
    }
}

P_SDI Upload_Init(char *file_path)
{
    if (file_path == (char *)NULL)
    {
        printf("will upload file path is NULL..\n");
        return (P_SDI)-1;
    }

    P_SDI p_sdi = (P_SDI)malloc(sizeof(SDI));
    if (p_sdi == (P_SDI)NULL)
    {
        perror("malloc error");
        return (P_SDI)-1;
    }

    // 创建Key值
    key_t key = ftok(IPC_PATH, IPC_MASK);
    if (key == -1)
    {
        perror("ftok error");
        return (P_SDI)-1;
    }
    else
    {
        printf("key = %d\n", key);
    }

    p_sdi->file_path = file_path;

    printf("请输入接收方的PID : \n");
    scanf("%ld", &p_sdi->other_id);

    // 创建共享内存ID
    if ((p_sdi->shm_id = shmget(key, PAGE_SIZE, IPC_CREAT | 0666)) == -1)
    {
        perror("shmget error");
        return (P_SDI)-1;
    }

    // 共享内存的映射
    if ((p_sdi->shm_p = (char *)shmat(p_sdi->shm_id, NULL, 0)) == (char *)-1)
    {
        perror("shmat error");
        return (P_SDI)-1;
    }

    if (signal(SIGUSR1, get_file_size) == SIG_ERR)
    {
        perror("signal file_upload_continue error");
        return (P_SDI)-1;
    }
    if (signal(SIGUSR2, file_upload_continue) == SIG_ERR)
    {
        perror("signal file_upload_continue error");
        return (P_SDI)-1;
    }
    if (signal(SIGALRM, file_upload_continue) == SIG_ERR)
    {
        perror("signal file_upload_continue error");
        return (P_SDI)-1;
    }
    return p_sdi;
}

int Running_upload(P_SDI p_sdi)
{
    // 先计算文件的大小
    struct stat sb;
    memset(&sb, 0, sizeof(sb));
    stat(p_sdi->file_path, &sb);

    // 把文件大小数据先传给对面
    long file_size = sb.st_size;

    printf("file_size = %ld\n", file_size);

    char file_size_str[MSG_LEN];
    memset(file_size_str, 0, sizeof(char) * MSG_LEN);
    memset(p_sdi->shm_p, 0, PAGE_SIZE);
    sprintf(file_size_str, "%ld", file_size);
    memcpy(p_sdi->shm_p, file_size_str, strlen(file_size_str));

    // 先暂停,等对面准备接收了再发数据
    pause();
    // 给download发送定时器信号,让对面接收数据大小
    usleep(30000);
    if (kill(p_sdi->other_id, SIGUSR1) == -1)
    {
        perror("kill error");
        return -1;
    }

    // 根据文件大小判断要传几次
    int fd = open(p_sdi->file_path, O_RDONLY);
    if (fd == -1)
    {
        perror("open error");
        return -1;
    }
    char file_date[PAGE_SIZE];
    int i;
    pause();

    for (i = 0; i < (int)(file_size / PAGE_SIZE); i++)
    {
        memset(file_date, 0, sizeof(char) * PAGE_SIZE);
        memset(p_sdi->shm_p, 0, PAGE_SIZE);
        int read_ret = read(fd, file_date, PAGE_SIZE);
        if (read_ret == -1)
        {
            perror("read error");
            return -1;
        }
        else
        {
            printf("read_ret = %d\n", read_ret);
        }

        // 将数据放到共享内存中
        memcpy(p_sdi->shm_p, file_date, read_ret);

        // 发信号给download,让它下载
        usleep(30000);
        if (kill(p_sdi->other_id, SIGUSR2) == -1)
        {
            perror("kill error");
            return -1;
        }
        pause(); // 接收信号继续下载
    }

    // 发送最后一次
    if ((file_size / PAGE_SIZE) != 0)
    {
        int read_ret = read(fd, file_date, file_size % PAGE_SIZE);
        if (read_ret == -1)
        {
            perror("read error");
            return -1;
        }
        else
        {
            printf("read_ret = %d, 剩余:%d\n", read_ret, (int)(file_size % PAGE_SIZE));
        }
        // 将数据放到共享内存中
        memset(p_sdi->shm_p, 0, PAGE_SIZE);
        memcpy(p_sdi->shm_p, file_date, file_size % PAGE_SIZE);

        // 发信号给download,让它下载,且是最后一次
        usleep(30000);
        if (kill(p_sdi->other_id, SIGUSR2) == -1)
        {
            perror("kill error");
            return -1;
        }
        
        pause();
        usleep(30000);
        if (kill(p_sdi->other_id, SIGALRM) == -1)
        {
            perror("kill error");
            return -1;
        }
    }

    return 0;
}

// ./upload upload_file_path
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        printf("输入参数有误!\n");
        return -1;
    }
    printf("upload pid is %ld \n", (long)getpid());

    P_SDI p_sdi = Upload_Init(argv[1]);
    if (p_sdi == (P_SDI)-1)
    {
        printf("Upload process init error");
        return -1;
    }

    if (Running_upload(p_sdi) == -1)
    {
        printf("uplaod running error\n");
        return -1;
    }

    if (Upload_Free(p_sdi) == -1)
    {
        perror("Upload Free error...\n");
        return -1;
    }
    return 0;
}

3.3、download

#include "common.h"

P_SDI Download_Init(char *file_path, key_t key);
int Running_Download(P_SDI p_sdi);
void file_download_continue(int signum);
void get_file_size(int signum);
int Download_Free(P_SDI p_sdi);

int Download_Free(P_SDI p_sdi)
{
    // 解除映射
    if (shmdt(p_sdi->shm_p) == -1)
    {
        perror("p_sdi->shm_p error");
        return -1;
    }
    // 删除映射
    if (shmctl(p_sdi->shm_id, IPC_RMID, NULL) == -1)
    {
        perror("shmctl error...\n");
        return -1;
    }
    printf("解除并删除共享内存成功\n");
    return 0;
}

void get_file_size(int signum)
{
    printf("get_file_size signal\n");
}

void file_download_continue(int signum)
{
    if (signum == SIGUSR2)
    {
        printf("file download continue!\n");
    }
    if (signum == SIGALRM)
    {
        printf("the last download\n");
    }
}

P_SDI Download_Init(char *file_path, key_t key)
{
    if (file_path == (char *)NULL)
    {
        printf("will upload file path is NULL..\n");
        return (P_SDI)-1;
    }

    P_SDI p_sdi = (P_SDI)malloc(sizeof(SDI));
    if (p_sdi == (P_SDI)NULL)
    {
        perror("malloc error");
        return (P_SDI)-1;
    }

    p_sdi->file_path = file_path;
    printf("请输入发送方的PID : \n");
    scanf("%ld", &p_sdi->other_id);

    // 创建共享内存ID
    if ((p_sdi->shm_id = shmget(key, PAGE_SIZE, IPC_CREAT | 0666)) == -1)
    {
        perror("shmget error");
        return (P_SDI)-1;
    }

    // 共享内存的映射
    if ((p_sdi->shm_p = (char *)shmat(p_sdi->shm_id, NULL, 0)) == (char *)-1)
    {
        perror("shmat error");
        return (P_SDI)-1;
    }

    if (signal(SIGUSR1, get_file_size) == SIG_ERR)
    {
        perror("signal get_file_size error");
        return (P_SDI)-1;
    }
    if (signal(SIGUSR2, file_download_continue) == SIG_ERR)
    {
        perror("signal file_download_continue error");
        return (P_SDI)-1;
    }
    if (signal(SIGALRM, file_download_continue) == SIG_ERR)
    {
        perror("signal file_download_continue error");
        return (P_SDI)-1;
    }

    return p_sdi;
}

int Running_Download(P_SDI p_sdi)
{
    usleep(30000);
    if (kill(p_sdi->other_id, SIGUSR1) == -1)
    {
        perror("kill error");
        return -1;
    }
    // 先获取文件大小
    pause();

    char file_size_str[MSG_LEN];
    memset(file_size_str, 0, sizeof(char) * MSG_LEN);
    memcpy(file_size_str, p_sdi->shm_p, MSG_LEN);
    long file_size = atol(file_size_str);
    printf("file_size = %ld\n", file_size);

    umask(0000);
    int fd = open(p_sdi->file_path, O_WRONLY | O_CREAT, 0777);
    if (fd == -1)
    {
        perror("open error");
        return -1;
    }

    // 循环读数据
    usleep(30000);
    if (kill(p_sdi->other_id, SIGUSR2) == -1)
    {
        perror("kill error");
    }
    char file_date[PAGE_SIZE];
    int i;
    for (i = 0; i < (int)(file_size / PAGE_SIZE); i++)
    {
        pause();
        memset(file_date, 0, sizeof(char) * PAGE_SIZE);
        memcpy(file_date, p_sdi->shm_p, PAGE_SIZE);
        printf("sizeof(file_date) = %ld\n", sizeof(file_date));
        int write_ret = write(fd, file_date, sizeof(file_date));
        if (write_ret == -1)
        {
            perror("read error");
            return -1;
        }
        else
        {
            printf("write_ret = %d\n", write_ret);
        }
        // 发信号给upload,让它继续上传
        usleep(30000);
        if (kill(p_sdi->other_id, SIGUSR2) == -1)
        {
            perror("kill errror");
            return -1;
        }
    }

    // 接收最后一次
    if ((file_size / PAGE_SIZE) != 0)
    {
        pause();
        memset(file_date, 0, sizeof(char) * PAGE_SIZE);

        memcpy(file_date, p_sdi->shm_p, file_size % PAGE_SIZE);
        int last_write_ret = write(fd, file_date, file_size % PAGE_SIZE);
        if (last_write_ret == -1)
        {
            perror("read error");
            return -1;
        }
        else
        {
            printf("last_write_ret = %d\n", last_write_ret);
        }
        // 发信号给uoload,表示下完了
        usleep(2000);
        if (kill(p_sdi->other_id, SIGALRM) == -1)
        {
            perror("kill error");
            return -1;
        }
        else
        {
            printf("发送定时器信号成功\n");
        }
        pause();
    }
    return 0;
}

// ./download download_file_path  key
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        printf("输入参数有误!\n");
        return -1;
    }
    printf("download pid is %ld \n", (long)getpid());

    P_SDI p_sdi = Download_Init(argv[1], atoi(argv[2]));
    if (p_sdi == (P_SDI)-1)
    {
        printf("Download process init error");
        return -1;
    }

    if (Running_Download(p_sdi) == -1)
    {
        printf("uplaod running error\n");
        return -1;
    }

    if (Download_Free(p_sdi) == -1)
    {
        perror("Download_Free error...\n");
        return -1;
    }
    return 0;
}

4、运行结果

  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 在C语言实现共享内存需要使用操作系统提供的系统调用来创建和访问共享内存。 具体来说,需要使用以下步骤来实现共享内存: 1. 使用shmget()系统调用创建共享内存段。shmget()函数需要传入三个参数:共享内存的key,共享内存大小,以及访问权限。如果共享内存段已经存在,则可以使用shmget()函数获取共享内存段的ID。 2. 使用shmat()系统调用将共享内存段连接到进程的地址空间中。shmat()函数需要传入两个参数:共享内存段的ID和一个地址,如果该地址为空,则系统会自动分配一个地址。 3. 对共享内存段进行读写操作,使用与操作普通内存相同的方法进行访问。 4. 当进程不再需要访问共享内存段时,需要使用shmdt()系统调用将共享内存段与进程断开连接。 5. 当所有进程都断开连接并且不再需要共享内存段时,使用shmctl()系统调用删除共享内存段。 需要注意的是,共享内存的并发控制是非常重要的,需要使用互斥锁或信号量等机制来保证共享内存的正确访问。 ### 回答2: C语言可以使用System V共享内存机制来实现共享内存使用共享内存是一种在进程之间共享数据的方式,可以提高进程间通信的效率。以下是使用C语言实现共享内存的一般步骤: 1. 首先创建共享内存区域。可以使用`shmget`函数来创建共享内存,需要指定共享内存的大小、权限和标识符等参数。 2. 绑定共享内存到当前进程中。可以使用`shmat`函数将共享内存映射到当前进程的地址空间中,通过返回的指针可以访问共享内存区域。 3. 使用共享内存进行数据读写。一旦共享内存区域被映射到进程的地址空间,就可以通过指针对其进行读写操作。当多个进程共享同一块内存时,需要进行同步操作,如使用互斥锁或信号量等机制来避免数据不一致或竞态条件。 4. 分离共享内存。当不再需要使用共享内存时,可以使用`shmdt`函数将共享内存从当前进程中分离,并保留共享内存区域供其他进程使用。 5. 删除共享内存。当所有进程都不再需要使用共享内存时,可以使用`shmctl`函数来删除共享内存区域,释放资源。 需要注意的是,共享内存区域只能用于进程间通信,对于线程间通信来说,并不适用。同时,使用共享内存需要谨慎操作,确保数据的一致性和安全性。 ### 回答3: 在C语言中,可以通过使用共享内存实现不同进程之间的数据共享。下面是一个简单的示例用法: 首先,需要使用`shmget()`函数来创建一个共享内存段。该函数接受三个参数,分别是一个标识符(可以是一个整数值,通常使用IPC_PRIVATE表示),所需内存的大小(字节数),以及标志位(通常为IPC_CREAT | 0666)。 ```c int shmid = shmget(IPC_PRIVATE, sizeof(int), IPC_CREAT | 0666); ``` 接下来,使用`shmat()`函数将共享内存段附加到当前进程的地址空间。该函数接受两个参数,即共享内存段的标识符以及访问内存段的权限(通常为0)。 ```c int* shared_memory = (int*) shmat(shmid, 0, 0); ``` 现在,可以在程序中使用`shared_memory`指针来访问共享内存。 当共享内存使用完毕后,需要使用`shmdt()`函数将其从进程中分离。 ```c shmdt(shared_memory); ``` 最后,如果不再需要该共享内存段,可以使用`shmctl()`函数进行控制。该函数的第一个参数为共享内存段的标识符,第二个参数为控制命令(通常为IPC_RMID),第三个参数为一个`struct shmid_ds*`类型的指针。 ```c shmctl(shmid, IPC_RMID, NULL); ``` 这样,就完成了在C语言使用共享内存进行进程间数据共享实现。需要注意的是,共享内存使用需要谨慎,因为多个进程访问共享内存时必须协调好数据的读写,防止出现数据不一致或竞争条件等问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Aogu181

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

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

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

打赏作者

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

抵扣说明:

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

余额充值