实验——进程通信与共享内存

代码要求:

  1. 利用shmget()创建共享内存;
  2. 利用shmat()建立共享内存与进程之间的关联;
  3. 用fork()创建子进程,子进程与父进程共享存储空间;
  4. 利用信号量机制实现子进程读入数据到共享内存,父进程取出共享内存中的数据保存到文件,完成通信;
  5. 调用shmdt()断开共享内存连接;
  6. 调用shmctl()释放共享内存;
  7. 有注释,能在ubuntu上运行

可参考

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

#define SHM_SIZE 1024

// 定义共享内存结构体
typedef struct {
    int flag;           // 标志位,0表示共享内存空闲,1表示共享内存已被写入数据
    char buf[SHM_SIZE]; // 数据缓冲区
} shm_data_t;

// 定义信号量结构体
typedef union {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
} sem_union_t;

// 定义信号量变量
static int sem_id;

// 定义P操作函数
static int sem_p(int sem_id) {
    struct sembuf sem_op = {0, -1, SEM_UNDO};
    return semop(sem_id, &sem_op, 1);
}

// 定义V操作函数
static int sem_v(int sem_id) {
    struct sembuf sem_op = {0, 1, SEM_UNDO};
    return semop(sem_id, &sem_op, 1);
}

int main() {
    int shmid;
    void *shmaddr;
    pid_t pid;
    shm_data_t *shm_data;
    sem_union_t sem_union;

    // 创建共享内存
    if ((shmid = shmget(IPC_PRIVATE, sizeof(shm_data_t), IPC_CREAT|0666)) < 0) {
        perror("shmget error");
        exit(1);
    }

    // 建立共享内存与进程之间的关联
    if ((shmaddr = shmat(shmid, NULL, 0)) == (void *)-1) {
        perror("shmat error");
        exit(1);
    }

    // 初始化共享内存
    shm_data = (shm_data_t *)shmaddr;
    shm_data->flag = 0;

    // 创建信号量
    if ((sem_id = semget(IPC_PRIVATE, 1, IPC_CREAT|0666)) < 0) {
        perror("semget error");
        exit(1);
    }

    // 初始化信号量
    sem_union.val = 1;
    if (semctl(sem_id, 0, SETVAL, sem_union) < 0) {
        perror("semctl error");
        exit(1);
    }

    // 创建子进程
    if ((pid = fork()) < 0) {
        perror("fork error");
        exit(1);
    } else if (pid == 0) { // 子进程
        int i = 0;
        char buf[SHM_SIZE];

        while (1) {
            sem_p(sem_id); // P操作

            // 如果共享内存空闲,写入数据
            if (shm_data->flag == 0) {
                sprintf(buf, "This is data %d from child process.\n", ++i);
                strncpy(shm_data->buf, buf, SHM_SIZE);
                shm_data->flag = 1;
            }

            sem_v(sem_id); // V操作

            sleep(1); // 等待1秒钟
        }
    } else { // 父进程
        FILE *fp;
        char buf[SHM_SIZE];

        // 打开文件
        if ((fp = fopen("output.txt", "w")) == NULL) {
            perror("fopen error");
            exit(1);
        }

        while (1) {
            sem_p(sem_id); // P操作

            // 如果共享内存已被写入数据,从共享内存中读取数据并保存到文件中
            if (shm_data->flag == 1) {
                strncpy(buf, shm_data->buf, SHM_SIZE);
                fprintf(fp, "%s", buf);
                shm_data->flag = 0;
            }

            sem_v(sem_id); // V操作

            sleep(1); // 等待1秒钟
        }

        // 关闭文件
        fclose(fp);
    }

    // 断开共享内存连接
    if (shmdt(shmaddr) < 0) {
        perror("shmdt error");
        exit(1);
    }

    // 释放共享内存
    if (shmctl(shmid, IPC_RMID, 0) < 0) {
        perror("shmctl error");
        exit(1);
    }

    // 释放信号量
    if (semctl(sem_id, 0, IPC_RMID, sem_union) < 0) {
        perror("semctl error");
        exit(1);
    }

    return 0;
}

如何使用共享内存和信号量实现进程间通信。在代码中,父进程创建共享内存和信号量,然后创建子进程。子进程将数据写入共享内存,并通过信号量锁住共享内存,以防止其他进程访问。父进程从共享内存中读取数据,保存到文件中,并释放共享内存和信号量。参考如下代码:


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

#define BUF_SIZE 1024

// 定义信号量
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
};

void sem_p(int semid) {
    struct sembuf sops = {0, -1, SEM_UNDO};
    semop(semid, &sops, 1);
}

