一、进程的相关概念 1.什么是进程?
进程:程序的一次执行过程就会创建一个进程,进程就是一个正在执行的任务,进程的所用信息通过task_struct(PCB)结构体记录,进程是分配资源的最小单位,进程是调度器调度的实体,每一个进程单独拥有自己的0-3G的(虚拟内存)空间,这0-3G包含堆、栈区、静态库。
还包括文件描述符的表。
程序执行结束的时候0-3G的空间就会被释放了,每一个进程都有自己的唯一的标识pid,pid就是进程号。
进程号pid:进程的唯一的标识,它是一个非负整数,由 *** 作系统随机分配。
pid就可以理解为进程的身份z号ID。
系统中正在执行的进程的进程号可以在/proc目录下查看。
注:在 *** 作系统上查看一个 *** 作系统能够分配的最大的进程号的命令cat /proc/sys/kernel/pid_max ===>131072
2.进程和程序有什么区别?进程:进程是动态的,具有生命周期,正在运行的进程在内存上存放。
程序:程序是静态的文件,没有生命周期,它是在磁盘上存放着。
在执行一个程序的时候就会创建一个进程。
进程的组成部分:进程控制块PCB,数据段,程序段组成
进程控制块(pcb):进程控制块其实在内核空间就是一个task_struct结构体,这个结构体记录了进程的所有的信息,例如进程的标号pid,进程的执行状态,进程的使用的内存空间等等。
数据段:堆、栈、.data、.bss
程序段:存放的是a.out的可执行程序的文本
示例:
struct task_struct{
进程的pid
进程的状态state
进程运行的内存
...
};
4.进程的种类
交互进程:交互进程是由shell控制的,交互进程可以是前台进程,也可以是后台进程(./a.out &)。
例如文本编辑器
批处理进程:批处理进程不属于某一个终端,在 *** 作系统内部维护了一个队列。
例如gcc编译程序的过程
守护进程:守护进程一直在后台执行,随着系统的启动而运行,随着系统的终止而终止。
例如:服务
5.一些特殊的进程0号进程(idle):在linux系统启动的时候,执行 *** 作系统代码的进程,当没有其他进程需要执行的时候就会运行0号进程
1号进程(init):1号进程是0号进程通过kernel_thread(内核)函数创建的,后面的进程都是拷贝这个进程得到的。
1号进程可以回收没有回收的资源
2号进程(kthreadd):2号进程是0号进程通过kernel_thread(内核)函数创建的,调度进程执行的进程
6.进程的状态进程的状态
D //不可中断的等待态(sleep)(不可被信号中断)
R //运行态
S //可中断的等待态(sleep)(可被信号中断)
T //停止态
t //代码追踪调试状态
X //死亡态
Z //僵尸,子进程死了,父进程没有为他收尸,如果父进程也死掉,init进程为它收尸
进程的附加状态
< 高优先级的进程
N 低优先级的进程
L 将进程的所在的内存所在内存区间
s 会话组组长
l 在进程中包含多线程
+ 前台进程
二、进程 *** 作相关的一些命令 1.ps命令
①ps -ef (查看进程的pid和ppid)
运行结果:
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 05:11 ? 00:00:06 /sbin/init auto noprompt
root 2 0 0 05:11 ? 00:00:00 [kthreadd]
root 3 2 0 05:11 ? 00:00:00 [rcu_gp]
root 4 2 0 05:11 ? 00:00:00 [rcu_par_gp]
//UID:进程所属的用户
//PID:进程的ID号
//PPID:父进程的ID号
//pidof a.out 可以查看a.out的进程号
②ps aux (查看进程的状态)
运行结果:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 225416 7896 ? Ss 05:11 0:06 /sbin/init auto noprompt
root 2 0.0 0.0 0 0 ? S 05:11 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? I< 05:11 0:00 [rcu_gp]
root 4 0.0 0.0 0 0 ? I< 05:11 0:00 [rcu_par_gp]
root 6 0.0 0.0 0 0 ? I< 05:11 0:00 [kworker/0:0H-kb]
root 9 0.0 0.0 0 0 ? I< 05:11 0:00 [mm_percpu_wq]
root 10 0.0 0.0 0 0 ? S 05:11 0:00 [ksoftirqd/0]
//PID:进程号
//%cpu:占用的cpu资源
//%mem:占用内存的资源
//VSZ:虚拟内存的大小
//RSS:实际的物理内存的大小
//TTY:如果没有对应的终端就是?
//STAT:进程的状态
③ps ajx
运行结果:
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
0 1 1 1 ? -1 Ss 0 0:06 /sbin/init
0 2 0 0 ? -1 S 0 0:00 [kthreadd]
2 3 0 0 ? -1 I< 0 0:00 [rcu_gp]
2 4 0 0 ? -1 I< 0 0:00 [rcu_par_gp]
2 6 0 0 ? -1 I< 0 0:00 [kworker/0:0H-kb]
2 9 0 0 ? -1 I< 0 0:00 [mm_percpu_wq]
2 10 0 0 ? -1 S 0 0:00 [ksoftirqd/0]
2 11 0 0 ? -1 I 0 0:20 [rcu_sched]
2 12 0 0 ? -1 S 0 0:00 [migration/0]
2 13 0 0 ? -1 S 0 0:00 [idle_inject/0]
//SID:会话:会话中有很多进程组,1前台进程组 ,多个后台进程组
//PGID:进程组:前台进程组 ,后台进程组
//PID:进程号
//PPID:父进程号
//TPGID:-1守护进程
注:通过top命令动态查看cpu和mem的使用率,htop(sudo apt-get install htop)更好看一点
信号:在linux系统中软件模拟的类似中断的一种机制。
当给一个进程发送信号的时候进程收到信号会执行这个信号的动作(默认,忽略,捕捉)
kill -l 查看信号
kill -信号号 进程的pid
如:kill -2 pid
kill -9 pid
示例:
pidof a.out //查看a.out的进程号
kill -9 10435 //杀死a.out的进程
killall a.out //将a.out运行的所有的进程全部杀掉
前台运行状态: ./a.out //前台运行程序(状态为R+)
后台运行状态: ./a.out & //后台运行程序(状态为R)
ps aux | grep a.out //查看进程运行的状态
进程前后台的切换:
./a.out //前台正在执行的进程
ctrl+z //让前台运行的程序先进入停止状态(状态为T)
kill -18 (a.out的pid) 或 bg 作业号 //后台运行进程
fg (作业号) //让后台运行的进程变为前台运行的进程,如果fg后面没有跟作业号,默认将倒数第一个后台进程变为前台进程
jobs -l //查看后台运行的所有的进程
4.特殊状态的进程僵尸进程:子进程结束之后,父进程没有为它收尸,此时子进程就是僵尸进程,只有当父进程结束收,被init收尸
孤儿进程:父进程先于子进程结束,子进程就是孤儿进程,孤儿进程被init进程收养。
三、多进程的C语言程序实现 1.进程是怎么创建得到的
在创建进程的时候得到子进程,子进程就是拷贝父进程得到的。
拷贝父进程的内容后pid是 *** 作系统随机分配的,会记录父进程的pid为ppid。
pid_t fork(void);
功能:创建子进程
#include
#include
参数:
@无
返回值:
>0 子进程的pid返回给了父进程
=0 子进程
-1 失败置位错误码
pid_t getpid(void);
功能:获取当前进程的pid
pid_t getppid(void);
功能:获取父进程的pid
示例:
#include
#include
#include
int main(int argc, char const *argv[])
{
pid_t pid;
pid = fork();
if(pid == -1){
perror("fork error");
return -1;
}else if(pid == 0){
//子进程的代码区
printf("我是子进程...pid = %d,ppid = %d\n",getpid(),getppid());
}else if(pid > 0){
//父进程的代码区
printf("我是父进程...cpid = %d,pid = %d,ppid = %d\n",pid,getpid(),getppid());
while(1);
}
//父子进程执行没有先后顺序,子进程如果执行结束了,父进程没有为他收尸,子进程变成僵尸进程
//将父进程结束的时候,init进程为们收尸
return 0;
}
注:父子进程执行顺序及时机:时间片轮询,上下文切换
3.进程结束方式(return/exit/_exit)return :return的作用:用来退出一个函数执行的栈空间,如果把return放在main函数可以具备退出进程的效果,但是如果把return放在子函数return就不能结束一个进程了。
exit:库函数
void exit(int status);
功能:退出一个进程,刷新缓冲区
#include
参数:
@status: 成功返回0 ,失败返回负数
EXIT_FAILURE 1
EXIT_SUCCESS 0
返回值:无
_exit:系统调用
void _exit(int status);
功能:退出一个进程,不刷新缓冲区
#include
参数:
@status: 成功返回0 ,失败返回负数
EXIT_FAILURE 1
EXIT_SUCCESS 0
返回值:无
示例:
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
pid_t pid;
pid = fork();
if (pid == -1)
{
perror("fork error");
return -1;
}
else if (pid == 0)
{
//子进程的代码区
printf("我是子进程pid = %d,ppid = %d",getpid(),getppid());
//exit(0); //程序在这里执行结束,并刷新缓冲区
_exit(0); //程序在这里执行结束,不会刷新缓冲区
while(1);
}
else if (pid > 0)
{
//父进程的代码区
printf("我是父进程cpid = %d,pid = %d,ppid = %d\n",pid,getpid(),getppid());
while (1)
;
}
return 0;
}
4.进程资源回收(wait/waitpid)
pid_t wait(int *wstatus);
功能:功能在父进程为子进程进程收尸处理
#include
#include
参数:
@wstatus:接受exit退出的时候给的值
NULL,不关注子进程接受的时候返回的值 ===>wait(NULL); 阻塞等待最先结束的子进程结束
返回值:成功返回收尸的pid,失败返回-1置位错误码
pid_t waitpid(pid_t pid, int *wstatus, int options);
功能:为子进程收尸
#include
#include
参数:
@pid:>0 :指定进程号收尸
-1 :为任意的子进程收尸
=0 :回收同组的子进程
<-1 : 如pid=-1234,对-1234取绝对值,回收1234这个组的进程的尸体
@wstatus:接受exit退出的时候给的值
NULL,不关注子进程接受的时候返回的值
@options: 0:阻塞
WNOHANG:非阻塞
返回值:成功返回收尸的pid,失败返回-1置位错误码
注:waitpid(-1,NULL,0) = wait(NULL);
示例:
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
pid_t pid;
pid = fork();
if (pid == -1)
{
perror("fork error");
return -1;
}
else if (pid == 0)
{
//子进程的代码区
printf("我是子进程pid = %d,ppid = %d\n",getpid(),getppid());
sleep(5);
exit(3); //0x14
}
else if (pid > 0)
{
int status;
//父进程的代码区
printf("我是父进程cpid = %d,pid = %d,ppid = %d\n",pid,getpid(),getppid());
//wait(&status);
waitpid(-1,NULL,0);
// sleep(7);
//waitpid(pid,NULL,WNOHANG);
printf("父进程收尸结束 status = %#x\n",status);
while (1);
}
return 0;
}
5.守护进程的创建
守护进程是后台进程,守护进程相当于系统的服务,随着系统的启动而启动,随着系统的终止而终止。
守护进程不依附终端,如果将终端关闭之后进程依然不能够退出(非守护进程终端结束后进程也就结束了)
需要用到的函数:setsid/chdir/umask/getdtablesize
pid_t setsid(void);
功能:创建一个会话
#include
#include
参数:
@无
返回值:成功返回会话id,失败返回-1置位错误码
int chdir(const char *path);
功能:切换目录
#include
#include
参数:
@path:目录的字符串
返回值:成功返回0,失败返回-1置位错误码
mode_t umask(mode_t mask);
功能:修改掩码
#include
#include
参数:
@mask:掩码的值
返回值:总是成功
int getdtablesize(void);
功能:获取进程中能打开的最大的文件描述符的值
#include
参数:
@无
返回值:返回最大文件描述符的值,总会成功
守护进程的创建流程图:
示例:
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
pid_t pid;
pid = fork();
if(pid == -1){
perror("fork error");
exit(EXIT_FAILURE);
}else if(pid == 0){
//子进程
int fd,num=0;
//1.只要父进程退出了,子进程就变成孤儿进程,被init收养
//2.设置setsid
if(setsid()==-1)
PRINT_ERR("setsid error");
//3.切换目录
if(chdir("/")==-1)
PRINT_ERR("chdir error");
//4.修改掩码
umask(0); //mode &(~umask)
//5.关闭不相关的文件描述符
for(int i=3;i
6.exec函数族
在使用fork创建多进程的时候,子进程只能够执行子进程代码块里面的程序,有人就设想能不能让子进程执行任意想执行的代码,答案是确定,通过exec函数族实现。
①execl/execv函数的使用(程序替换)
int execl(const char *path, const char *arg, .../* (char *) NULL */);
//int execv(const char *path, char *const argv[]);
//execv和execl的用法是一样的,只不过将execl的参数列表放入到argv的指针数组中即可
//char *const argv[] = {
// "a.out","参数1","参数2",... NULL,
//}
功能:执行在参数中给定的可执行程序
#include
参数:
@path:新的可执行程序的路径和可执行程序名字 "/home/linux/a.out"
@arg:可执行程序的参数 "a.out","参数1","参数2"...,NULL
返回值:失败返回-1置位错误码
示例:
#include
#include
#include
#include
#include
#include
char *const argvv[] = {
"ls","-l","/home/linux",NULL
};
int main(int argc, char const *argv[])
{
pid_t pid;
pid = fork();
if(pid == -1){
perror("fork error");
exit(EXIT_FAILURE);
}else if(pid == 0){
if(execv("/bin/ls",argvv)==-1)
//if(execl("/bin/ls", "ls","-l","/home/linux",NULL)==-1)
PRINT_ERR("execl error");
//这里的“11111111111”是不会打印的,因为整个程序都被ls的可执行程序替换掉了
printf("111111111111111111111111\n");
}else{
wait(NULL);
}
return 0;
}
②execlp/execvp函数(执行程序的时候可以使用PATH环境变量)
int execlp(const char *file, const char *arg, ... /* (char *) NULL */);
//int execvp(const char *file, char *const argv[]);
功能:在执行替换程序的时候,可执行程序的路径不需要写了 (PATH路径下的程序(echo ${PATH}))
#include
参数:
@path:新的可执行程序的路径和可执行程序名字 "a.out"
@arg:可执行程序的参数 "a.out","参数1","参数2"...,NULL
返回值:失败返回-1置位错误码
注:使用如下命令给路径:
export PATH=${PATH}:/home/linux/IO/day3/
/home/linux/ .bashrc
/etc/environment
示例:
#include
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
pid_t pid;
pid = fork();
if(pid == -1){
perror("fork error");
exit(EXIT_FAILURE);
}else if(pid == 0){
if(execlp("a.out", "a.out",argv[1],NULL)==-1)
PRINT_ERR("execlp error");
}else{
wait(NULL);
}
return 0;
}
③execle/execve函数(可以传递环境变量)
int execle(const char *path, const char *arg, ...
/*, (char *) NULL, char * const envp[] */);
//int execvpe(const char *file, char *const argv[], char *const envp[]);
功能:在执行新的可执行程序的时候,向这个新的可执行程序中传递envp的数组(环境变量)
#include
参数:
@path:新的可执行程序的路径和可执行程序名字 "a.out"
@arg:可执行程序的参数 "a.out","参数1","参数2"...,NULL
@envp:环境变量的数据
返回值:失败返回-1置位错误码
示例:
#include
#include
#include
#include
#include
#include
//在向env中传递环境变量的时候,不是非得传递environ,可以通过如下方式自己定义
char * const envp[] = {
"AA=aa",
"/home/linux",
NULL,
};
extern char **environ;
int main(int argc, char const *argv[])
{
pid_t pid;
pid = fork();
if(pid == -1){
perror("fork error");
exit(EXIT_FAILURE);
}else if(pid == 0){
int i;
//bash在执行a.out的时候,会将bash中的环境变量传递给a.out可执行程序
//environ[0] = "PATH=/bin:/sbin/"
//environ[1] = "HOME=/home/linux"
//NULL
for(i=0;environ[i]!=NULL;i++){
printf("%s\n",environ[i]);
}
//execle函数就是在当前子进程中不执行原来的代码了,取而代之的执行env这个可执行程序
//e:表示可以向env这个可执行程序中传递环境变量,将bash传递给a.out环境变量在传递给env
//可执行程序。
#if 0
if(execle("./env", "env",NULL,environ)==-1)
PRINT_ERR("execle error");
#else
char * const argvv[] = {
"env",NULL
};
if(execve("./env",argvv ,environ)==-1)
PRINT_ERR("execle error");
#endif
}else{
wait(NULL);
}
return 0;
}
//env.c =====>gcc env.c -o env
#include
#include
#include
extern char **environ;
int main(int argc, char const *argv[])
{
printf("-----------------------------------------\n");
int i;
//这里拿到的环境变量就是a.out传递过来的
for(i=0;environ[i]!=NULL;i++){
printf("%s\n",environ[i]);
}
printf("-----------------------------------------\n");
// AA=aaa
//AA就是键,aaa就是值
//这里的getenv就是在通过键或者值
printf("PATH = %s\n",getenv("PATH"));
//执行a.out的可执行程序,会在PATH寻找a.out可执行程序,如果PATH下有a.out就
//执行程序,否则就执行失败。
if(execlp("a.out","a.out","/home/linux/IO/day3/labixiaoxin.bmp",NULL)==-1){
perror("execlp error");
return -1;
}
return 0;
}
④system函数
int system(const char *command);
功能:fork+execl=system 在一个程序中执行程序
#include
参数:
@command:命令 "ls -l"
示例:
#include
#include
int main(int argc, char const *argv[])
{
system("bash shell.sh");
return 0;
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)