相关概念
- 程序: 编译好的二进制文件, 存放在磁盘上(占用的是物理内存空间), 不占用系统资源(CPU, 内存, 打开的文件, 设备, 锁...)
- 进程: 抽象的概念, 与操作系统原理联系紧密, 是活跃的程序, 占用系统资源, 在内存中执行.
由原来的单道程序设计转换成多道程序设计, 是CPU分成若干的时间碎片, 各个进程抢占资源去执行, 虽然程序有优先级高低, 但是只是抢占到的几率高; 即使是多核, 也是需要时才会开启其他核.
处理速度:寄存器->cache->内存->硬盘->网络->存储介质
其他概念: 串行和并发, CPU和MMU - 进程控制块 PCB 在/usr/src/linux-headers-3.15.0-30/include/linux/sched.h文件中可以查看struct task_struct 结构体定义, 包含部分定义: 进程id, 进程状态, 进程切换时需要保存和回复的CPU寄存器, 描述虚拟的地址空间信息, 当前工作目录, 文件描述符表, 用户id和组id, 会话和进程组, 资源上限(命令:ulimit -a 查看), umask掩码...
- 进程状态: 初始态(创建时, 非常短暂), 就绪态(有执行资格, 但是没执行权限, 需要抢占资源), 运行态(正在执行), 挂起态(没有执行资格和执行权限), 终止态
点我查看进程状态图
进程控制
程序默认的是单进程的; 下面介绍多进程的使用
创建进程函数: pid_t fork(void);(查看 Linux下使用命令:man 2 fork)
点击查看描述图
返回值: 失败返回-1, 父进程返回子进程ID, 子进程返回0
查看当前进程 ps aux | grep "要查找的程序"; ps ajx
刚fork完成时(父子进程之间) 1. 0-3G地址空间相同 2. PCB相同, 但是pid除外 3.其他内容由mmu映射到物理内存上, 读时共享, 写时复制
获取pid 和 ppid 对应的函数是getpid(); getppid();
//wait 函数回收子进程
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid;
pid = fork();
if(pid == -1) {
perror("fork error");
exit(-1);
} else if(pid > 0) {
printf("parent process\n");
} else if(pid == 0) {
printf("child process\n");
}
printf("mul process\n");
return 0;
}
回收进程
进程有孤儿进程, 僵尸进程
- 孤儿进程 : 子进程活着, 父进程死了, linxu中的init进程会领养孤儿
- 僵尸进程 : 子进程死了, 父进程正在忙, 没有去回收子进程
- 进程回收 : 代码附在下边
wait - 阻塞函数
pid_t wait(int* status);
调用一次回收一个子进程资源
返回值:
0: 回收的子进程的pid;
-1: 没有子进程可以回收了;
status -- 传出参数
获取退出时候的返回值
- 正常退出( return num; exit(num); exit(num);
)
WIFEXITED(status) > 0
获取返回值: WEXITSTATUS(status)
- 被信号杀死
WIFSIGNALED(status) > 0
获取杀死子进程的信号
WTERMSIG(status)
waitpid -- pid_t waitpid(pid_t pid, int *status, int options);
(可以设置非阻塞, 提高wait的效率)
pid: -1: 所有的子进程
0: 当前进程组的子进程0(pid): 回收指定进程的pcb
-pid: 回收不在当前进程组的子进程
opttions: 阻塞: 0
非阻塞: WNOHANG 可以设置为非阻塞
可以有针对性的回收某一个子进程资源 ;需要注意的是即使是非阻塞, 也需要循环来回收子进程
//wait函数回收子进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/wait.h>
int counter = 100;
int main(int argc, const char* argv[])
{
int number = 5;
int i;
pid_t pid;
for(i=0; i<number; ++i)
{
pid = fork();
if(pid == 0)
break;
}
if(i<number)
{
// 子进程
counter += 100;
printf("child pid = %d, ppid = %d\n", getpid(), getppid());
printf("counter = %d\n\n", counter);
//sleep(100);
exit(9);
}
else if(i == number)
{
counter += 300;
printf("parent pid = %d, ppid = %d\n", getpid(), getppid());
printf("counter = %d\n\n", counter);
sleep(1);
// 回收子进程
int status;
pid_t wpid;
while( (wpid=wait(&status)) != -1)
{
// 正常退出
if(WIFEXITED(status))
{
printf("porcess exit by number: %d\n", WEXITSTATUS(status));
}
// 被信号杀死
else if(WIFSIGNALED(status))
{
printf("process kill by signal: %d\n", WTERMSIG(status));
}
printf("child died pid = %d\n", wpid);
}
}
return 10;
}
//验证父子进程是否文件共享
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
int main(int argc, const char* argv[]) {
int fd = open("temp", O_CREAT | O_RDWR, 0664);
if(fd == -1) {
perror("open error");
exit(1);
}
pid_t pid = fork();
if(pid == -1) {
perror("fork error");
exit(1);
}
if(pid > 0) {
char* p = "123123123";
write(fd, p, strlen(p)+1);
} else if(pid == 0) {
// 睡1s保证父进程已经完成了文件的写操作
sleep(1);
char buf[1024];
lseek(fd, 0, SEEK_SET);
int len = read(fd, buf, sizeof(buf));
printf("%s\n", buf);
}
close(fd);
return 0;
}
//子进程执行不同任务, 父进程负责回收子进程
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main(int argc, const char* argv[])
{
int i = 0;
int number = 3;
pid_t pid;
for(i = 0; i<number; ++i)
{
pid = fork();
if(pid == 0)
{
break;
}
}
// 父进程
if(i == number)
{
sleep(2);
// 回收子进程
pid_t wpid;
int status;
while( (wpid = waitpid(0, &status, WNOHANG)) != -1 )
{
if(wpid == 0)
{
continue;
}
printf("child pid = %d\n", wpid);
if(WIFEXITED(status))
{
printf("return number: %d\n", WEXITSTATUS(status));
}
else if(WIFSIGNALED(status))
{
printf("exit by signal: %d\n", WTERMSIG(status));
}
}
}
else if(i == 0)
{
execl("/home/kevin/test/app", "app", NULL);
}
else if(i == 1)
{
execl("./error", "error", NULL);
}
else if(i == 2)
{
execlp("ps", "ps", "aux", NULL);
}
printf("over......\n");
return 0;
}
//waitpid 来回收子进程, 注意非阻塞时也需要循环去回收
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main(int argc, const char* argv[])
{
int num = 3;
int i = 0;
pid_t pid;
for(i=0; i<num; ++i)
{
pid = fork();
if(pid == 0)
{
break;
}
}
if(i == 0)
{
execlp("ps", "ps", "aux", NULL);
perror("execlp ps");
exit(1);
}
else if(i == 1)
{
execl("/home/kevin/test/app", "app", NULL);
perror("execl app");
exit(1);
}
else if(i == 2)
{
execl("./error", "error", NULL);
perror("execl error");
exit(1);
}
else if(i == num)
{
// 回收
int status;
pid_t wpid;
while( (wpid = waitpid(-1, &status, WNOHANG)) != -1 )
{
if(wpid == 0)
continue;
printf(" ----- child died pid = %d\n", wpid);
if(WIFEXITED(status))
{
printf("return value %d\n", WEXITSTATUS(status));
}
else if(WIFSIGNALED(status))
{
printf("died by signal: %d\n", WTERMSIG(status));
}
}
}
return 0;
}