进程与线程
进程
程序概念:
存放在磁盘上的指令和数据的有序集合(文件)
静态的
进程概念:
执行一个程序所分配的资源的总称
进程就是程序的一次执行过程
动态的,包括创建、调度、执行和消亡
进程包括代码、用户数据和 系统数据(进程控制块、cpu寄存器的值、堆栈)
进程控制块(pcb)
进程标识PID
进程用户
进程状态、优先级
文件描述符表
进程类型
交互进程:在shell下启动。以在前台运行,也可以在后台运行。
批处理进程:和在终端无关,被提交到一个作业队列中以便顺序执行。
守护进程:和终端无关,一直在后台运行。
进程状态
运行态:进程正在运行或者准备运行
等待态:进程在等待一个事件的发生或某种系统资源 – 分为可中断和不可中断两种状态
停止态:进程被终止,收到信号后可以继续运行
死亡态:已终止的进程,但是pcb没有被释放(僵尸进程)
查看进程信息
ps 查看系统进程快照 ps -ef | grep ps aux(显示进程当前状态)
top 查看进程动态信息 top
/proc 查看进程详细信息
创建进程 - fork
pid_t fork(void);
以下代码在linux中运行:
#include <unistd.h> #include <stdio.h> int main() { pid_t pid; pid = fork(); if(pid < 0) { perror("fork"); return -1; } if(pid == 0) { printf("child process; my pid is %d\n", getpid()); } else { printf("parent process; my pid is %d\n", getpid()); } }
父子进程:
1.子进程继承了父进程内容
2.父子进程有独立的地址空间,互不影响
3.若父进程先结束
○ 子进程成为孤儿进程,被init(1)进程收养
○ 子进程变成后台进程
4.若子进程先结束
○ 父进程如果没有及时回收,子进程变成僵尸进程
子进程从何处开始运行? 从fork()之后语句开始执行。
父子进程谁先执行? 不确定,由内核调度决定
父进程能否多次调用fork()? 子进程呢? 都可以
结束进程 - exit/_exit
#include <stdlib.h> #include <unistd.h> void exit(int status); void _exit(int status); // 丢弃缓冲区
结束当前进程,并且将status 返回
exit结束进程时会刷新(流)缓冲区
以下代码在linux中运行:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { printf("aaa"); _exit(0); printf("bbbb"); }
exec函数族
调用exec函数族执行某个程序
进程当前内容被指定程序替换
实现让父子进程执行不同的程序
● 父进程创建子进程
● 子进程调用exec函数族
● 父进程不受影响
进程 - execl/execlp
int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...);
成功时执行置顶程序;失败时候返回EOF
path 执行程序名称,包含路径
arg… 传递给执行程序的参数列表
file 执行程序名称,在PATH中查找
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { if(execl("/bin/ls", "ls", "-al", "/etc", NULL) < 0) { perror("execl"); } }
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { if(execlp("ls", "ls", "-al", "/etc", NULL) < 0) { perror("execlp"); } }
进程 - execv/execvp
int execv(const char *path, char *const arg[]); int execvp(const char *file, char *const arg);
成功时执行置顶程序;失败时候返回EOF
arg… 封装成指针数组的形式
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { char *argv[] = { "ls", "-al", "/etc", NULL}; if(execv("/bin/ls",argv) < 0) { perror("execv"); } if(execvp("ls",argv) < 0) { perror("execvp"); } }
进程 - system
int system(const char *command);
进程回收
● 子进程结束时候由父进程回收
● 孤儿进程由init进程回收
● 若没有及时回收会出现僵尸进程
进程回收 - wait
pid_t wait(int *status);
成功时候返回进程好;失败时候返回EOF
若子进程没有结束,父进程一直阻塞
若有多个子进程,则哪个先结束先回收哪个子进程
status 置顶保存子进程返回值和结束方式的地址
status 为NULL标志直接释放子进程的PCB
nclude <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main() { int status; pid_t pid; if((pid = fork()) < 0) { perror("fork"); return -1; } if(pid == 0) { sleep(1); exit(2); } else { wait(&status); printf("%x\n", status); } }
子进程通过exit/_exit/return返回某个值(0-255);
父进程调用wait(&status)回收
● WIFEXITED(status) 判断子进程是否正常结束
● WEXITSTATUS(status) 获取子进程的返回值
● WIFSIGNALED(status) 判断子进程是否被信号结束
● WTERMSIG(status) 获取结束子进程的信号
进程回收 - waitpid
pid_t waitpid(pid_t pid, int *status, int option);
成功时返回回收的子进程的pid或0;失败时返回EOF
pid可用于指定回收哪个子进程或者任意子进程
status指定用于保存子进程返回值和结束方式的地址
option指定回收方式 0或者 WNOHANG
waitpid(pid, &status, 0); waitpid(pid, &status, WNOHANG); waitpid(-1, &status, 0); waitpid(-1, &status, WNOHANG);
守护进程
守护进程(Daemon)是linux三种进程类型之一
通常在系统启动时运行,系统关闭时结束
Linux系统中大量使用,很多服务程序以守护进程形式运行
特点
● 始终在后台运行
● 独立于任何终端
● 周期性的执行某种任务或者等待处理特定事件
守护进程 - 会话、控制终端
● Linux以会话(session)、进程组的方式管理进程
● 每个进程属于一个进程组(子进程和父进程在同一个进程组)
● 会话是一个或者多个进程组的集合。通常用户打开一个终端时,系统会创建一个会话。所有通过该终端运行的进程都属于这个会话
● 终端关闭时候,多有相关的进程都会被结束
守护进程 - 创建
1.创建子进程,父进程退出
if(fork>0) { exit(0); }
● 子进程变成孤儿进程,被init进程收养
● 子进程在后台运行
2.子进程创建新会话
if(setsid() < 0) { exit(-1); }
● 子进程成为新的会话组长
● 子进程脱离原先的终端
3.更改当前工作目录
chdir("/"); chdir("/tmp");
● 守护进程一直在后台运行,其工作目录不能被卸载
● 重新设定当前工作目录cwd
4.重设文件权限掩码
if(umask(0) < 0) { exit(-1); }
● 文件权限掩码设置为0
● 只影响当前进程
5.关闭打开的文件描述符
for(int i = 0; i < getdtablesize(), i++) { close(i); }
● 关闭所有从父进程继承的打开文件
● 已经脱离终端, stdin/ stdout/ stderr无法在使用
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <time.h> #include <sys/stat.h> int main() { pid_t pid; FILE *fp; time_t t; if((pid = fork()) < 0) { perror("fork"); exit(-1); } if(pid > 0) { exit(0); } setsid(); umask(0); chdir("/tmp"); for(int i = 0; i < getdtablesize(); i++) { close(i); } if((fp = fopen("time.log","a+")) == NULL) { perror("fopen"); exit(-1); } while(1) { time(&t); fprintf(fp, "%s", ctime(&t)); fflush(fp); sleep(1); } }