void sem_v(int semid) {
    struct sembuf sops = {0, 1, SEM_UNDO};
    semop(semid, &sops, 1);
}

int main(int argc, char *argv[]) {
    int shmid, semid;
    key_t key;
    char *shm_ptr;
    char buf[BUF_SIZE];
    pid_t pid;

    // 创建共享内存的key
    if ((key = ftok(".", 'a')) == -1) {
        perror("ftok error");
        exit(1);
    }

    // 创建共享内存
    if ((shmid = shmget(key, BUF_SIZE, IPC_CREAT|0666)) == -1) {
        perror("shmget error");
        exit(1);
    }

    // 关联共享内存
    if ((shm_ptr = (char *)shmat(shmid, 0, 0)) == (void *)-1) {
        perror("shmat error");
        exit(1);
    }

    // 创建信号量
    if ((semid = semget(key, 1, IPC_CREAT|0666)) == -1) {
        perror("semget error");
        exit(1);
    }

    // 初始化信号量
    union semun sem_union;
    sem_union.val = 1;
    if (semctl(semid, 0, SETVAL, sem_union) == -1) {
        perror("semctl error");
        exit(1);
    }

    // 创建子进程
    pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(1);
    } else if (pid == 0) {  // 子进程
        // 写入数据到共享内存
        sem_p(semid);  // P操作,锁住信号量
        sprintf(shm_ptr, "Hello, parent process!");
        sem_v(semid);  // V操作,解锁信号量
        exit(0);
    } else {  // 父进程
        // 从共享内存中读取数据
        sem_p(semid);  // P操作,锁住信号量
        memset(buf, 0, BUF_SIZE);
        strncpy(buf, shm_ptr, strlen(shm_ptr));
        sem_v(semid);  // V操作,解锁信号量

        // 保存数据到文件
        int fd;
        if ((fd = open("output.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) {
            perror("open error");
            exit(1);
        }
        write(fd, buf, strlen(buf));
        close(fd);

        // 断开共享内存连接
        if (shmdt(shm_ptr) == -1) {
            perror("shmdt error");
            exit(1);
        }

        // 释放共享内存
        if (shmctl(shmid, IPC_RMID, 0) == -1) {
            perror("shmctl error");
            exit(1);
        }
    }

    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <string.h>

#define SHM_SIZE 1024

int shmid;
char *shmaddr;
int semid;

void sig_handler(int signo) {
    if (signo == SIGUSR1) {
        printf("Child process is writing to shared memory...\n");
        // 子进程写入数据到共享内存
        strcpy(shmaddr, "Hello, parent process!");
        // 发送信号通知父进程可以读取共享内存中的数据
        union sigval val;
        val.sival_int = 0;
        sigqueue(getppid(), SIGUSR1, val);
    }
}

void child_process() {
    // 建立共享内存与进程之间的关联
    shmaddr = (char *) shmat(shmid, NULL, 0);
    if (shmaddr == (char *) -1) {
        perror("shmat");
        exit(1);
    }

    // 注册信号处理函数
    signal(SIGUSR1, sig_handler);

    // 等待父进程发送信号
    pause();

    // 断开共享内存连接
    if (shmdt(shmaddr) == -1) {
        perror("shmdt");
        exit(1);
    }
}

void parent_process() {
    // 建立共享内存与进程之间的关联
    shmaddr = (char *) shmat(shmid, NULL, 0);
    if (shmaddr == (char *) -1) {
        perror("shmat");
        exit(1);
    }

    // 等待子进程写入数据到共享内存
    pause();

    // 从共享内存中读取数据并保存到文件
    FILE *fp = fopen("output.txt", "w");
    if (fp == NULL) {
        perror("fopen");
        exit(1);
    }
    fprintf(fp, "%s\n", shmaddr);
    fclose(fp);

    // 断开共享内存连接
    if (shmdt(shmaddr) == -1) {
        perror("shmdt");
        exit(1);
    }

    // 释放共享内存
    if (shmctl(shmid, IPC_RMID, NULL) == -1) {
        perror("shmctl");
        exit(1);
    }
}

int main() {
    // 创建共享内存
    shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget");
        exit(1);
    }

    // 创建信号量
    semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
    if (semid == -1) {
        perror("semget");
        exit(1);
    }

    // 初始化信号量
    union semun {
        int val;
        struct semid_ds *buf;
        unsigned short *array;
    } arg;
    arg.val = 0;
    if (semctl(semid, 0, SETVAL, arg) == -1) {
        perror("semctl");
        exit(1);
    }

    // 创建子进程
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork");
        exit(1);
    } else if (pid == 0) {
        // 子进程
        child_process();
    } else {
        // 父进程
        parent_process();
    }

    return 0;
}

