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

4 篇文章 0 订阅
1 篇文章 0 订阅
本文详细介绍了如何在C语言中使用共享内存进行进程间通信,包括upload和download函数的代码实现,以及信号处理机制,展示了如何通过共享内存在两个进程间高效地传输文件数据。
摘要由CSDN通过智能技术生成

目录

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
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Aogu181

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

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

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

打赏作者

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

抵扣说明:

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

余额充值