C语言进程(第一章进程基础,fork()函数,pid_t, pid, getpid())

简介: C语言进程(第一章进程基础,fork()函数,pid_t, pid, getpid())

C语言进程(第一章进程基础,fork()函数,pid_t, pid, getpid())

简介

当我们用c编程构建一个应用时,常常会遇到这样的场景:需要在程序中同时完成几项任务,如处理数据、打印输出、和读取用户输入等。 这种需要同时进行多项任务效率更高的需求,就是“并发”。

一般由操作系统负责协调和分配计算机资源, 管理“进程”和“线程”。在c语言中,我们可以使用进程和线程来实现并发执行的目的。

  • 进程(process) 是操作系统管理资源,供程序运行时需要访问和控制的运行空间和内存地址。每个进程拥有自己的内存空间、系统资源、打开文件以及与其他进程的互动等等。

我们常把整个程序作为一个进程来处理,但是一个程序同时可能包含多个运行实例,即多个进程。 比如的浏览器中同时执行浏览网页、下载文件、播放视频等很多任务,那这些任务就会在不同的进程中执行。

入门

相关在线编辑网站:https://www.ideone.com/whPQYr

当学习进程和线程这些概念之后,下面是一些适合入门的C语言案例如下:

  1. 创建一个简单的进程
#include <stdio.h>
#include <stdlib.h> 
#include <unistd.h> 
int main() {
    pid_t pid;
    pid = fork();
    if (pid == -1) {
        printf("error: 创建进程失败 \n");
        exit(1);
    } else if (pid == 0) {
        printf("我是子进程,我的pid是 %d \n", getpid());
    } else {
        printf("我是父进程,我的pid是 %d \n", getpid());
    }
    return 0;
}

该程序使用fork()函数创建新进程。如果pid值小于0,则fork()系统调用失败并且创建一个新进程将无法完成;如果pid等于0,则说明当前运行的进程为子进程。所以我们执行该程序可以得到父进程的进程 id 和子进程的进程 id。

运行结果:

运行结果解释:

这个程序运行的输出结果是:

我是父进程,我的pid是 3173 
我是子进程,我的pid是 3213 

在程序运行时,当 fork() 函数被调用时,进程会生成一个新的地址空间,然后把父进程的数据拷贝到新的地址空间中。新的进程就是所谓的子进程,它与父进程几乎完全相同,只有进程 ID 不同。

  • 在父进程中,fork()函数返回子进程的进程 ID 号,也就是变量 pid 值大于 0,如果pid等于-1,则说明进程创建失败。
  • 在子进程中,fork()函数返回0,因此在代码块 else if (pid == 0) 中执行,
    输出 “我是子进程,我的pid是 xxx” 的格式化字符串,使用 getpid() 来获取子进程的PID号码。
  • 在父进程中,在 else 语句块中执行,打印 “我是父进程,我的pid是 xxx”。使用 getpid() 来获取父进程自己的 PID 号。

两个打印信息出现的顺序是不确定的,这取决于操作系统选择在哪个进程上运行,这在每台计算机和每种操作系统都可能会有所不同。

fork()

fork()函数是Unix操作系统中创建新进程的一个函数。在C语言中,可以使用这个函数来创建新进程。

具体地说,当程序执行到 fork()函数时,它会创建一个子进程并使其运行同样的代码,相当于将原来的进程“复制”一份,形成了两个几乎完全相同的进程。其中,原有进程称为父进程,新创建出来的进程称为子进程。而在父进程和子进程中,通过fork()函数的返回值来区分不同进程,因为在子进程中,fork()函数将返回0,在父进程中,该值将是它所创建的子进程的进程ID。

fork() 函数可以用来实现普通进程的派生,也可以在后续中使用 execve() 系列函数去加载不同命令。

下面是基本的fork()函数使用案例:

#include <stdio.h>
#include <stdlib.h> 
#include <unistd.h> 
int main() {
    pid_t pid;
    pid = fork();
    if (pid == -1) {
        printf("error: 创建进程失败 \n");
        exit(1);
    } else if (pid == 0) {
        printf("我是子进程,我的pid是 %d \n", getpid());
    } else {
        printf("我是父进程,我的pid是 %d \n", getpid());
    }
    return 0;
}

在这个例子里,调用fork()函数创建了一个子进程。如果fork()函数调用失败,那么就会返回-1;在子进程时,其返回值为0,而在父进程时其返回值是子进程 ID。

需要注意的是,fork()函数非常耗费资源,因为该函数需要拷贝整个进程。因此在代码中应当尽量避免使用超过必要的fork()函数调用,在多次使用时也需要减少系统资源消耗和时间延迟等方面进行优化。

