在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
进程的概念 程序: 存放在磁盘上的指令和数据的有序集合(文件) 静态的 进程: 执行一个程序所分配的资源的总称 进程是程序的一次执行过程 动态的,包括创建、调度、执行和消亡 进程包含的内容 进程包含:正文段(代码段)、用户数据段、系统数据段 程序包含:正文段(代码段)、用户数据段 系统数据包含:进程控制块、CPU寄存器值、堆栈 进程控制块(PCB)包含: 进程标识PID 进程用户 进程状态、优先级 文件描述符表 CPU寄存器值: PC:program counter, 记录着下一条执行指令的地址 堆栈:所有的局部变量都是在栈中存在的 进程的类型 交互进程:在shell下启动。可以在前台运行,也可以在后台运行 批处理进程:和在终端无关,被提交到一个作业队列中以便顺序执行 守护进程:和终端无关,一直在后台运行 进程的状态 运行态:进程正在运行,或者准备运行 等待态:进行在等待一个事件的发生或某种系统资源,又分为可中断和不可中断 停止态:进程被中止,收到信号后可继续运行 死亡态:已终止的进程,但pcb没有没有被释放 查看进程信息 ps:查看系统进程快照 ps -ef : 查看系统中所有的进程信息 ps aux: 比ps -ef 多一个当前的进程状态信息,进程优先级 top:查看进程动态信息,每个3秒刷新一次 /proc:查看进程详细信息,在proc目录下以进程PID为名的目录,其中status文件保存着进程的详细信息,fd目录保存这使用的文件 改变进程优先级 nice: 按用户指定的优先级运行进程,nice的区间[-20, 19],默认值为0,值越小优先级越高,普通用户能够设置的最小值为0。 nice -n 2 ./a.out renice:改变正在运行进程的优先级,普通用户只能增加这个值。 renice -n 2 PID 前后台进程切换 jobs:查看后台进程 xdl@xdl-gj:~/C语言/thread$ ./a.out & [1] 22441 //1表示作业号 xdl@xdl-gj:~/C语言/thread$ jobs [1]- 运行中 ./a.out & bg:降挂起的进程在后台运行 fg:把后台运行的进程放到前台运行 xdl@xdl-gj:~/C语言/thread$ fg 1 ./a.out ^Z // ctrl +z 使进程挂起 创建进程 fork函数用于创建一个进程 #include <unistd.h> pid_t fork(void); 创建新的进程,失败时返回-1 成功时父进程返回子进程的进程号,子进程返回0 通过fork的返回值区分父进程和子进程 #include <stdio.h> #include <unistd.h> int main(int argc, char *argv[]) { pid_t pid; pid = fork(); printf("pid=%d\n", pid); if (pid < 0) { perror("fork"); return -1; } else if (pid == 0) { printf("child process: my pid is %d\n", getpid()); } else { printf("parent process: my pid is %d\n", getpid()); } return 0; } /* pid=6018 parent process: my pid is 6017 pid=0 child process: my pid is 6018 */ 父子进程 子进程继承父进程的内容 父子进程有独立的地址空间,互不影响 若父进程先结束 子进程称为孤儿进程,被init进程收养(linux启动内核之后自动创建的用户态进程PID为1) 子进程变成后台进程 若子进程先结束 父进程如果没有及时回收,子进程变成僵尸进程 子进程从何处开始运行? 从fork下一句指令开始,并没有执行fork语句 父子进程谁先执行? 不一定,一般父进程创建子进程后时间片没有用完则继续执行 父进程能否多次调用fork?子进程呢? 可以 结束进程 exit/_exit #include <stdlib.h> #include <unistd.h> void exit(int status); void _exit(int status); 结束当前进程并将status(低八位)返回 exit结束结束进程时会刷新(流)缓冲区 #include <stdio.h> #include <stdlib.h> // for exit #include <unistd.h> //for _exit int main(int argc, char *argv[]) { printf("this process will exit"); //exit(0);//会打印上一句 _exit(0);//不会打印 printf("never be displayed"); return 0; } exec函数族 进程调用exec函数执行某个程序 进程当前内容被指定的程序替换 实现让父子进程执行不同的程序 父进程创建子进程 子进程调用exec函数族 父进程不受影响 execl/execlp #include <unistd.h> int execl(const char *path, const char *arg, ...); int execlp(const char *file, const char *arg, ...); EOF path 执行的程序的名称,包含路径 arg... 传递给执行的程序的参数列表 file执行的程序的名称,在PATH中查找 执行ls命令,显示/etc目录下所有文件的详细信息 #include <stdio.h> #include <unistd.h> int main(int argc, char *argv[]) { pid_t pid; pid = fork(); printf("pid=%d\n", pid); if (pid < 0) { perror("fork"); return -1; } else if (pid == 0) { printf("child process: my pid is %d\n", getpid()); if (execl("/bin/ls", "ls", "-a", "-l", "/etc", NULL) < 0) { perror("execl"); return -1; } } else { printf("parent process: my pid is %d\n", getpid()); if (execlp("ls", "ls", "-al", "/etc", NULL) < 0 ) { perror("execlp"); return -1; } } return 0; } /* pid=6018 parent process: my pid is 6017 pid=0 child process: my pid is 6018 */ execv/execvp #include <unistd.h> int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); 成功时执行指定的程序;失败时返回EOF arg...封装成指针数组的形式 char *arg[] = {"ls", "-a", "-l", "/etc", NULL}; if(execv("/bin/ls", arg) < 0){ perror("execv"); } if(execvp("ls", arg) < 0){ perror("execvp"); } system #include <stflib.h> int system(const char *command); 成功时返回命令command的返回值;失败时返回EOF 当前进程等待command执行结束后才继续执行 进程回收 子进程结束时由父进程回收 孤儿进程由init进程回收 若没有及时回收会出现僵尸进程 wait函数 #include <unistd.h> pid_t wait(int *status); 成功时返回回收的子进程的进程号;失败时返回EOF 若子进程没有结束,父进程一直阻塞 若有多个子进程,哪个先结束就先回收 status指定保存子进程返回值和结束方式的地址 status为NULL表示直接释放子进程PCB,不接收返回值 wait pid函数 #include <unistd.h> pid_t waitpid(pid_t pid, int *status, int option); 成功时返回回收的子进程的pid或0(表示子进程还没有结束);失败时返回EOF pid可用于指定回收哪个子进程或任意子进程(-1表示任意子进程) status指定用于保存子进程返回值和结束方式 option指定回收方式,0(阻塞方式)或WNOHANG(非阻塞) 进程返回值和结束方式 子进程通过exit/_exit/return 返回某个值(0-255) 父进程调用wait(&status)回收 WIFEXITED(status) : 判断子进程是否正常结束 WEXITSTATUS(status): 获取子进程返回值 WIFSIGALED(status): 判断子进程是否被信号结束 WTERMSIG(status): 获取结束子进程的信号类型 #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h> int main(int argc, char *argv[]) { int status; pid_t pid; if ((pid = fork()) < 0) { perror("fork"); exit(-1); } else if (pid == 0) { sleep(1); exit(2); } else { wait(&status); printf("%d\n", status); } return 0; } /* 200 521 0010 0000 0000 低7为(0~7)表示进程结束的类型,0表示正常结束,非零表示信号结束 高8为(8~15)表示子进程正常结束时的返回值 */ 守护进程 守护进程(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、关闭打开的文件描述符 int i; for (i = 0; i < getdtablesize(); i++){ close(i); } 关闭所有从父进程继承的打开文件 已脱离终端,stdio,stdout,stderr无法在使用 创建守护进程,每隔1秒将系统时间写入文件time.log #include <stdio.h> #include <unistd.h> #include <time.h> #include <stdlib.h> #include <sys/stat.h> // for umask() int main(int argc, char *argv[]) { pid_t pid; FILE *fp; time_t t; int i; if ((pid = fork()) < 0) { perror("fork"); exit(-1); } else if (pid > 0) { exit(0); } setsid(); umask(0); chdir("/tmp"); for (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); } return 0; }
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论