C语言基础-进程相关
进程的概念
程序
- 存放在磁盘上的 指令和数据的有序集合(文件)
- 静态的
进程
- 执行一个程序所分配的资源总称
- 进程是程序一次执行过程
- 动态的,包括创建、调度、执行和消亡
进程控制块(pcd)
- 进程标识PID
- 进程用户
- 进程状态、优先级
- 文件描述符表
进程类型
- 交互进程:在shell下启动。以在前台运行,也可以在后台运行
- 批处理进程:和终端无关,被提交到一个作业队列中,以便顺序执行
- 守护进程:和终端无关一直在后台运行
进程状态
- 运行态:
- 正在运行
- 准备运行
- 等待态:进程在等待一个事件的发生或某种资源
- 可终端
- 不可中断
- 停止态:进程被中止,收到信号后可继续运行
- 死亡态:以终止的进程,但pcb没有被释放
查看进程信息
- ps 查看系统进程快照
- top 查看进程动态信息
- /proc 查看进程详细信息
修改进程优先级
- nice 按用户指定的优先级运行进程
- nice -n num ./test
- num范围是 -20~19 越低优先级越高
- 普通用户是没办法指定负数的优先级
- renice 改变正在运行进程的优先级
- renice -n num pid
- 普通用户是没办法提高该优先级,只能进行降低
进程相关
- jobs 查看后台进程
- bg 将挂起的进程在后台运行
- bg id
- id是jobs的后台进程编号
- fg 吧后台运行的程序放到前台来运行
- fg id
- id 是jobs的后台进程编号
- ctrl + z 将前台进程放到后台挂起
C与进程相关函数
父子进程
- 子进程继承了父进程的内容
- 父子进程有独立的地址空间,互不影响
- 若父进程先结束
- 子进程成为孤儿进程,被init进程收养
- 子进程变成后台进程
- 若子进程先结束
- 父进程如果没有及时回收,子进程变成僵尸进程
进程创建 - fork
#include <unistd.h> pid_t fork(void); // 创建新的进程,失败返回-1 // 成功时父进程返回子进程的进程号,子进程返回0
创建完子进程后,从fork后下一条语句开始执行;
父子进程的执行顺序不定
父子进程可以多次调用fork
获取当前进程 - getpid
#include <unistd.h> pid_t getpid(void);
demo:创建子进程
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
pid_t pid;
if ((pid = fork()) < 0) {
perror("fork");
return -1;
} else if (pid == 0) {
printf("child process: my pid is :%d\n",getpid());
}else{
printf("parent process :%d\n",getpid());
}
}
/* 输出:
parent process :7124
child process: my pid is :7125
*/
结束进程
#include <stdlib.h> void exit(int status); #include <unistd.h> void _exit(intstatus); // 引入的头文件不同 //*** exit结束进程时会刷新(流)的缓冲区 // _exit结束时不会刷新缓冲区 可能会造成流文件丢失
exit(0) // 退出当前程序
进程资源回收
- 子进程结束时由父进程回收
- 孤儿进程由init进程回收
- 若没有及时回收会出现僵尸进程
#include <unistd.h> pid_t wait(int *status); // status指定保存子进程返回值和结束方式的地址 // status为NULL表示直接释放子进程PCB,不接受返回值 // 成功时返回回收的子进程号 // 失败是返回EOF // 若子进程没有结束,父进程会一直阻塞 // 若有多个子进程,那个先结束就先回收
Demo:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
pid_t pid;
int status = 0;
if ((pid = fork()) < 0) {
perror("fork");
return -1;
} else if (pid == 0) {
printf("child process: my pid is :%d\n", getpid());
sleep(1);
exit(2);
} else {
wait(&status);
printf("%x\n", status);
printf("parent process :%d\n", getpid());
}
}
进程回收的另一种方式
#include <unistd.h> pid_t waitpid(pid_t pid,int * status,int option); // pid可用于指定回收那个子进程或任意子进程 // pid= -1 代表当前进程的任意子进程 // status指定用于保存子进程返回值和结束方式的地址 // option指定回收方式,0或WNOHANG // 0表示阻塞一直等到进程结束 // WNOHANG表示非阻塞 // 成功时返回子进程的pid或0 0表示当前子进程还未结束 // 失败时返回EOF
进程返回值和结束方式
- 子进程通过exit、_exit/return返回某个值(0~255)
- 父进程调用wait(&status)回收
WIFEXITED(status) // 判断子进程是否正常结束 0为false 1为true
WEXITSTATUS(status) // 获取子进程返回值
WIFSIGNALED(status) // 判断子进程是否被信号结束
WTERMSIG(status) // 获取结束子进程的信号类型
守护进程
- 守护进程(Deamon)是linux三种进程类型之一
- 通常是在系统启动时运行,系统关闭时结束
- linux系统中大量使用,很多服务程序以守护进程方式运行
会话、控制终端
- linux会话(session)、进程组的方式管理进程
- 每个进程属于一个进程组
- 会话是一个或多个进程组的集合。通常用户打开一个终端,系统会创建一个会话。所有通过该终端运行的进程都属于这个会话
- 终端关闭时,所有相关进程会被结束
守护进程特点
- 始终在后台运行
- 独立于任何终端
- 周期性的执行某种任务或等待处理特定事件
守护进程创建
- 创建子进程,父进程退出--子进程变为孤儿进程,被init进程收养;子进程在后台运行(还是会依附于当前终端)
if (fork() > 0) {
exit(0);
}
- 子进程创建新会话--子进程称为新的会话组长;脱离原先的终端
- 更改当前工作目录--守护进程一直在后台运行,其工作目录不能被卸载一般指向根目录(/)或(tmp)
chdir("/"); // 普通用户指向这个目录可读
chdir("/tmp"); // 所有用户都是最高权限
- 重设文件权限掩码--文件权限掩码设置为0;只影响当前进程
#include <sys/stat.h>
if (umask(0) < 0) {
exit(-1);
}
- 关闭父进程打开的文件描述符--已脱离终端 stdin/stdout/stderr 无法再使用
#include <zconf.h>
for (int i = 0; i < getdtablesize(); ++i) {
close(i);
}
Demo:每隔1秒写入时间到time.log
exec函数族
- 进程调用exec函数族执行某个程序
- 进程当前内容被指定的程序替换
- 实现让父子进程执行不同程序
- 父进程创建 子进程
- 子进程调用exec函数族
- 父进程不受影响
通过shell命令去执行其他程序
#include <unistd.h> int execl(const char *path,const char*arg,...); / int execlp(const char *file,const char *arg,...); // 成功执行指定的程序 // 失败返回EOF // path找到要执行程序的名称(要包含路径) // arg...传递给执行程序的参数表 // file 执行程序的名称,在$PATH中查找
Demo:
if (execl("/bin/ls", "ls", "-a","/etc") < 0) {
perror("excel");
}
if (execlp("ls", "ls", "-a","/etc") < 0) {
perror("excel");
}
通过shell命令去执行其他程序 参数为数组
#include <unistd.h> int execv(const char * path,char * const argv[]); int execvp(const char * file,char * const argv[]); // 成功时执行指定的成 // 失败返回EOF // arg... 封装成指针数组 比上面exec更加灵活
char *arg[] = {"ls", "-a", "/etc",NULL};
if (execv("/bin/ls", arg) < 0) {
perror("excel");
}
char *arg[] = {"ls", "-a", "/etc",NULL};
if (execvp("ls", arg) < 0) {
perror("excel");
}
通过shell命令去执行其他程序 字符串
#include <stdlib.h> int system(const char * command); // 会自动创建一个子进程去执行command // 成功返回command指令的返回值 // 失败返回EOF
<style></style>
