回忆昨天内容
一、进程的基础
进程树 树根 init进程 1号进程
父子关系 兄弟关系
pid PCB
进程是资源分配的基本单位
ps -aux|grep 进程数
top
pstree
二、创建子进程
fork(2)
父进程和子进程是异步的
wait(2)让父进程和子进程同步
复制父进程的PCB
PCB中有文件描述符 进程的映像
父进程调用fork(2) 但是返回时 已经是两个进程了
调用fork的进程是父进程
新的进程是子进程
三、进程的终止
1、return和exit(3)
status&0377 传递给父进程
2、遗言函数的注册 atexit(3)on_exit(3)
四、回收子进程的资源
wait(2)家族
wait
僵尸进程zombie
孤儿进程orphan
今天的内容:
一、waitpid(2)
#include <sys/types.h>
#include <sys/wait.h>
wait(&status)=waitpid(-1,&status,0)
pid_t waitpid(pid_t pid, int *status, int options);
功能:等待回收子进程的资源
参数: pid指定一个特定的孩子
<-1 pid的绝对值是一个进程组的pid,等待
-1 等待任意子进程
0 等待跟父进程同组的子进程的终止
>0 等待的子进程的pid
status将子进程的退出状态保存在这个变量指向的空间里
options
WNOHANG 如果没有子进程的终止,立即返回
0 阻塞等待子进程的终止
返回值:成功 返回子进程的pid(收到了) 如果设置了非阻塞,未收到的情况下 返回0
错误 -1 errno被设置
process group
进程组 进程组有多个进程,但是有一个组长 进程组的pid就是组长这个进程的pid
fork产生的子进程默认是同一组
代码验证 waitpid.c
二、进程映像的替换
fork(2)创建子进程后,子进程继承父进程的映像
PCB fd image
exec(3)家族
execve(2)
#include <unistd.h>
int execve(const char *filename, char *const argv[],
char *const envp[]);
功能:进程映像的替换
参数:filename 可执行程序,使用这个可执行程序替换掉从父进程继承的image
argv作为新程序的参数传递
envp作为新映像的环境变量传递 都必须以NULL结尾
返回值:成功 不返回
失败 -1 errno被设置
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...
/* (char *) NULL */);
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
int execle(const char *path, const char *arg, ...
/*, (char *) NULL, 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[]);
l list argv[] argv[]
v vector
p PATH 带这个字符的,指示找可执行程序时,到PATH指定的路径下找
e 不带这个字符的,默认从父进程继承环境变量
带这个字符的,用户指定环境变量,传递给新的映像
举例说明 创建一个子进程 将子进程的映像替换 代码参见 execl.c
bash如何执行a.out
bash fork execl("./a.out","a.out",NULL);
wait
bash ls
ls 和bash是两个独立的可执行程序。执行时和bash 一个父进程,一个子进程(是两个进程)
这种命令称为bash的外部命令,不是bash本身携带的
本身为bash的一部分的命令,执行时,不需要fork创建子进程,这些命令称为bash的内部命令
如何判断一个命令是内部命令还是外部命令?
type command
三、环境变量
环境变量是进程的环境变量
externa char** environ; 指向环境变量列表的首地址
char * envp[] ; envp是常量
编写程序遍历进程的环境变量列表
代码参见 t_env.c
默认的情况是将父进程的环境变量传递给新的程序
代码参见 t_env1.c
使用程序操作环境变量
获取环境变量的值
getenv(3)
#include <stdlib.h>
char *getenv(const char *name);
功能:找到环境变量的值
参数:
name 指定要找的环境变量的名字
返回值: 找不到 NULL
找到 返回value string的地址
设置环境变量的值
setenv(3)
#include <stdlib.h>
int setenv(const char *name, const char *value, int overwrite);
功能:改变或者增加一个环境变量 如果环境变量名字不存在,增加一个环境变量
参数 name指定了环境变量的名字
value 环境变量的值
overwrite 非0 如果环境变量存在,将环境变量的值改为value
0 如果环境变量存在,环境变量不改变
返回值:
成功 0
失败 -1 errno被设置
删除环境变量
unsetenv(3)
#include <stdlib.h>
int unsetenv(const char *name);
功能:删除环境变量
参数: name指定环境变量的名字
返回值:成功返回0
失败返回-1 errno被设置
清除所有环境变量
clearenv(3)
#include <stdlib.h>
int clearenv(void);
功能:清除所有的环境变量 设置全局变量environ为NULL
参数:无
返回值:成功 0
失败 非0
putenv(3)
代码参见 env_os.c
传递带环境变量的参数exece.c
四、管道 进程间的通讯
无名管道
ps -aux|grep a.out
应用与具有亲缘关系的进程间通讯 父子关系 兄弟关系
#include <unistd.h>
int pipe(int pipefd[2]);
功能:创建一个管道 一个用于进程间通信的单向数据管道 单工
参数:
pipefd[2] 返回管道两端的文件描述符
pipefd[0]指向管道读端
pipefd[1]指向管道写端
返回值: 成功返回0 错误返回-1 errno被设置 //09pm178分
1、创建管道,返回两个文件描述符
2、创建子进程,子进程继承父进程的文件描述符
3、子进程负责的任务(读
关闭写端
从管道读取数据(阻塞,无数据时等待)
读取到数据以后,将数据输出到显示器
close(读端)
子进程终止
4、父进程负责的任务(写
关闭读端
将数据写入到管道(阻塞,满则等待)
close(写端)
回收子进程的资源
代码参见pipe.c
有名管道
env_os.c
#include<stdio.h>
#include<stdlib.h>
int main(void){
#if 0
//获取环境变量的值
char *str=getenv("caption");
if(!str)
printf("not found...\n");
else
printf("%s\n",str);
#endif
//增加一个环境变量
setenv("caption","beijing",0);
char *str=getenv("caption");
if(!str)
printf("not found...\n");
else
printf("%s\n",str);
return 0;
}
exece.c
#include<t_stdio.h>
#include<stdlib.h>
#include<t_file.h>
#include<sys/wait.h>
int main(){
//创建子进程
char *ps_envp[]={"caption=beijing","user=nan","name=zhangsan",NULL};
pid_t pid=fork();
if(pid==-1) E_MSG("fork",-1);
if(pid==0){
// execlp("ps","ps","-o","pid,ppid,pgrp,comm",NULL);
//以下两句只有失败时才能够执行
// execl("./t_env","t_env",NULL);
execle("./t_env","t_env",NULL,ps_envp);
// execlp("/bin/ps","ps","-o","pid,ppid,pgrp,comm",NULL);
perror("execl");
exit(-1);
}else{
wait(NULL);//等待子进程结束
}
return 0;
}
execl.c
#include<t_stdio.h>
#include<stdlib.h>
#include<t_file.h>
#include<sys/wait.h>
int main(){
//创建子进程
char *ps_argv[]={"ps","-o","pid,ppid,pgrp,comm",NULL};
pid_t pid=fork();
if(pid==-1) E_MSG("fork",-1);
if(pid==0){
// execlp("ps","ps","-o","pid,ppid,pgrp,comm",NULL);
//以下两句只有失败时才能够执行
execvp("ps",ps_argv);
// execlp("/bin/ps","ps","-o","pid,ppid,pgrp,comm",NULL);
perror("execl");
exit(-1);
}else{
wait(NULL);//等待子进程结束
}
return 0;
}
pipe.c
#include<t_stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<stdlib.h>
#include<string.h>
int main(){
int fd[2];
char buf[128];
char* msg="this is a test..\n";
//在父进程创建管道,fd[0]指向管道的读端,fd[1]指向管道的写端
int pp=pipe(fd);
if(pp==-1) E_MSG("pipe",-1);
//创建子进程,子进程继承父进程的文件描述符。
pid_t pid=fork();
if(pid==-1) E_MSG("fork",-1);
if(pid==0){
//这是子进程负责的任务
close(fd[1]);
int r=read(fd[0],buf,128);
write(1,buf,r);
close(fd[0]);
exit(0);
}else{//这是父进程负责的任务
close(fd[0]);
sleep(5);
write(fd[1],msg,strlen(msg));
close(fd[1]);
wait(NULL);
}
return 0;
}
t_env.c
#include<stdio.h>
#include<unistd.h>
extern char** environ;
int main(void){
int i;
for(i=0;environ[i]!=NULL;i++)
printf("%s\n",environ[i]);
return 0;
}
waitpid.c
#include<t_stdio.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<t_file.h>
int main(){
//创建 子进程
int s;
pid_t pid=fork();
if(pid==-1)E_MSG("fork",-1);
if(pid==0){//子进程执行的
printf("child process...%d\n",getpid());
sleep(10);
exit(-1);
}
else {
//父进程执行的
int w=waitpid(0,&s,0);//挂起执行,等待子进程终止
if(w>0){
printf("parent process...\n");
if(WIFEXITED(s))//子进程正常终止
//输出子进程终止状态码
printf("exit code:%d\n",WEXITSTATUS(s));
if(WIFSIGNALED(s))//子进程被信号打断
//输出打断子进程的信号编号
printf("signum...%d\n",WTERMSIG(s));
}
}
return 0;
}