欢迎光临
我们一直在努力

linux的时间编程

时间编程

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。
    • 分类:
      • 管道:传递数据。
      • 信号:发通知。

    赞(0)
    未经允许不得转载:171主机测评 » linux的时间编程
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址