进程
进程和程序
- 程序:死的,不占用系统资源(内存)
- 进程:活跃的程序
- 程序1:N进程
cpu
pcb进程控制块
- 本质:结构体:struct task_struct{…} — find/usr/src/ -name
- 存储目录位置: /usr/src/linux-headers…/sched.h
- 大致存储内容
- 进程状态
- id
- 当前工作目录
- 虚拟地址空间映射信息
- umask掩码
- 用户id,组id
- 文件描述符表
进程状态
-
就绪态:完成准备,等待cpu划分时间片
-
运行态:获取cpu时间片,正则运算
-
挂起态:等待cpu以外其他资源。
-
停止: 正常异常终止程序
-
使用env查看系统环境变量
-
环境变量语法格式: 名 = 值: 值: 值:
-
echo$ 环境变量名 查看对应值。如 echo $PATH
fork函数
pid_t fork(void);
成功:
// fork之后,产生一个子进程。
父进程:返回子进程id
子进程:返回0
失败:
例子:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<dirent.h>
void sys_err(const char*str)
{
perror(str);
exit(1);
}
int main(int argc,char* argv[])
{
printf("-------before fork----------\n");
pid_t pid = fork();
if(pid==-1)
sys_err("fork err");
if(pid==0) //子进程
{
printf("i'm a child 我的id = %d 父进程 = %d\n",getpid(),getppid());
}
else if(pid >0)
{
printf("父进程,我儿子id = %d,我的ID = %d 父父进程 = %d\n",pid,getpid(),getppid());
sleep(1);
}
printf("-------after fork----------\n");
return 0;
}
进程控制
-
fork之后,父子进程共同争cpu
-
ps|aux | grep 关键字 – 搜索关键字的进程
-
./a.out 进程的父进程bash
-
系统调用、库函数区别:
- 系统调用:1、访问内核数据结构2、访问硬件资源
- 库函数
循环创建n个子进程
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<dirent.h>
void sys_err(const char*str)
{
perror(str);
exit(1);
}
int main(int argc,char*argv[])
{
int i = 0;
for(i = 0;i<5;i++)
{
if(fork()==0)
break; //循环期间,子进程不参与
}
if(5 == i)
{
sleep(5);
printf("i'm a parent\n");
}
else
{
sleep(i);
printf("I'm %dth child\n",i+1);
}
return 0;
}
fork后父子进程异同
父子相同:
- 全局变量、.data、.text
父子不同:
- 进程id,fork返回值、进程运行时间、父进程id、定时器
读时共享,写时复制
- fork后,对于父进程的用户空间的数据,系统采用读时共享,写时复制
gdb调式父子进程
- 在fork函数调用之前:
- 跟踪父进程执行逻辑:set follow-fork-mode parent
- 子进程: set follow-fork-mode child
exec函数族
-
将当前进程的.text .data替换为索要加载程序的 .text .data。然后让进程从新的.text第一条指令开始执行。
-
exec函数族,一旦调用成功不会返回。失败才会,错误值为-1.errno。
execlp
-
p:path。自动借助环境变量path找寻可执行程序。
#include <unistd.h> extern char **environ; int execl(const char *pathname, const char *arg, ... /* (char *) NULL */);
实例:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<dirent.h>
void sys_err(const char*str)
{
perror(str);
exit(1);
}
int main(int argc,char* argv[])
{
pid_t pid = fork();
if(pid == 0)
{
execlp("ls","ls","-l","-F","-a",NULL);
perror("/bin/ls exec error");
exit(1);
}
else if(pid > 0)
{
sleep(1);
perror("parent\n");
}
return 0;
}
execl
#include <unistd.h>
extern char **environ;
int execl(const char *pathname, const char *arg, ...
/* (char *) NULL */);
直接指定药加载的程序绝对访问路径。可以是系统可以是执行文件,也可以是用户自定义的可执行文件
实例
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<dirent.h>
void sys_err(const char*str)
{
perror(str);
exit(1);
}
int main(int argc,char* argv[])
{
pid_t pid = fork();
if(pid == 0)
{
// execl("/bin/ls","ls","-l","-F","-a",NULL);
execl("./while","while","Aa","bb","cc",NULL);
perror("/bin/ls exec error");
exit(1);
}
else if(pid > 0)
{
sleep(1);
perror("parent\n");
}
return 0;
}
练习
-
编写程序,创建子进程,子进程使用exec函数,获取当前系统中的进程详细信息,打印到一个文件中。
-
ps aux > out
-
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<fcntl.h> #include<errno.h> #include<dirent.h> void sys_err(const char*str) { perror(str); exit(1); } int main(int argc,char * argv[]) { pid_t pid = fork(); if(pid == 0) { int fd = open("out",O_WRONLY|O_CREAT|O_TRUNC,0644); if(fd == -1) { sys_err("open err"); } dup2(fd,STDOUT_FILENO); execlp("ps","ps","a","u","x",NULL); perror("execlp error"); exit(1); } else if (pid > 0) { sleep(1); printf("I'm a parent\n"); } return 0; }
进程回收
- fork后的子进程,其父进程有义务在子进程结束时,回收该子进程。隔辈进程无回收关系。
- 进程终止:
- 关闭所有文件描述符
- 释放用户空间分配的内存
- 进程的pcb残留在内核。保存进程结束的状态。
孤儿进程
父进程先于子进程终止。子进程沦为孤儿进程。
使用命令:ps ajx查看 ppid(父进程id) pid(进程id)
僵尸进程
子进程终止。父进程没有终止,没有对子进程回收。在此期间,子进程为僵尸进程。
杀死进程:kill -9 进程id
wait回收
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
// 传出参数.回收进程的状态
pid_t waitpid(pid_t pid, int *wstatus, int options);
返回值:
成功,回收的进程pid
失败 -1,errno
- 函数的作用
- 阻塞等待子进程退出
- 回收子进程残留在内核的pcb
- 获取子进程的退出状态(正常,异常)
- 回收子进程退出状态:
- 正常退出
- 异常退出
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<dirent.h>
void sys_err(const char*str)
{
perror(str);
exit(1);
}
int main(int argc,char*argv[])
{
int status = 0;
pid_t wpid = 0;
pid_t pid = fork();
if(pid == -1)
{
sys_err("fork err");
}
else if(pid == 0)
{
printf("I'm a child pid = %d\n",getpid());
int a = 5/0;
sleep(1);
exit(73);
}
else
{
wpid = wait(&status);
}
if(wpid==-1)
{
sys_err("wait err");
}
if(WIFEXITED(status))
{
printf("I'm a parent,pid = %d child, exit code = %d\n",wpid,WEXITSTATUS(status));
}
else if(WIFSIGNALED(status))
{
//宏函数为真,说明子进程被信号终止
printf("I''m a parent, pid = %d child, killed by %d signal\n",wpid,WTERMSIG(status));
}
return 0;
}
waitpid回收
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
功能:
等待子进程终止,如果子进程终止了,此函数会回收子进程的资源。
参数:
pid : 参数 pid 的值有以下几种类型:
pid > 0 等待进程 ID 等于 pid 的子进程。
pid = 0 等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid 不会等待它。
pid = -1 等待任一子进程,此时 waitpid 和 wait 作用一样。
pid < -1 等待指定进程组中的任何子进程,这个进程组的 ID 等于 pid 的绝对值。
status : 进程退出时的状态信息。和 wait() 用法一样。
options : options 提供了一些额外的选项来控制 waitpid()。
0:同 wait(),阻塞父进程,等待子进程退出。
WNOHANG:没有任何已经结束的子进程,则立即返回。
WUNTRACED:如果子进程暂停了则此函数马上返回,并且不予以理会子进程的结束状态。(由于涉及到一些跟踪调试方面的知识,加之极少用到)
返回值:
waitpid() 的返回值比 wait() 稍微复杂一些,一共有 3 种情况:
1) 当正常返回的时候,waitpid() 返回收集到的已经回收子进程的进程号;
2) 如果设置了选项 WNOHANG,而调用中 waitpid() 发现没有已退出的子进程可等待,则返回 0;
3) 如果调用中出错,则返回-1,这时 errno 会被设置成相应的值以指示错误所在,如:当 pid 所对应的子进程不存在,或此进程存在,但不是调用进程的子进程,waitpid() 就会出错返回,这时 errno 被设置为 ECHILD;