参考小小代码示例:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/ipc.h>  
#include <sys/shm.h>  
#include <semaphore.h>  
  
#define SHM_SIZE 1024  // 共享内存大小  
#define SEM_VALUE 1    // 信号量初始值  
  
int main() {  
    int shmid;            // 共享内存标识符  
    char *shmaddr;        // 共享内存地址指针  
    int semid;            // 信号量标识符  
    semid_t semaphore;    // 信号量变量  
    pid_t pid;            // 子进程标识符  
    int status;           // 等待子进程返回状态  
  
    // 创建共享内存  
    shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0666);  
    if (shmid < 0) {  
        perror("shmget");  
        exit(1);  
    }  
  
    // 映射共享内存到进程空间  
    shmaddr = shmat(shmid, NULL, 0);  
    if (shmaddr == (char *) -1) {  
        perror("shmat");  
        exit(1);  
    }  
  
    // 创建信号量  
    semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);  
    if (semid < 0) {  
        perror("semget");  
        exit(1);  
    }  
  
    // 初始化信号量值  
    if (semctl(semid, 0, SETVAL, SEM_VALUE) < 0) {  
        perror("semctl");  
        exit(1);  
    }  
  
    // fork一个子进程,并与父进程共享共享内存和信号量  
    pid = fork();  
    if (pid < 0) {  // fork返回一个负值表示出错,需要退出程序并打印错误信息  
        perror("fork");  
        exit(1);  
    } else if (pid == 0) {  // fork返回0表示子进程,否则返回子进程的进程ID,这里是父进程  
        // 子进程读取共享内存中的数据并输出到控制台,这里简单地将共享内存清空并退出子进程  
        memset(shmaddr, ' ', SHM_SIZE);  // 清空共享内存中的数据,用空格填充到整个共享内存空间中  
        printf("Child process read data from shared memory: \n");  
        printf("%s", shmaddr);  // 输出共享内存中的数据到控制台,这里只是简单地输出到终端,需要实际修改以实现需要的功能,例如将数据写入文件等操作  
        exit(0);  // 子进程结束,需要调用exit退出,而不是return返回,因为子进程结束会由内核回收资源并处理返回值,所以这里需要用exit退出子进程而不是return返回一个值给父进程来处理结束子进程的资源回收和返回值处理等操作。  
    } else {  // fork返回一个正值表示父进程,这里是父进程的代码逻辑部分,等待子进程结束并处理子进程的返回值等操作。这里简单地将数据写入共享内存中并等待子进程结束。需要实际修改以实现需要的功能。例如将数据写入文件等操作。

#未完待续,如有帮助,恭请采纳。

C语言完成
将数据写入到共享内存中。

write.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <string.h>
#include <signal.h>
struct Msg {
    int flag;//0为读,1为写
    char content[104];
};
int shmid;
void *pshm;
void Handle(int s) {//监听函数
    if (s == 2)
    {
        shmdt(pshm);
        shmctl(shmid, IPC_RMID, 0);
        exit(0);
    }
}
int main() {
    signal(2, Handle);//按ctrl+c键退出时会处理这个消息,进行共享内存卸载、删除操作,最后exit(0)退出程序
 
    key_t key = ftok(".", 2);//通过ftok函数得到id值存在key中
 
    shmid = shmget(key, 100, IPC_CREAT | IPC_EXCL | 0666);/*创建共享内存,成功返回共享内存的ID,出错返回-1,IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的共享内存,如果共享内存已存在,返回一个错误。*/
 
    pshm = shmat(shmid, 0, 0);//挂接操作,成功返回指向共享存储段的指针,出错返回-1
 
    if (*(int *)pshm == -1) {//查看挂接是否成功如果出错返回-1,报错
        printf("shmat error!\n");
        exit(0);
    }
    memset(pshm, 0, 100);//初始化
    struct Msg *msg = (struct Msg *)pshm;
    msg->flag = 1;
    while (1) {
        if (msg->flag == 1) {//当为1时写消息,此时读文件中不能操作此共享内存
            printf("请输入内容至共享内存:");
            scanf("%s", msg->content);
            msg->flag = 0;//当写消息后flag置为0,让读文件开始执行读操作,此时写文件不能进行写操作
        }
        else {
            sleep(1);
        }
    }
    return 0;
}

参考讲的很详细 https://blog.csdn.net/m0_59337025/article/details/127790882

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 以下回答来自chatgpt:

    答案: 1. 这段代码可以在Linux操作系统上运行。 2. 这段代码主要实现了父进程创建一个共享内存区域,然后fork()出子进程,子进程向共享内存写入数据并发送信号通知父进程,父进程收到信号后从共享内存读取数据并输出。 3. shmget()用于创建共享内存区域,shmat()用于将共享内存区域映射到进程的地址空间中,fork()用于创建子进程,shmdt()用于分离共享内存区域,shmctl()用于控制共享内存区域。 4. 采用信号量机制实现子进程和父进程之间的通信是因为信号量是一种轻量级的同步机制,可以避免死锁和资源竞争的问题。 5. 读取的数据会保存在共享内存区域中。 6. 代码的运行流程: 1)父进程创建共享内存区域; 2)父进程fork()出子进程; 3)子进程向共享内存区域写入数据并发送信号通知父进程; 4)父进程收到信号后从共享内存区域读取数据并输出; 5)父进程分离共享内存区域并删除。 具体代码和注释如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #include <string.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    
    #define SHMKEY 1234 //共享内存key值
    #define SHMSIZE 16  //共享内存大小
    
    int shmid, *shmaddr;    //共享内存标识符和地址
    
    //信号处理函数
    void mysignal(int signum)
    {
        if (signum == SIGUSR1)  //收到子进程发送的信号
        {
            printf("parent process received signal from child process\n");
            printf("the data read from shared memory is: %d\n", *shmaddr);
        }
    }
    
    int main()
    {
        pid_t pid;
    
        signal(SIGUSR1, mysignal);  //注册信号处理函数
    
        //创建共享内存区域
        if ((shmid = shmget(SHMKEY, SHMSIZE, IPC_CREAT | 0666)) < 0)
        {
            perror("shmget");
            exit(1);
        }
    
        //将共享内存区域映射到进程地址空间中
        if ((shmaddr = shmat(shmid, NULL, 0)) == (int *)-1)
        {
            perror("shmat");
            exit(1);
        }
    
        //清空共享内存区域
        memset(shmaddr, 0, SHMSIZE);
    
        //创建子进程
        if ((pid = fork()) < 0)
        {
            perror("fork");
            exit(1);
        }
    
        if (pid == 0)   //子进程
        {
            printf("child process is writing data to shared memory\n");
            *shmaddr = 100; //向共享内存写入数据
            kill(getppid(), SIGUSR1);   //发送信号给父进程
            exit(0);
        }
        else    //父进程
        {
            printf("parent process is waiting for signal from child process\n");
            pause();    //等待信号
        }
    
        //分离共享内存区域
        if (shmdt(shmaddr) < 0)
        {
            perror("shmdt");
            exit(1);
        }
    
        //删除共享内存区域
        if (shmctl(shmid, IPC_RMID, 0) < 0)
        {
            perror("shmctl");
            exit(1);
        }
    
        return 0;
    }
    

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

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

