时间编程
1. 时间流
- 返回值或输出参数得到时间流:time_t,长整型。
- 常见函数:
- time_t tm = time(NULL);
- ctime(&tm);
- struct tm *pt = localtime(&tm);
2. 时间流转字符串格式
- 带换行符。
3. 时间流转时间结构体对象
struct tm {
int tm_sec; // 秒,0-59
int tm_min; // 分,0-59
int tm_hour; // 时,0-23
int tm_mday; // 日,1-31
int tm_mon; // 月,0-11,使用时 +1
int tm_year; // 年,从1900开始,使用时 +1900
int tm_wday; // 星期,0-6,0表示星期日
int tm_yday; // 一年中的第几天,0-365
int tm_isdst; // 夏令时标志
};
注意:月份要 +1,年份要 +1900。
进程基础
1. 进程概念
- FreeRTOS 里面多任务,进程 === 任务。
- 单任务:MCU 无系统裸机程序 while(1) { … }。
- 多任务:同时运行多个程序,如手机系统多任务操作系统。
- Linux 任务叫进程,多任务多进程。
- 程序:静态,可执行文件,标本。
- 进程:动态,在内存里程序一次执行,有生命周期。
- 如何实现多进程?
- 单核 1 个 CPU:假并发,宏观并行,微观串行,充分利用 CPU。
- 多核多个 CPU:真并发。
- 时间片轮换、调度策略、优先级。
- CPU 时间片:5ms、5ms、5ms、5ms,4 个进程 20ms。
- 大厨比喻:锅、锅、锅、锅。
- 任务调度具有不确定性。
- 调度队列,进程是基本的调度单位。
2. 进程的内存映像
- 32 位系统:4GB = 用户空间 3GB + 内核空间 1GB。
- 虚拟地址与物理地址映射。
- 物理内存 8GB,充分利用内存。
3. 获取进程号
- 每个进程都会有一个对象来描述:struct task_struct,进程描述符。
- 该结构体有很多成员,其中:
- pid_t pid; 进程号,pid_t 数据类型是整型。
- 获取进程号的函数:
- getpid():获取当前进程的进程号。
- getppid():获得父进程号。
- pid = 1:祖先进程 init 进程。
- pid = 0:特殊进程,空闲进程,没有父进程。
4. 进程树
pstree
pstree -p # 带进程号
- 交互进程:都是在终端里面运行的,由终端进程创建的子进程。
- 进程分类:
- 守护进程:后台脱离终端运行,不使用标准文件,输出写入日志文件。
- 批处理:脚本运行。
- 进程的运行可在用户空间和内核空间中。
5. 创建进程
5.1 方法 1
./hello
5.2 方法 2:fork 函数调用来创建进程
- fork:叉子,分叉。
- 进程间地址完全独立,而线程间地址共享。
- 分裂、细胞、复制 DNA。
printf("fork 前:我的PID=%d, 父PID=%d\\n", getpid(), getppid());
pid_t child_pid = fork();
if (child_pid > 0) {
// 父进程
printf("父进程:我自己PID=%d, 父PID=%d\\n", getpid(), getppid());
printf("父进程:fork返回的子进程PID=%d\\n", child_pid);
} else if (child_pid == 0) {
// 子进程
printf("子进程:我自己PID=%d, 父PID=%d\\n", getpid(), getppid());
printf("子进程:fork返回给我的=%d\\n", child_pid);
}
- pid = fork(); 1 个进程分裂为 2 个进程。
- 返回 2 次:
- 当 pid > 0:fork() 返回后执行后面的代码是执行父进程的代码,pid 的值是子进程的进程号。
- 当 pid = 0:fork() 返回执行后面的代码是执行子进程的代码。
- 当 pid = -1:fork 失败,设置 errno,调用 perror。
示例输出:
fork 前:我的PID=2765, 父PID=1994
父进程:我自己PID=2765, 父PID=1994
父进程:fork返回的子进程PID=2766
子进程:我自己PID=2766, 父PID=1
子进程:fork返回给我的=0
再次示例:
before fork() pid=4663 ppid=4162
after fork() pid=4663 ppid=4162
after fork() pid=4664 ppid=4663
父4663 子4664
before fork() pid=4685 ppid=4162
after fork() pid=4685 ppid=4162
after fork() pid=4686 ppid=4685
after fork() pid=4687 ppid=4685
after fork() pid=4688 ppid=4686
父4685 子4686 子4687 子4688
- 一个父进程退出,它的一个或多个子进程还在运行,那么这些子进程叫孤儿进程。
- 孤儿进程交给 init 进程(pid=1)所收养。
6. ps 输出格式定制方法
ps -o pid,ppid,state,tty,command
示例:
PID PPID S TT COMMAND
3343 3339 S pts/0 -bash
4776 3343 R pts/0 ps -o pid,ppid,state,tty,command
- 如果在代码中运行 shell 命令用 system 函数。
7. 僵尸进程
- 子进程先结束,父进程没结束,如果父进程没有给子进程收尸,那么子进程就是僵尸进程。
- 子进程结束后父进程要记得给子进程收尸,调用 wait 函数可得到子进程结束的状态信息并回收子进程剩余资源。
- 记住:父进程创建子进程后,记得给子进程收尸。
示例:
befor fork() pid=4707 ppid=4599
after fork() pid=4707 ppid=4599
after fork() pid=4708 ppid=4707
PID PPID S TT COMMAND
4599 4598 S pts/1 -bash
4707 4599 S pts/1 ./4_fork
4708 4707 Z pts/1 [4_fork] <defunct>
4710 4707 S pts/1 sh -c ps -o pid,ppid,state,tty,command
4711 4710 R pts/1 ps -o pid,ppid,state,tty,command
- 子进程还在,但变成了僵尸进程,因为没人收尸。
8. 创建子进程的目的
9. exec 函数族
- 实现替换子进程执行代码的函数。
- 原理:执行 exec 函数时,可替换子进程 .data、.bss、.stack、.heap 等等。
- 当 exec 函数执行完后,子进程除进程号相关信息以外其他全部被替换。
- 函数名特点:
- 带 p:参数 file 表示程序的文件名,例如 ls。
- 不带 p:参数 pathname 表示带路径的程序文件名,例如 /usr/bin/ls。
- 带 l:使用 … 参数,参数形式 arg1, arg2, …, NULL。
- 带 v:使用 argv[] 数组。
- 返回值:
- 执行成功,不返回了。
- 出错返回 -1,设置 errno。
10. wait 函数:给子进程收尸
- 父进程等子进程。
- 参数:wstatus 输出参数,获得子进程的退出码,子进程的 exit(退出码)。
- 说明:
- 父进程调用 wait 时,如果子进程没有结束,父进程就阻塞,直到子进程结束时返回。
- 阻塞:休眠,CPU 不会为阻塞的进程服务,类似 scanf、fgets、getchar 等,例如等设备就绪。
- 父进程调用 wait 可等待多个子进程中某一个子进程,要给所有子进程收尸,那么就要调用多个 wait。
- 等待多个子进程时,只写一个 wait 函数,wait 等待的是最先运行结束的那个子进程。
- 返回值:
- 成功,返回等待子进程的 pid。
- 失败,返回 -1,设置 errno。
11. waitpid 函数
- waitpid 函数专等某个 pid。
- waitpid(-1, &status, 0) === wait(&status)。
- 参数:
- pid > 0:要等待某个 pid 子进程结束。
- pid = -1:等价于 wait。
- wstatus:输出参数,获得子进程的退出码 exit(退出码)。
- options:
- 0:类似 wait,阻塞。
- WNOHANG:不阻塞,立即返回。
- 使用进程的组合:fork + exec + waitpid。
12. 实现一个 shell 命令解释器
- 实现类似 system 函数的功能。
./myshell
% ls
…
% pwd
..
% date
..
% ./xx
…
- buf 示例:
buf: l s \\n 0 0 0 0 …
strlen(buf) = 3
sizeof(buf) = 1024
buf[strlen(buf)-1] = '\\0'
13. 进程间通信
- IPC:Inter-Process Communication。
- 分类:
- 管道:传递数据。
- 信号:发通知。






