目录
一.创建进程函数fork
二.vfork创建进程
三.进程退出
四.exec族函数
五.孤儿进程与僵尸进程
六.system函数
七.popen函数
什么是进程?
进程是程序的一次运行活动,通俗点意思是程序跑起来了,系统中就多了一个进程
如何查看系统中有哪些进程?
a.使用ps指令查看实际工作中,配合grep来查找程序中是否存在某一个进程
b.使用top指令查看,类似windows任务管理器
什么是进程标识符?
每个进程都有一个非负整数表示的唯一ID,叫做pid,类似身份证·
Pid=0: 称为交换进程(swapper)-------作用:进程调度
Pid=1:init进程 -------作用:系统初始化
编程调用getpid函数获取自身的进程标识符
getppid获取父进程的进程标识符
什么叫父进程,什么叫子进程
进程A创建了进程B,那么A叫做父进程,B叫做子进程,父子进程是相对的概念,理解为人类中的父子关系。
C程序的存储空间是如何分配?
正文段:存放的代码如switch,if,else,for等
数据段:初始化过的变量
bss段:在函数外未被初始化的变量
堆:如malloc申请空间就在堆中申请
栈:函数调用,函数调用产生的局部变量
一.创建进程函数fork
SYNOPSIS
#include
pid_t fork(void);
fork函数调用成功,返回两次
返回值为0, 代表当前进程是子进程
返回值非负数,代表当前进程为父进程(且返回值为新进程的pid)
调用失败,返回-1
demo
#include#include #include int main() { pid_t pid; pid_t pid2; pid_t retpid; pid = getpid(); printf("this is before pid:%dn",pid); retpid = fork(); pid2 = getpid(); printf("this is after pid:%dn",pid2); if(pid > 0){ printf("this is father pid,return is:%dn",retpid); } else { printf("this child pid,return is%dn",retpid); } return 0; }
运行结果:
二.vfork创建进程
关键区别一:vfork 直接使用父进程存储空间,不拷贝。
关键区别二:vfork保证子进程先运行,当子进程调用exit退出后,父进程才执行。
demo
#include#include #include #include int main() { pid_t pid; int cnt; pid = vfork(); //用vfork创建进程 if(pid > 0){ while(1){ printf("this is father print,pid is %dn",getpid()); sleep(1); } } if(pid == 0){ while(1){ printf("this is child print,pid is %dn",getpid()); sleep(1); cnt++; if(cnt == 3){ exit(0); //当cnt=3,退出进程 } } } return 0; }
三.进程退出
函数原型
SYNOPSIS
#include
void exit(int status);
SYNOPSIS
#include
void _exit(int status);
#include
void _Exit(int status);
正常退出
1.Main函数调用return
2.进程调用exit(),标准c库
3.进程调用_exit()或者_Exit(),属于系统调用
异常退出
1.调用abort
2.当进程收到某些信号时,如ctrl+C
3.最后一个线程对取消(cancellation)请求做出响应
补充:进程最后一个线程返回;最后一个线程调用pthread_exit
父进程等待子进程退出
创建子进程之后,我们需要判断子进程是否“干完活”可以通过回收子进程的退出状来判断。子进程退出状态不被收集,变成僵死进程(僵尸进程)
父进程通过下面的api来收集子进程的退出状态。
wait
SYNOPSIS
#include
#include
pid_t wait(int *status);
| status参数 | 是一个整型数指针 非空:子进程退出状态放在它所指向的地址中。 空: 不关心退出状态 |
status判断子进程如何退出状态
| WIFEXITED(status) | 为非0 ,进程正常结束 |
| WEXITSTATUS(status) | 如上宏为真,使用此宏,获取进程退出状态的参数 |
| WIFSIGNALED(status) | 为非0,进程异常退出 |
| WTERMSIG(status) | 如上宏为真,使用此宏,取得使进程种植的那个信号的编号 |
waitpid
pid_t waitpid(pid_t pid, int *status, int options);
函数作用:同wait函数,区别在于可以调用者不阻塞。
| pid | pid == ‐1 等待任意子进程 pid > 0 等待其进程ID与pid相等的子进程 pid == 0 等待其组ID调用调用进程组ID的任一子进程 pid < -1 等待其组ID等于pid绝对值的任一子进程 |
| status | 同上 |
| options | 设置为WNOHANG,函数非阻塞,设置为0,函数阻塞 |
返回值: >0 :返回清理掉的子进程ID ‐1 :回收失败,无子进程 如果为非阻塞 =0 :参数3为WNOHANG,且子进程正在运行
孤儿进程
父进程如果不等待子进程退出,在子进程之前就结束了自己的“生命”,此时子进程叫做孤儿进程
Linux避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程
demo
#include#include #include #include #include int main() { pid_t pid; int cnt = 0; int status = 0; pid = fork(); if(pid>0) { wait(&status); printf("child quit,child status is %dn",WEXITSTATUS(status)); while(1) { printf("cnt=%dn",cnt); printf("this father pid print,pid is=%dn",getpid()); sleep(1); } } else if(pid==0) { while(1) { printf("this children pid print,pid is=%dn",getpid()); sleep(1); cnt++; if(cnt==5){ exit(3); } } } return 0; }
四.exec族函数
SYNOPSIS
#include
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
作用
我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。
返回值
exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。
参数说明
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束
file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。
它们的一般规律如下
l (list) 命令行参数列表
p (path) 搜素file时使用path变量
v (vector) 使用命令行参数数组
e (environment) 使用环境变量数组,不使用进程原有的环境变量,设置新加载程序运行的环境变量
demo
//文件execl.c #include#include #include //函数原型:int execl(const char *path, const char *arg, ...); int main(void) { printf("before execln"); if(execl("./bin/echoarg","echoarg","abc",NULL) == -1) { printf("execl failed!n"); } printf("after execln"); return 0; }
//文件echoarg.c #includeint main(int argc,char *argv[]) { int i = 0; for(i = 0; i < argc; i++) { printf("argv[%d]: %sn",i,argv[i]); } return 0; }
五.孤儿进程与僵尸进程
孤儿进程: 一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进 程(进程号为1)所收养,并由init进程对它们完成状态收集工作
僵尸进程: 一个比较特殊的状态,当进程退出父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵尸进程。僵尸进程会在以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码。
六.system函数
函数原型
SYNOPSIS
#include
int system(const char *command);
system()函数的返回值如下:
成功,则返回进程的状态值;
当sh不能执行时,返回127;
失败返回-1;
demo
#include#include #include #include #include int main() { pid_t pid; int data = 0; while(1){ printf("please input datan"); scanf("%d",&data); if(data == 1) { pid = fork(); if(pid == 0) { system("./changedata text.config"); } if(pid>0) { wait(NULL); } } else{ printf("wait do nothingn"); } } return 0; }
七.popen函数
函数原型
SYNOPSIS
#include
FILE *popen(const char *command, const char *type);
比system在应用中的好处:可以获取运行的输出结果
popen的好处:可以将指令的执行结果存入字符串中或存入文件中
demo
#include#include #include int main() { char ret[1024] = {0} ; FILE *fp; fp = popen("ps","r"); int n_read = fread(ret,1,1024,fp); printf("read %d byte,ret=%sn",n_read,ret); return 0; }



