Linux中使用C语言的fork()函数创建子进程的实例教程

下面是详细讲解创建子进程的实例教程。

什么是子进程?

在Linux系统中,一个进程可以创建其他进程。被创建的进程称为子进程,而新创建进程的进程称为父进程。子进程继承了父进程的所有属性和资源,包括进程ID、打开的文件描述符、信号处理方式等。

如何创建子进程?

Linux中使用C语言提供了 fork() 函数来创建子进程。fork()函数是一个系统调用,调用后会在调用进程中复制出一个新进程,这个新进程就是子进程,同时复制出来的新进程会和原进程一样执行 fork() 之后的代码。

#include <unistd.h>

pid_t fork(void);

成功调用 fork() 函数后,会返回两个值:子进程中返回0;父进程中返回子进程的进程ID(PID)。

下面是一个简单的示例,展示了如何使用 fork() 函数创建子进程:

#include <stdio.h>
#include <unistd.h>

int main() {
    pid_t pid;
    printf("Before fork\n");
    pid = fork();
    if (pid == -1) {
        perror("Fork failed");
        return -1;
    } else if (pid == 0) { // child process
        printf("I am the child process\n");
        printf("My PID is %d\n", getpid());
    } else { // parent process
        printf("I am the parent process\n");
        printf("My child's PID is %d\n", pid);
    }
    printf("After fork\n");
    return 0;
}

运行程序,可以看到输出:

Before fork
I am the child process
My PID is 1234
After fork
I am the parent process
My child's PID is 1234
After fork

子进程的运行顺序

在实际场景中,子进程和父进程的运行顺序是不能确定的。因此,在使用 fork() 函数创建子进程时,我们应该始终确保子进程和父进程是交替运行的。这需要我们在代码中进行特殊处理。

我们还以一个具体的服务端与客户端通信过程为例:

  • 服务端监听本地的某个端口,等待客户端连接
  • 当服务端接收到连接请求时,创建一个子进程处理该连接
  • 父进程继续监听连接请求

以下是代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define MAXLINE 4096
#define SERV_PORT 9877

void do_echo(int sockfd);

int main(int argc, char **argv)
{
    int listenfd, connfd;
    pid_t childpid;
    socklen_t clilen;
    struct sockaddr_in cliaddr, servaddr;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd < 0) {
        perror("socket error");
        return -1;
    }

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    if (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
        perror("bind error");
        return -1;
    }

    if (listen(listenfd, 10) < 0) {
        perror("listen error");
        return -1;
    }

    for (;;) {
        clilen = sizeof(cliaddr);
        connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen);
        if (connfd < 0) {
            perror("accept error");
            continue;
        }
        if ((childpid = fork()) == 0) { // In child process
            close(listenfd);
            do_echo(connfd);
            exit(0);
        }
        close(connfd); // In parent process
    }
    return 0;
}

void do_echo(int sockfd)
{
    ssize_t n;
    char buf[MAXLINE];

    for (;;) {
        memset(buf, 0, MAXLINE);
        n = read(sockfd, buf, MAXLINE);
        if (n < 0) {
            if (errno == EINTR) {
                continue;
            } else {
                perror("read error");
                break;
            }
        } else if (n == 0) {
            printf("client closed connection\n");
            break;
        }
        write(sockfd, buf, n);
    }
    close(sockfd);
}

运行服务端程序,可以看到输出:

accepted connection from 127.0.0.1:56178
accepted connection from 127.0.0.1:56180

然后可以在客户端向服务端发送数据,服务端会回显收到的数据。

总结

