目录
进程创建 - fork函数
在linux中 fork 函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程
#include <unistd.h>
pid_t fork(void);
返回值:如果成功,子进程中返回0,父进程返回子进程 pid;如果出错返回 -1
使用 for 循环批量创建子进程
#include <unistd.h>
#include <stdlib.h>
#define N 5 // 创建五个子进程
void runchild()
{
int cnt = 10;
while (cnt--)
{
printf("I am child,pid : %d,ppid : %d\n", getpid(), getppid());
sleep(1);
}
}
int main()
{
int i = 0;
for (; i < N; i++)
{
pid_t id = fork();
if (id == 0)
{
runchild();
exit(0);
}
}
sleep(1000);
return 0;
}
进程终止
进程退出场景无非 3 种情况:
- 代码运行完毕,结果正确
- 代码运行完毕,结果不正确
- 代码异常终止
对于第 1 和第 2 两种情况:
为什么 main 函数总是返回 0,它返回给谁?返回 1、2 可不可以?
0 是 main 函数的退出码,表征 main 函数的运行结果正确,0 通常代表 success。main 函数的返回码返回给 bash (父进程),一般父进程要关心子进程的退出码,但父进程只关心子进程结果不正确时的退出码,对于子进程结果正确时的退出码,父进程不关心。子进程结果正确只有一种情况,而子进程结果不正确可能有无数种情况,可以用 return 一个非 0 数字表征不同的出错原因,return 的数字,也叫错误码,可以在 bash 输入 echo $? 查看最近运行完毕的子进程的退出码。为什么要有退出码,因为不 是所有程序都要往显示器打印结果。有时候,如果退出码为 0、1、2,父进程知道是什么含义,但人不知道,所以,要有可以将退出码转换为文字信息的工具,这个工具就是 strerror 函数
strerror 函数
这个函数的参数是退出码,返回值是一个字符串,即错误信息,使用时要包含头文件 string.h
#include <string.h>
int main()
{
int i = 0;
for (; i < 200; i++) printf("%d: %s\n", i, strerror(i));
return 0;
}
部分输出结果:
0: Success
1: Operation not permitted
2: No such file or directory
3: No such process
4: Interrupted system call
5: Input/output error
6: No such device or address
7: Argument list too long
8: Exec format error
9: Bad file descriptor
10: No child processes
11: Resource temporarily unavailable
12: Cannot allocate memory
13: Permission denied
14: Bad address
15: Block device required
16: Device or resource busy
17: File exists
18: Invalid cross-device link
19: No such device
故意打开一个不存在的文件,验证 strerror 提供的错误码信息:

全局变量 errno
我们在使用 C 语言提供的库函数时,可能会遇到执行失败的情况,比如用 fopen 打开一个不存在的文件,或者用 malloc 申请超过内存大小的空间,此时它们返回 NULL,我们知道它执行失败了,那如果我们想知道具体的失败的原因,该怎么办呢?原来,C 语言提供的库函数在执行失败时都会把全局变量 errno 设置为特定的数值,表征失败的原因(与 strerror 的对应关系一致),因为 errno 只有一个,所以 errno 存储的是最近的错误信息。使用全局变量 errno 要包含头文件 errno.h
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
char* p = (char*)malloc(1000 * 1000 * 1000 * 4);
int ret = 0;
if (p == NULL)
{
printf("malloc error: %d: %s\n", errno, strerror(errno));
ret = errno;
}
else
{
// 使用申请的内存
}
return ret;
}
输出信息:

exit 函数
exit 函数在 main 函数内使用,与 return 语句等价,exit 函数的真正作用是:在任意位置被调用,都可以使进程带着退出码退出,退出码就是 exit 函数的参数。比如在自定义函数中,使用 return 代表函数返回值,exit 代表终止整个程序。
void show()
{
printf("enter show\n");
exit(0);
printf("end show\n");
}
int main()
{
show();
printf("Hello Linux!\n");
return 0;
}
程序只输出了 enter show。
_exit 系统调用
exit 是库函数,最终会调用 _exit 来终止程序,在调用 _exit 之前,会做一些其他的工作,比如将缓冲区的信息打印到屏幕,而 _exit 是系统调用,在调用处直接终止程序,不做其他工作。

验证 _exit 的例子:
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("you can see me");
sleep(1);
_exit(11);
}
说明:printf 输出的内容不加 \n 是为了让信息暂时存储在缓冲区,sleep(1) 的作用是让程序没那么快结束,以便观察结果。最后没有输出 you can see me,说明 _exit 确实是直接终止程序。
printf 函数一定是先把数据写入缓冲区,在合适的时候(遇到 \n,程序结束)再进行刷新,从 exit 和 _exit 的区别可以推断:缓冲区绝对不在操作系统的内核,因为如果在,不管是 exit 还是 _exit 都要刷新缓冲区。
对于第 3 种情况:
问题:如果程序异常终止,退出码还有意义吗?
答案是没有意义,比如一个人考试作弊,那么讨论他考了多少分(退出码)完全没有意义。所以如果要检查一个程序的退出情况,1、先检查是否异常退出,如果是,为什么,如果不是 2、再检查退出码对应的错误信息。程序异常终止的典型情景:除0错误、野指针问题。这些问题会被操作系统识别,操作系统会向进程发送信号来解决问题。一些常见的信号:指令 kill -9 (SIGKILL)杀死进程,kill -8 (SIGFPE) 除0错误。可以人为的向进程发送信号,比如向一个没有任何错误的进程发送 SIGFPE 信号(命令行输入 kill -8 进程名),发现进程也被终止了,并且输出“Floating point exception”
3934

被折叠的 条评论
为什么被折叠?



