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站也叫极客李华。大家喜欢也可以关注一下

相关文章
|
4月前
|
存储 C语言
`scanf`是C语言中用于按格式读取标准输入的函数
`scanf`是C语言中用于按格式读取标准输入的函数,通过格式字符串解析输入并存入指定变量。需注意输入格式严格匹配,并建议检查返回值以确保读取成功,提升程序健壮性。
1115 0
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
762 23
|
6月前
|
安全 C语言
C语言中的字符、字符串及内存操作函数详细讲解
通过这些函数的正确使用,可以有效管理字符串和内存操作,它们是C语言编程中不可或缺的工具。
361 15
|
8月前
|
Web App开发 Linux 程序员
获取和理解Linux进程以及其PID的基础知识。
总的来说,理解Linux进程及其PID需要我们明白,进程就如同汽车,负责执行任务,而PID则是独特的车牌号,为我们提供了管理的便利。知道这个,我们就可以更好地理解和操作Linux系统,甚至通过对进程的有效管理,让系统运行得更加顺畅。
249 16
|
7月前
|
Shell Linux C语言
函数和进程之间的相似性
在一个C程序可以fork/exec另一个程序,其过程是先fork一个子进程,然后让子进程使用exec系列函数将子进程的代码和数据替换为另一个程序的代码和数据,之后子进程就用该程序的数据执行该程序的代码,从而达到程序之间相互调用的效果。在学了C语言、C++或是JAVA等高级语言,你会知道,在这些语言中的函数是可以相互进行见调用的,但是在学习了Linux的前面的知识后,你就会有意无意的认识到其实进程也是与函数有相同之处的,进程之间也是可以相互调用的。程序之间相互调用带来的好处之一。那么下面就将这部分内容扩展。
121 0
|
11月前
|
人工智能 Java 程序员
一文彻底搞清楚C语言的函数
本文介绍C语言函数:函数是程序模块化的工具,由函数头和函数体组成,涵盖定义、调用、参数传递及声明等内容。值传递确保实参不受影响,函数声明增强代码可读性。君志所向,一往无前!
500 1
一文彻底搞清楚C语言的函数
|
算法 C语言
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
本文档介绍了如何编写两个子函数,分别求任意两个整数的最大公约数和最小公倍数。内容涵盖循环控制与跳转语句的使用、最大公约数的求法(包括辗转相除法和更相减损术),以及基于最大公约数求最小公倍数的方法。通过示例代码和测试说明,帮助读者理解和实现相关算法。最终提供了完整的通关代码及测试结果,确保编程任务的成功完成。
696 15
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
|
C语言
【C语言程序设计——函数】亲密数判定(头歌实践教学平台习题)【合集】
本文介绍了通过编程实现打印3000以内的全部亲密数的任务。主要内容包括: 1. **任务描述**:实现函数打印3000以内的全部亲密数。 2. **相关知识**: - 循环控制和跳转语句(for、while循环,break、continue语句)的使用。 - 亲密数的概念及历史背景。 - 判断亲密数的方法:计算数A的因子和存于B,再计算B的因子和存于sum,最后比较sum与A是否相等。 3. **编程要求**:根据提示在指定区域内补充代码。 4. **测试说明**:平台对代码进行测试,预期输出如220和284是一组亲密数。 5. **通关代码**:提供了完整的C语言代码实现
298 24
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
699 16
|
存储 编译器 C语言
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
577 3