C 编程:Segmentation Fault (Core Dumped)

C Programming: Segmentation Fault (Core Dumped)

我正在尝试编写的程序试图演示 IPC 如何在 Linux 上工作,但我不断收到核心转储错误。它编译得很好,并且会 运行 直到父进程中的最后一个输出语句。

我的代码如下:

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


 #define SHM_SIZE 15

int main (int argc, char ** argv[]) {
     pid_t pid; //pid variable of type pid
     int shmid; //shared memory id
     int key = 1776; //randomly chosen key
     char *shm; //shared memory name 

     int pipefd[2];
     char buff;

     pid = fork(); //creating child process
     pipe(pipefd); //creating pipe


if (pid < 0) {
    fprintf(stderr, "Fork Failed"); 
    return -1;
} else if (pid == 0) {
    shmid = shmget(key, SHM_SIZE, 0); 
    shm = shmat(shmid, 0, 0);

    char *n = (char *) shm;

    printf("hello i am the child process. my pid is %d. what is your name?: ", getpid());
    scanf("%s", n);
    printf("\n");

    /////////////////////////////////////////////////////////////////////////////////////// 

    close(pipefd[1]);
    printf("pipe opened on child end");
    printf("\n");
    while(read(pipefd[0], &buff, 1) > 0) {
            write(1, &buff, 1);
    }    
    write(1, "\n", 1);
    close(pipefd[0]);
    printf("pipe successfully closed");
    printf("\n");
    exit(EXIT_SUCCESS);

} else {
    shmid = shmget(key, SHM_SIZE, 0777 | IPC_CREAT);
    shm = shmat(shmid, 0, 0);

    wait(NULL);

    printf("\nThis is Child's Parent. My pid is %d. Nice to me you %s.\n", getpid(), shm);
    printf("\n");

    //////////////////////////////////////////////////////////////////////////////////////

    close(pipefd[0]);
    printf("pipe open on parent end");
    printf("\n");
    write(pipefd[1], argv[1], strlen(argv[1]));
    close(pipefd[1]);
    printf("pipe successfully closed");
    wait(NULL);
    exit(EXIT_SUCCESS);
}
return 0;
}

跟我的args[]有关系吗?比如我可以访问遥不可及的内存吗?或者正在尝试访问一些无效的指针?

非常感谢!

您正在从您创建的管道的同一端读取和写入。通常的做法是从 end [1] 读取并写入 end [0]。告诉我这是否有帮助。此外,通常的做法是不要在子进程和父进程之间进行太多操作。尝试在段(父段和子段)之间执行代码通常会以段错误告终,即使您的代码可以编译。

你的代码有几个问题

  1. fork 之前创建管道 。您创建管道两次,一次用于 parent 进程和一个 child 进程。没意义,水管 创建的 child 不能被 parent 使用。管道必须已经 存在,以便 child 在 child 是时继承文件描述符 已创建。

  2. 通常 parent 创建共享内存而 child 获取 shmid 从 parent 执行 fork。否则你将不得不同步 child 和 parent。所以我会先创建共享内存 fork,以便 child 从 parent.

  3. 继承 shmid
  4. 在行 char *n = (char *) shm; 中不需要转换,shm 是 已经 char*.

  5. fork 之后的 parent 块中,您执行 wait(NULL); 然后继续 写入管道。这毫无意义,您同时屏蔽了 parent 和 child。 child 在 read 上阻塞,因为 parent 没有通过 管,还。 parent 在 wait 上阻塞,因为 child 永远不会退出,因此 不能通过管道发送任何东西。 parent必须发送数据 通过管道,然后 wait 为 child 退出。

  6. 在child块你做scanf("%s", n);,你不是在保护你 防止缓冲区溢出。 scanf("%14s", n) 会更好。还有你不是 检查 scanf 是否阅读了任何内容。如果用户按下 CtrlD然后stdin关闭,scanf失败。在这种情况下 n 可能不会被 '[=34=]' 终止,这会导致未定义的行为 当 parent 尝试打印它时。所以会更好:

    if(scanf("%14s", n) != 1) // avoid buffer overflow
    {   
        fprintf(stderr, "Child: cannot read from stdin\n");
        n[0] = 0; // 0-terminating
    }
    
  7. fork之后的parent块中,你做了两次wait,为什么?

  8. 你的main错了,应该是

    int main(int argc, char **argv);
    
  9. parent通过管道将argv[1]的内容发送到child,但是 你没有检查 argv[1] 是否不是 NULL。在开始时使用它 程序:

     if(argc != 2)
     {
         fprintf(stderr, "usage: %s string\n", argv[0]);
         return 1;
     }
    

所以正确的版本应该是:

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


 #define SHM_SIZE 15

int main (int argc, char **argv) {
     pid_t pid; //pid variable of type pid
     int shmid; //shared memory id
     char *shm; //shared memory name 

     if(argc != 2)
     {
         fprintf(stderr, "usage: %s string\n", argv[0]);
         return 1;
     }

     int pipefd[2];
     char buff;

     // create shared memory before the fork,
     // otherwise you will need to syncronize parent
     // and child

     pipe(pipefd); //creating pipe before the fork

     // parent creates shared memory, child inherits shmid
     // after fork
     shmid = shmget(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | 0666); 

     pid = fork(); //creating child process


    if (pid < 0) {
        fprintf(stderr, "Fork Failed"); 
        return 1; // return -1 would be the same as return 255
    } else if (pid == 0) {
        shm = shmat(shmid, 0, 0);

        char *n = shm; // shm is already a char*

        printf("hello i am the child process. my pid is %d. what is your name?: ", getpid());
        if(scanf("%14s", n) != 1) // avoid buffer overflow
        {
            fprintf(stderr, "Child: cannot read from stdin\n");
            n[0] = 0; // 0-terminating
        }
        printf("\n");

        /////////////////////////////////////////////////////////////////////////////////////// 

        close(pipefd[1]);
        printf("pipe opened on child end");
        printf("\n");
        printf("Parent sends: ");
        fflush(stdout);
        while(read(pipefd[0], &buff, 1) > 0) {
                write(1, &buff, 1);
        }    
        write(1, "\n", 1);
        close(pipefd[0]);
        printf("pipe successfully closed");
        printf("\n");
        exit(EXIT_SUCCESS);

    } else {
        shm = shmat(shmid, 0, 0);
        close(pipefd[0]);
        printf("pipe open on parent end");
        printf("\n");
        write(pipefd[1], argv[1], strlen(argv[1]));
        close(pipefd[1]);
        printf("pipe successfully closed");

        // not we wait for child to exit
        wait(NULL);

        printf("\nThis is Child's Parent. My pid is %d. Nice to me you %s.\n", getpid(), shm);
        printf("\n");

        //////////////////////////////////////////////////////////////////////////////////////

        exit(EXIT_SUCCESS);
    }
    return 0;
}

输出为:

$ ./b "message to child: stop playing video games!"
pipe open on parent end
hello i am the child process. my pid is 10969. what is your name?: Pablo

pipe opened on child end
Parent sends: message to child: stop playing video games!
pipe successfully closed
pipe successfully closed
This is Child's Parent. My pid is 10968. Nice to me you Pablo.