本文详细讲解了如何在Linux中使用C语言的fork()函数创建子进程,并且提供了两个实例来巩固理解。子进程和父进程的运行顺序是不能确定的,因此在使用时需要注意编写代码,保证子进程和父进程的交替执行。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Linux中使用C语言的fork()函数创建子进程的实例教程 - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • QT中如何读写ini配置文件

    QT中可以很方便地读写ini格式的配置文件,下面是读写ini配置文件的完整攻略: 1. 先创建QSettings对象 QSettings对象是QT中读写配置文件的对象,调用它的相关方法可以轻松完成对配置文件的读写操作。需要调用QSettings对象的构造函数来创建对象,构造函数的参数有两个:文件名和格式。 例如,在mainwindow.cpp中创建一个叫做m…

    C 2023年5月23日
    00
  • 如何提高画画水平?给迷茫艺术生写的小建议

    如何提高画画水平?给迷茫艺术生写的小建议 在绘画领域,提高画画水平需要不断修炼和实践。以下是一些小建议,希望可以帮助迷茫的艺术生们提高画画水平。 1. 提高绘画技能的练习 要成为一名优秀的画家,需要系统地学习基础绘画技能。以下是一些常用的练习方法: 1.1 画基本形状 要想画好任何东西,首先需要掌握基本形状。 建议在纸上反复练习画圆、方、三角等不同形状,并尝…

    C 2023年5月22日
    00
  • C 标准库 setjmp.h

    setjmp.h 是 C 语言标准库中的一个头文件,提供了一种跳转控制流的机制。setjmp.h 库中包含了两个函数:setjmp 和 longjmp。这两个函数分别用于保存程序的当前环境(内存状态)和基于后已保存的状态跳回。下面我们详细讲解 setjmp.h 的使用攻略。 setjmp 函数 setjmp 函数的原型如下: #include <set…

    C 2023年5月10日
    00
  • Unix下C程序内存泄漏检测工具Valgrind的安装与使用详解

    下面是对应的攻略。 Unix下C程序内存泄漏检测工具Valgrind的安装与使用详解 1. 简介 Valgrind是一款非常强大的Unix下的C程序内存泄漏检测工具。它能够检测出C语言程序中的内存泄漏、未初始化使用的变量、使用已释放的指针等常见的错误。此外,它还能够检测出死锁等问题,并能够通过日志和报告等形式给出详细的分析结果,帮助开发人员轻松定位和修复程序…

    C 2023年5月30日
    00
  • C语言中#define定义的标识符和宏实例代码

    我来给你讲解关于C语言中#define定义的标识符和宏的完整攻略。 定义标识符 在C语言中,使用#define关键字可以定义一个标识符,并将其代表的值替换到程序中。语法如下: #define 标识符 数值或表达式 其中,标识符可以是任意字符串,而数值或表达式则可以是任意C语言表达式,例如: #define PI 3.1415926 // 将标识符PI定义为3…

    C 2023年5月30日
    00
  • C++两个cpp文件间如何进行各自函数的调用方式

    当我们在一个项目中有多个 C++ 源文件时,我们需要知道如何在不同的文件中调用其它文件的函数。 下面是两个cpp文件间如何进行各自函数的调用方式的攻略: 声明和定义 要在一个文件中使用另一个文件中定义的函数,我们必须将该函数的定义标记为 “extern”,并在需要使用它的文件中进行声明。 例如,如果我们有两个文件,一个叫做 main.cpp 和另一个叫做 h…

    C 2023年5月23日
    00
  • const int*、const int * const 和 int const * 的区别

    const int、const int const 和 int const* 的区别 c++中,指针前面的const关键字总是会导致困惑。本文将对 const int*、const int* const 和 int const* 之间的区别进行讲解。 首先,我们需要知道,* 是一个“附加符号”,它决定了符号左边的标识符是一个指针而非其他类型的变量。指针可以看…

    C 2023年5月10日
    00
  • C++实现简单的HTTP服务器

    下面是实现简单的HTTP服务器的攻略: 1. 搭建服务器 在C++中,我们可以使用socket进行网络编程。首先创建一个Socket,接着Bind绑定端口号和IP地址,最后调用Listen进行监听客户端的连接请求。 // 创建socket int server_socket = socket(AF_INET, SOCK_STREAM, 0); // 绑定端口…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部