#define SHM_SIZE 1024
#define FILENAME "shared_data.txt"

int shmid;
char* shared_memory;

void signal_handler(int signum) {
    if (signum == SIGUSR1) {
        printf("Parent process received data from child process.\n");
        FILE* file = fopen(FILENAME, "w");
        if (file == NULL) {
            perror("Failed to open file.");
            exit(1);
        }
        fprintf(file, "%s", shared_memory);
        fclose(file);
        printf("Data saved to file: %s\n", FILENAME);
        shmdt(shared_memory);
        shmctl(shmid, IPC_RMID, NULL);
        exit(0);
    }
}

int main() {
    key_t key = ftok(".", 'S');
    if (key == -1) {
        perror("ftok failed");
        exit(1);
    }

    // Create shared memory segment
    shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0666);
    if (shmid == -1) {
        perror("shmget failed");
        exit(1);
    }

    // Attach shared memory segment
    shared_memory = (char*)shmat(shmid, NULL, 0);
    if (shared_memory == (char*)(-1)) {
        perror("shmat failed");
        exit(1);
    }

    // Fork a child process
    pid_t pid = fork();
    if (pid == -1) {
        perror("fork failed");
        exit(1);
    }

    if (pid == 0) {
        // Child process
        sleep(1);  // Wait for parent process to be ready
        printf("Child process is writing data to shared memory.\n");
        strcpy(shared_memory, "Hello, parent process!");
        kill(getppid(), SIGUSR1);  // Send signal to parent process
        shmdt(shared_memory);
        exit(0);
    } else {
        // Parent process
        signal(SIGUSR1, signal_handler);  // Register signal handler
        printf("Parent process is waiting for data from child process.\n");
        wait(NULL);  // Wait for child process to finish
    }

    return 0;
}