运行结果:

pid

pid 是 “process ID” 的缩写,即进程ID。

在操作系统中,每个正在运行的进程都会被分配一个唯一的整数进程 ID (PID),用于标识该进程。进程 ID 在系统内部用作识别进程、管理进程状态等方面起着非常重要的作用,这意味着唯一的PID意味着对应唯一的进程标识符。在 Linux 和 Unix 系统中,可以使用 ps 命令和其他系统工具来查看当前运行的进程及其 PID。

你可以通过编写 shell 脚本 或 C 语言程序来获得当前进程的 pid,使用 getpid()函数,如下所示:

#include <stdio.h>
#include <stdlib.h> 
#include <unistd.h> 
int main() {
    printf("当前进程的pid是:%d\n", getpid());
    return 0;
}

在该程序中,使用getpid()函数获取当前进程的 PID,并把其打印出来。因为每个进程的PID都是独特的,运行该程序时,您将会得到一个不同于其他正在运行进程的独特 PID 值输出结果。

总之,在操作系统中了解和使用进程 ID 很重要,这有助于开发人员和系统管理员进行多种监控和管理活动,以及进行更好的调试和错误处理。

运行结果:

pid_t

在 C 语言中,pid_t 是一个整数类型,用于表示进程的 ID 。具体来说,在绝大多数 Unix 或 Linux 系统中,pid_t 的定义如下:

typedef int pid_t;

因此,pid_t 实际上是 int 类型,而且在一般情况下其大小与 int 相等。

使用 pid_t 这种类型是为了能够让代码更加可移植。因为不同平台之间对于进程ID的实现可能会有所不同,比如 Windows 平台就使用 HANDLE 类型而非 pid_t;在32位系统和64位系统上PID最大值也可能不同,而使用这种类型可以保证程序在不同的平台上都能正常编译和运行。因此,在编写依赖于进程ID的程序时,最好使用 pid_t 而非硬编码整数类型。

在 C 标准库 <unistd.h>中,fork() 函数说明返回一个 pid_t 类型的值,从而方便地获得新创建进程的 PID 号,示例代码如下:

#include <stdio.h>
#include <stdlib.h> 
#include <unistd.h> 
int main() {
    pid_t pid;
    pid = fork();
    if (pid == -1) {
        printf("error: 创建进程失败 \n");
        exit(1);
    } else if (pid == 0) {
        printf("我是子进程,我的pid是 %d \n", getpid());
    } else {
        printf("我是父进程,我的pid是 %d \n", getpid());
    }
    return 0;
}

在该程序中,定义 pid_t pid 来保存进程 ID 值,即 fork 函数返回值。这有助于让您的代码更符合 C 标准,并以可移植方式使用操作系统提供的功能。

如果大家觉得有用的话,可以关注我下面的微信公众号,极客李华,我会在里面更新更多行业资讯,企业面试内容,编程资源,如何写出可以让大厂面试官眼前一亮的简历等内容,让大家更好学习编程,我的抖音,B站也叫极客李华。大家喜欢也可以关注一下

相关文章
|
16天前
|
程序员 C语言
C语言库函数 — 内存函数(含模拟实现内存函数)
C语言库函数 — 内存函数(含模拟实现内存函数)
27 0
|
27天前
|
编译器 C语言 C++
【C语言】memset()函数(内存块初始化函数)
【C语言】memset()函数(内存块初始化函数)
26 0
|
27天前
|
编译器 C语言 C++
【C语言】memcpy()函数(内存块拷贝函数)
【C语言】memcpy()函数(内存块拷贝函数)
42 0
|
21小时前
|
C语言
【C语言】字符分类函数与字符转换函数
【C语言】字符分类函数与字符转换函数
7 1
|
22小时前
|
程序员 编译器 C语言
C语言之函数与参数
C语言之函数与参数
5 0
|
2天前
|
C语言
C语言:内存函数(memcpy memmove memset memcmp使用)
C语言:内存函数(memcpy memmove memset memcmp使用)
|
2天前
|
C语言
C语言:字符函数和字符串函数(strlen strcat strcmp strncmp等函数和模拟实现)
C语言:字符函数和字符串函数(strlen strcat strcmp strncmp等函数和模拟实现)
|
3天前
|
算法 Linux Shell
【linux进程(二)】如何创建子进程?--fork函数深度剖析
【linux进程(二)】如何创建子进程?--fork函数深度剖析
|
3天前
|
存储 C语言
C语言函数的返回值
C语言函数的返回值
7 0
|
4天前
|
C语言 Windows
C语言中的fopen与fclose函数详解
C语言中的fopen与fclose函数详解
11 1