ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
> Linux 进程是正在执行中的程序 [TOC] ## 1 进程简介 启动进程有两种方式: - 手工启动 - 前台启动:执行一条命令 - 后台启动:在命令后输入一个`&`符号 - 调度启动 - 例如定时执行,输入 `at 11:27 12/25/2020`,输入命令`ls -l`,再输入`Ctrl + D`退出;或者使用相对时间,输入`at now +5 min`;结果不会在终端回显,可以重定向到文件中查看 > Linux 常见进程操作命令 命令 | 作用 ---- | ---- ps | 查看系统中的进程 top | 动态显示系统中的进程 nice | 按用户执行的优先级运行 renice | 改变正在运行的进程的优先级 kill | 终止进程(包括后台进程) crontab | 用于安装、删除或者列出用于驱动 cron 后台进程的任务 bg | 将挂起的进程放到后台执行 fg | 把后台进程转到前台执行 ## 2 进程控制 系统会为每个新创建的进程分配一个唯一的正整数,称为进程标识符(PID),父进程的为(PPID),可以通过 `getpid` 和 `getppid` 获取 > Linux C 与进程相关的主要函数 函数名称 | 功能 ---- | ---- getpid | 获取当前进程的进程号 getppid | 获取当前进程的父进程号 exec 函数族 | 在进程中启动另一个进程执行 system | 在进程中开始另一个进程 fork | 从已存在的进程中复制一个新进程 sleep | 令当前进程进入睡眠状态,直到达到指定时间,或者被信号中断 exit | 正常终止进程,并把参数 status 返回给父进程,进程中的所有缓冲区数据会自动写回并关闭未关闭的文件 _exit | 立即终止进程,并把参数 status 返回给父进程 ,并关闭未关闭的文件,不会处理标准I/O缓冲区 wait | 暂停父进程,等待子进程运行完成 waitpid | 暂停父进程,等待子进程运行完成 ### 2.1 进程创建 **1. fork函数** > fork 函数示例(备注:vfork 函数可以保证子进程一定优先于父进程开始执行) ```c #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> int main(void) { pid_t pid; char *message; int n; pid = fork(); if (pid < 0) { // 进程创建失败 perror("fork failed"); exit(1); } else if (pid == 0) { // 子进程执行 message = "子进程执行"; n = 3; } else { // 父进程执行 message = "父进程执行"; n = 6; } for (; n > 0; n--) { printf(message); // 父进程与子进程交替打印 sleep(1); } return 0; } ``` **2. exec 函数族** ```c execl --- [把可变参数保存到以NULL结尾的指针函数中] ---> execv execle --- [把可变参数保存到以NULL结尾的指针函数中] ---> execve execlp --- [把可变参数保存到以NULL结尾的指针数组中] ---> execvp ---[依次在PATH环境变量指示的各目录中查找该程序] ---> execv --- [使用enviorn所指向的当前环境变量表] --> execve 系统调用 exec 函数族的六个成员函数的语法(头文件`#include <unistd.h>`),省略了`exec`前缀 int l (const char *path, const char *arg, ...) // 列表传递 int v (const char *path, char const *argv[]) // 字符串传递 int le (const char *path, const char *arg, ..., char *const envp[]) int ve (const char *path, char *const argv[], char *const envp[]) int lp (const char *file, const char *arg, ...) int vp (const char *file, char *const argv[]) ``` ### 2.2 进程终止 有五种方式使进程终止: - 正常终止 - 在 main 函数内执行 return 语句,等效于调用 exit - 调用 exit 函数(由ANSI C定义),其操作包括调用各终止处理程序,然后关闭所有标准I/O流等 - 异常终止 - 调用 abort - 由一个信号终止 ### 2.3 僵尸进程 一个已经终止运行、但其父进程尚未对其**善后处理**(获取终止子进程的有关信息、释放它仍然占用的资源)的进程被称为僵尸进程(zombie),ps 将僵尸进程的状态显示为`Z` 可以在父进程中调用 `wait` 或 `waitpid` 函数,使子进程比父进程早终止,而让父进程有机会了解子进程终止时的状态 - wait 用于使父进程阻塞,直到一个子进程终止或者该进程接到了一个指定的信号为止 - waitpid 有若干选项,也能支持作业控制,wait 只是 waitpid 的一个特例 **init** 进程是Linux启动后创建的第一个进程,当一个程序退出,它的子进程会被 init 进程继承 ### 2.4 守护进程 守护进程最重要的特性是**后台运行**,其次守护进程必须与其运行前的环境隔离开来(包括未关闭的文件描述符、控制终端、会话和进程组、工作目录以及文件创建掩码),可以通过 `/etc/rc.d`、`crond 进程`或用户终端启动 输入 `ps -aux`后: - init 系统守护进程,PID 为 1,负责启动各层次特定的系统服务,这些服务通常是由它们自身的守护进程实现的 - keventd 守护进程,为在内核中运行计划执行的函数提供进程上下文 - kswapd 守护进程,页面置换守护进程 - bdflush 和 kupdated 守护进程,Linux内核使用 - portmap 端口映射守护进程,提供将RPC程序号映射为网络端口号的服务 - syslogd 守护进程,可帮助操作人员把系统消息记入日志 - inetd 守护进程(xinetd),监听系统网络接口,以便取得来自网络的对各种网络服务进程的请求 - nfsd,lockd,rpciod 守护进程,提供对网络文件系统(NFS)的支持 - cupsd 守护进程,打印脱机进程,处理对系统提出的所有打印请求 编写守护进程的要点: 1. 创建子进程,终止父进程 2. 在子进程中创建新会话(例如setsid) 3. 改变工作目录(chdir) 4. 重设文件掩码(unmask(0)) 5. 关闭文件描述符(遍历NOFILE,close(i)) ```c void init_daemon(void); int main() { FILE *fp; time_t t; init_daemon(); while (1) { sleep(10); if (fp=fopen("xxx.log") >= 0) { t = time(0); fprintf(fp, "%s", asctime(localtime(&t))); fclose(fp); } } } void init_daemon(void) { pid_t child1, child2; int i; child1 = fork(); if (child1 > 0) { exit(0); // 1. 创建子进程,终止父进程 } else if (child < 0) { perror("创建子进程失败"); exit(1); } setsid(); // 2. 在子进程中创建新会话 chdir("/tmp"); // 3. 改变工作目录到 /tmp umask(0); // 4. 重设文件创建掩码 for(i = 0; i < NOFILE; ++i) { close(i); // 5. 关闭文件描述符 } return; } ```