目录
一、进程
1.程序和进程
2.进程的八种状态
3. 几个状态
4.关于进程常用命令
二、关于进程的函数
1.fork
2.面问
3.孤儿进程 后台进程
2. exec函数族 (只保留父子关系,做新的事情)
strtok函数
三、进程的结束
1.分类
exit和_exit的区别
wait函数
waitpid函数
四、总结
一、进程
1.程序和进程
内存中正在进行(运行)中的程序。
./a.out跑起来就是a.out对应的进程。
程序 静态 硬盘
进程 动态 内存
程序= 代码 + 数据= 代码区(text段)+栈区+堆区+BSS+Data
进程 = 代码区(text段)+栈区+堆区+BSS+Data + PCB(Process Control Block)
BSS:存放程序中未初始化的全局变量的一块内存区域。
DATA:存放程序中已初始化的全局变量的一块内存区域。
2.进程的八种状态
*D 不可中断态的睡眠态
*R 正在运行态 或者 就绪态
*S 可终端的睡眠态
*T stopped by job control signal
t stopped by debugger during the tracing
X 死亡态
*Z 僵尸态
3. 几个状态
通用三态图
linux系统的状态
4.关于进程常用命令
top 动态查看系统中进程
pstree -sp [pid] 查看进程树(可指定pid号)
kill 发出信号
kill -l //查看可以发送的信号
18) SIGCONT //继续信号
19) SIGSTOP //暂停
9) SIGKILL //死亡信号,杀死进程
kill -19 [pid] //给指定pid号的进程发送 信号
ps aux | grep a.out 查看pid号进程的状态信息
ps -eLf | grep a.out 查看pid和ppid号
二、关于进程的函数
1.fork
pid_t fork(void); 通过复制主调进程创建子进程。
一次fork返回了两次 typedef int pid_t
成功 在父进程空间返回子进程pid,在子进程空间返回 0
失败 父进程返回值-1 errno会被设置
2.面问
eg1如果两次fork同时前后执行,会生成几个进程?
4个进程
eg2 fork()&&fork()||fork();
5个进程
fork()
/ \
fork() fork1()
/ \ / \
fork fork2 fork1 fork3
/ \
fork2 fork4
3.孤儿进程 后台进程
子进程没有父进程就是孤儿进程,将会有 init(1)-进程 收养子进程。变成 后台进程 ( ctrl + c) 这个信号只能发给前台进程。 结束后台进程要用kill
子进程结束,但是父进程,没有对子进程收尸。那就是僵尸进程。僵尸态是有危害的,消耗内存。
eg1 fork创建子进程,分别打印pid
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>int main(void)
{pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}if (pid > 0){printf("father pid = %d ---\n",getpid());printf("pid = %d",getpid());}else if (pid == 0){printf("child pid = %d ---\n",getpid());}printf("--pid = %d --end---\n",getpid());return 0;
}
2. exec函数族 (只保留父子关系,做新的事情)
通过将新进程的各个段替换当前进程的各个段来实现,用来执行一个新的功能
int execl(const char *path, const char *arg, .../* (char *) NULL */);
int execv(const char *path, char *const argv[]);@path 代表要运行的新程序的名字 要包含路径
eg:
"/home/linux/fileio/mycp"
@arg 表示要运行的程序的名字
eg:
"mycp"
@... 可变参数(要执行程序用到的参数)
eg:
"src.txt","dest.txt",NULL结尾
l(list)和v(vector)的区别 传参的方式不同
execl( "/home/linux/fileio/mycp","mycp", "src.txt","dest.txt",NULL);
char * const args[] = {"mycp", "src.txt","dest.txt",NULL};
int execv( "/home/linux/fileio/mycp", args);
printf("---exec---code---");后续代码不执行,因为段被替换
int execlp(const char *file, char *const argv[]);
int execvp(const char *file, char *const argv[],char *const envp[]);p 表示 PATH系统环境变量(系统运行时侯需要的一些变量)
表示要执行的可执行文件到PATH环境变量中去寻找
execlp("ls","ls","-l","/",NULL);
char *const args[] = {"ls","-l","/",NULL};
execlp("ls","ls","-l","/",NULL);
int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */); 看要运行的程序需不需要环境变量,需要就可以传。不需要的话用之前的方式运行起来就行。
int execvpe(const char *file, char *const argv[],char *const envp[]);
e 表示用户环境变量 ---提供了一种方式,可以给要运行的程序传递环境变量
extern char **environ;
int main{
execle("/usr/bin/env","env",NULL,environ);
char *const my_env[] = {"USERNAME=linux","PSWD=123456 ",NULL};
execle("/usr/bin/env","env",NULL,my_env);
execvpe("env",my_env,NULL);
return 0;
}
strtok函数
作用,提取字符串
char *strtok(char *str, const char *delim);
@str --- 要提取完整字符串 -- buf
如果连续的分割 填NULL
@delim --- 分隔标志 // " ;,"可以有多个分割标志
返回值:
成功 返回提取到的字符串的地址
失败 NULL
eg strtok的使用
#include<stdio.h>
#include<string.h>
int main(int argc, char const *argv[])
{char buf[1024]={"ls -l ;/"};
#if 0 char *s1 = strtok(buf," ;");printf("s1 = %s\n",s1);char *s2 = strtok(NULL," ;");printf("s2 = %s\n",s2);char *s3 = strtok(NULL," ;");printf("s3 = %s\n",s3);
#endifchar *s[5] = {NULL};int i= 0;s[i] = strtok(buf," ;");while (s[++i] = strtok(NULL," ;"));for ( i = 0; i < 5; ++i){printf("s[%d]=%s\n",i,s[i]);}return 0;
}
eg1 实现一个shell程序
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>int main(int argc,const char *argv[])
{char buf[1024] = {0};while (1){printf("myshell$ ");fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = '\0';if (strncmp(buf,"exit",4) == 0 || strncmp(buf,"quit",4) == 0){printf("exit---myshll---\n");return 0;}int i = 0;char *arg[10] = {NULL};arg[i] = strtok(buf," ");while (arg[++i] = strtok(NULL," "));
#if 0for (i = 0; i < 10; ++i){printf("%d:%s\n",i,arg[i]);}
#endif pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}if (pid > 0){wait(NULL);continue;}else if (pid == 0){if (execvp(arg[0],arg) < 0){perror("execvp fail");return -1;}}}return 0;
}
三、进程的结束
1.分类
正常结束:
1)main 中 return
2)exit() //库函数
c库函数,会执行io库的清理工作,关闭所有 的流,以及所有打开的文件。
注册清理函数(atexit)。
3)_exit,_Exit 会关闭所有的已经打开的文件,不执行清理函数。 //系统调用
//4) 主线程退出
//5)主线程调用pthread_exit
异常终止:
6)abort() //发送一个SIGABRT
7)signal //发信号 结束了进程 kill pid -9
//8) 最后一个线程被pthread_cancle
exit函数
#include <stdlib.h>
void exit(int status);
功能:
造成进程正常结束
参数:
@status 带出一个状态值给到父进程 结合wait和宏才能查看状态值
return 0 自动调取一个exit函数,从而调用atexit函数,_exit不调用atexit函数
_exit
#include <unistd.h>
void _exit(int status);
功能:
造成进程正常结束 ,立刻结束
参数:
@status 带出一个状态值给到父进程 结合wait和宏才能查看状态值
exit和_exit的区别
区别:
exit 是库函数
退出前,
1.先清理IO缓存
2.调用清理函数
_exit 是系统调用
立即结束进程
atexit
#include <stdlib.h>
int atexit(void (*function)(void));
功能:
注册一个退出清理函数
参数:
@function ---- 函数指针
函数类型 void func1(void)
返回值:
成功 返回0
失败 非0
ps注册顺序和调用顺序相反(有栈的结构)
状态值:
status & 0377 => 一个字节的数据
1 1 1 1 1 1 1 1 //0377 二进制
0 0 0 0 0 0 0 0
//数值 个数 256 个值
wait函数
pid_t wait(int *wstatus);
用于等待子进程的状态的变化,并获取一些该子进程的信息。
三种变化
1.子进程结束
2.子进程被信号暂停(kill SIGSTOP)
3.子进程因信号被回恢复(SIGCONT)
参数:
@status 获取子进程退出时的状态信息(要用宏才能提取出来) //被调修改主调的方式
返回值:
成功 返回状态改变了的子进程的pid
失败 -1
如果不关心其退出状态一般用NULL表示
注意:
wait 本身是个阻塞操作(只有有子进程时,才阻塞,没有子进程时,立即返回wait调用失败) //子进程结束
父进程在子进程结束后用wait进行资源回收和状态的获取称为"关心"。
不进行资源回收的结束的子进程是僵尸态。
宏:
//正常结束
WIFEXITED(wstatus) //判断子进程是否是正常结束
//正常结束 则为真
WEXITSTATUS(wstatus) //使用这个宏去那返回值
//异常结束
WIFSIGNALED(wstatus) //判断子进程是否是被信号结束
WTERMSIG(wstatus) //获得 结束子进程的那个信号编号
eg 1 WIFEXITED(wstatus)和 WEXITSTATUS(wstatus)
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>int main(void)
{pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}if (pid > 0){sleep(3);printf("---wait----child---\n");int status;wait(&status);printf("status = %d\n",status);if (WIFEXITED(status)){printf("child status = %d\n",WEXITSTATUS(status));}}else if (pid == 0){printf("---child----exit---\n");exit(99);}return 0;
}
eg 1 WIFSIGNALED(wstatus) 和 WTERMSIG(wstatus)
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>int main(void)
{pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}if (pid > 0){sleep(3);printf("---wait----child---\n");int status;//wait(&status);//waitpid(-1,&status,WNOHANG);waitpid(-1,&status,0);printf("status = %d\n",status);if (WIFEXITED(status)){printf("child status = %d\n",WEXITSTATUS(status));}}else if (pid == 0){printf("---child----exit---\n");exit(99);}return 0;
}
eg3 wait的使用
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>int main(void)
{pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}if (pid > 0){ sleep(3);printf("---wait----child---\n");int status;wait(&status);printf("status=%d\n",status);if(WIFEXITED(status)){printf("child status = %d\n",WEXITSTATUS(status));}}else if (pid == 0){printf("---child----exit---\n");exit(99);}return 0;
}
eg4 无人机状态模拟
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>void do_fly(void)
{int i = 0;while (i < 5){printf("---%s---\n",__FUNCTION__);++i;sleep(1);}exit(88);
}void do_video(void)
{int i = 0;while (i < 3){printf("---%s---\n",__FUNCTION__);++i;sleep(1);}exit(77);
}
void do_transmit(void)
{int i = 0;while (i < 4){printf("---%s---\n",__FUNCTION__);++i;sleep(1);}exit(66);
}
void do_store(void)
{int i = 0;while (i < 6){printf("---%s---\n",__FUNCTION__);++i;sleep(1);}exit(55);
}int main(void)
{int i = 0;pid_t pid = 0;for (i = 0; i < 4; ++i){pid = fork();if (pid < 0){perror("fork fail");return -1;}if (pid == 0)break;}if (pid > 0){int status;for (i = 0; i < 4; ++i){wait(&status);if (WIFEXITED(status)){switch(WEXITSTATUS(status)){case 55:printf("do_store exit---\n");break;case 66:printf("do_transmit exit---\n");break;case 77:printf("do_video exit---\n");break;case 88:printf("do_fly exit---\n");break;}}}}else if (pid == 0){switch(i){case 0:do_fly();break;case 1:do_video();break;case 2:do_transmit();break;case 3:do_store();break;}}return 0;
}
waitpid函数
pid_t waitpid(pid_t pid, int *wstatus, int options);
功能:
等待子进程状态改变
参数:
@pid
< -1 meaning wait for any child process whose process group ID is equal to the absolute value of pid.
eg: -100
表示等待 进程组ID 为 |-100| 这个进程组中的任意子进程
-1 meaning wait for any child process.
表示等待 当前父进程的任意子进程
0 meaning wait for any child process whose process group ID is equal to that of the calling process.
等待 进程组ID 等于 父进程pid的那个进程组中的任意子进程
> 0 meaning wait for the child whose process ID is equal to the value of pid.
eg: 100
等待 进程pid 为 100的这个子进程状态改变
@wstatus //与wait的参数类似 如果不关心其退出状态一般用NULL表示
获取准确状态值也是用
@options //
0 //阻塞调用
WNOHANG //非阻塞 不断的看子进程状态有没有改变,没有改变就返回了。有改变了,将资源回收,并进行if判断下面的操作。
waitpid(-1, &wstatus, 0); //等价于 wait(&wstatus)
四、总结
//进程
创建 --- fork
运行
//1.跟父进程类似 事情
//2.独立运行一个新程序 ---exec函数
//3.运行多个不同任务
结束
正常结束
//1.return //main
//2.exit
//3._exit
异常结束
//4.abort //信号
//5.signal //发其它信号
进程结束是两种特殊状态
孤儿进程
僵尸进程 --- wait/waitpid