C语言进程(第一章进程基础,fork()函数,pid_t, pid, getpid())
简介
当我们用c编程构建一个应用时,常常会遇到这样的场景:需要在程序中同时完成几项任务,如处理数据、打印输出、和读取用户输入等。 这种需要同时进行多项任务效率更高的需求,就是“并发”。
一般由操作系统负责协调和分配计算机资源, 管理“进程”和“线程”。在c语言中,我们可以使用进程和线程来实现并发执行的目的。
- 进程(process) 是操作系统管理资源,供程序运行时需要访问和控制的运行空间和内存地址。每个进程拥有自己的内存空间、系统资源、打开文件以及与其他进程的互动等等。
我们常把整个程序作为一个进程来处理,但是一个程序同时可能包含多个运行实例,即多个进程。 比如的浏览器中同时执行浏览网页、下载文件、播放视频等很多任务,那这些任务就会在不同的进程中执行。
入门
相关在线编辑网站:https://www.ideone.com/whPQYr
当学习进程和线程这些概念之后,下面是一些适合入门的C语言案例如下:
- 创建一个简单的进程
#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站也叫极客李华。大家喜欢也可以关注一下