exec(鸠占鹊巢)
- 查找文件:fins /usr
execl
#include <unistd.h> //read() write()
#include <stdio.h>
#include <string.h>
#include <errno.h>//errno
/*open()*/
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
/* dir open */
#include <sys/types.h>
#include <dirent.h>
int main()
{
int ret=-1;
//使用新进程镜像替换当前进程镜像
//但是当前进程的ID被新进程使用
//-->鸠占鹊巢
//想看user/include下面的内容(路径+要执行的命令+命令的方式)
//第一个参数:可执行文件的路径,之后的参数,可执行文件执行的方式
//ret=execl("/bin/ls","ls","/usr/include","-l",NULL);
//ret=execl("./exec1","exec1",NULL);
//ret=execl("/usr/bin/gedit","gedit","dirCopy.c",NULL);
//ret=execlp("gedit","gedit","dirCopy.c",NULL);//可以打开dirCopy.c文件
/*ret=execlp("exec1","exec1",NULL);//运行不起来,不会循环打印hello world,因为不加路径,则会去系统找相应的命令,但是找不到,可以不指定需要执行文件的命令,启动该执行文件时,到系统默认路径下找该执行文件,若找到了则执行,否则出错返回
if(-1==ret)
{
perror("execlp");
return -1;
}*/
/*char *const argv[]={"ls","/usr/include","-l",NULL};
ret=execv("/bin/ls",argv);*///运用数组将后面的放进数组里。
char *const argv[]={"gedit","dirCopy.c",NULL};
ret=execvp("gedit",argv);
return 0;
}
- 模拟一个终端,可以同时打开两个文件
#include <unistd.h> //read() write()
#include <string.h>
#include <stdio.h>
int main(int argc,char *argv[])
{
pid_t pid=-1;
pid=fork();
if(pid>0)//父进程
{
while(1)
{
printf("this is parent\n");
sleep(1);
}
}
else if(0==pid)
{
//./a.out /bin/ls /usr/include -l
int ret=-1;
ret=execv(argv[1],argv+1);//argv+2:表示从第2个往后,直到碰到NULL
if(-1==ret)
{
perror("execv");
return -1;
}
}
/*else if(-1==pid)
{
perror()
}*/
return 0;
}
//以下为运行结果
模拟一个终端
#include <unistd.h> //read() write()
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
char *getInput(char *argv[])
{
int ret=-1;
int i=0;
char *pData=NULL;
pData=(char *)malloc(64);
while(EOF!=scanf("%s",pData))
{
printf("%s\n",pData);
argv[i]=pData;
i++;
pData=(char *)malloc(64);
}
free(pData);
argv[i]=NULL;
return NULL;
}
void showArgv(char *argv[])
{
int i=0;
while(argv[i]!=NULL)
{
printf("%s\n",argv[i]);
i++;
}
}
int main(int argc,char *argvm[])
{
char *argv[32]={NULL};
while(1)
{
printf("MyTermal@sq$:");
getInput(argv);
pid_t pid=-1;
pid=fork();
if(0==pid)
{
int ret=-1;
ret=execvp(argv[0],argv);
if(ret==-1)
{
perror("execvp");
return -1;
}
}
}
return 0;
}
- 上一个程序没有处理僵尸进程,所以不完善
atexit(在程序正常结束的时候,调用)
- 用户层,对相关的进行清理
- 到内核层,也对相关的进行清理
//错误号被设置:出错可以看到错误信息,否则看不到
#include<stdio.h>
#include<stdlib.h>
void fun1()
{
printf("fun1...\n");
}
void fun2()
{
printf("fun2...\n");
}
void fun3()
{
printf("fun3...\n");
}
//进程正常结束时首先在用户层做一些善后工作
//然后进入内核层做一些善后工作
int main(void)
{
//atexit注册的函数会在进程正常结束后被执行
//执行的顺序和注册的顺序相反
atexit(fun1);
atexit(fun2);
atexit(fun3);
printf("hello world\n");
return 0;
}
- 加了exit(-1),就自杀了
#include<stdio.h>
#include<stdlib.h>
void fun1()
{
printf("fun1...\n");
}
void fun2()
{
printf("fun2...\n");
}
void fun3()
{
printf("fun3...\n");
}
//进程正常结束时首先在用户层做一些善后工作
//然后进入内核层做一些善后工作
int main(void)
{
//atexit注册的函数会在进程正常结束后被执行
//执行的顺序和注册的顺序相反
atexit(fun1);
atexit(fun2);
atexit(fun3);
exit(-1);//自杀
printf("hello world\n");
return 0;
}
//不会执行hello world
abort(非正常结束)
#include<stdio.h>
#include<stdlib.h>
void fun1()
{
printf("fun1...\n");
}
void fun2()
{
printf("fun2...\n");
}
void fun3()
{
printf("fun3...\n");
}
//进程正常结束时首先在用户层做一些善后工作
//然后进入内核层做一些善后工作
int main(void)
{
//atexit注册的函数会在进程正常结束后被执行
//执行的顺序和注册的顺序相反
atexit(fun1);
atexit(fun2);
atexit(fun3);
abort();//已放弃(核心已转储)
// exit(-1);//自杀
printf("hello world\n");
return 0;
}
//用了abort,就段错误
- 可以退出整个程序的
- return 0;exit():会首先在用户层做一些善后工作,然后进入内核层组一些善后工作
- _exit():直接进入内核层作一些善后工作
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
void fun1()
{
printf("fun1...\n");
}
void fun2()
{
printf("fun2...\n");
}
void fun3()
{
printf("fun3...\n");
}
//进程正常结束时首先在用户层做一些善后工作
//然后进入内核层做一些善后工作
int main(void)
{
//atexit注册的函数会在进程正常结束后被执行
//执行的顺序和注册的顺序相反
atexit(fun1);
atexit(fun2);
atexit(fun3);
_exit(-1);//也能退出程序(进程结束)
// abort();//已放弃(核心已转储)
// exit(-1);//自杀
printf("hello world\n");
return 0;
}
//用了_exit(-1):不会进行运行直接退出
作业
- 完善文件拷贝(假如将一个目录拷贝到了文件里,进行分析)
- 进程的状态以及状态间的转化
- 独立完成模仿终端代码
进程与通信
- IPC:代表进程间
进程间的通信方式
- 管道通信:命名管道和无名管道
- env | grep unix :grep是匹配规则,只要数据符合规则,则全部都提取出来
pipe(管道通信)
- 父进程发送消息,子进程接收消息
- 如果要换成子进程发消息,父进程接收
#include<unistd.h> //pipe() fork()
#include<stdio.h>
#include<string.h>
int main()
{
int pipefd[2]={-1};//管道两端,一端读,一端写
int ret=-1;
//创建一个管道(不属于父进程)
ret=pipe(pipefd);
if(-1==ret)
{
perror("pipe");//创建失败
return -1;
}
//创建一个子进程
pid_t pid=-1;
pid=fork();
if(pid>0)//父进程
{
close(pipefd[0]);//一个说
while(1)
{
write(pipefd[1],"Hello child",11);
sleep(1);
}
}
else if(pid==0)//子进程
{
close(pipefd[1]);//一个听,不能同时说,同时听
char caBuf[32]={'\0'};
while(1)
{
memset(caBuf,'\0',sizeof(caBuf));//清空读掉的空间
read(pipefd[0],caBuf,11);
printf("%s\n",caBuf);
sleep(1);
}
}
else if(pid==-1)//创建进程失败
{
perror("fork");
return -1;
}
return 0;
}
- 父进程与子进程之间进行相互通信
#include<unistd.h> //pipe() fork()
#include<stdio.h>
#include<string.h>
//通过pipe()函数创建的管道属于无名管道,只能在父子进程间使用,或者子进程间使用,创建该管道的进程一旦结束,则该无名管道将会被销毁
int main()
{
int pipefd[2]={-1};//管道两端,一端读,一端写
int ret=-1;
//创建一个管道(不属于父进程),管道两边的描述符存储到pipefd数组中
//pipefd[0]表示数据流出端,可以从此端读取数据
//pipefd[1]表示数据进入端,可以往此端写入数据
ret=pipe(pipefd);
if(-1==ret)
{
perror("pipe");//创建失败
return -1;
}
//创建一个子进程
pid_t pid=-1;
//管道的创建的是在内核中,不独立属于进程
//所以,fork产生子进程是并不会再次创建一个管道
//只是对管道文件描述符进行了一次拷贝
pid=fork();
if(pid>0)//父进程
{
int iSign=0;
char caBuf[64]={'\0'};
while(1)
{
memset(caBuf,'\0',sizeof(caBuf));
if(0==iSign)
{
printf("parent-input data:");
scanf("%s",caBuf);
write(pipefd[1],caBuf,strlen(caBuf));
iSign=1;
}
else if(1==iSign)
{
read(pipefd[0],caBuf,sizeof(caBuf));
printf("child says:%s\n",caBuf);
iSign=0;
}
sleep(1);
}
}
else if(pid==0)//子进程
{
int iSign=1;
char caBuf[64]={'\0'};
while(1)
{
memset(caBuf,'\0',sizeof(caBuf));
if(0==iSign)
{
read(pipefd[0],caBuf,sizeof(caBuf));
printf("parent says:%s\n", caBuf);
iSign=1;
}
else if(1==iSign)
{
printf("child-input data:");
scanf("%s",caBuf);
write(pipefd[1],caBuf,strlen(caBuf));
iSign=0;
}
sleep(1);
}
}
else if(pid==-1)//创建进程失败
{
perror("fork");
return -1;
} return 0;
}
进程在运行中不断改变其运行状态,而运行的进程有三个基本状态。
- 就绪状态:当进程已分配到除CPU以外所有必要的资源,只要获得处理器便可立即执行。
- 执行状态:当进程已获得处理器,其程序正在处理器上执行。
- 阻塞状态:正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态,而引起进程阻塞的事件可能有很多种,如等待I/O完成,申请缓冲区不能满足、等待信号等。
而一个进程在运行期间,会不断地从一个状态转换到另一个状态。于是便有了进程的三种基本状态间的相互转换,如下示意图:
- 就绪->执行:处于就绪状态的进程,当进程调度程序为之分配了处理器后,该进程便由就绪状态转变为执行状态。
- 执行->就绪:处于执行状态的进程在其执行进程中,因分配给它的一个时间片已用完而不得不让处理器,于是进程从执行状态转换成就绪状态。
- 执行->阻塞:正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态。
- 阻塞->就绪:处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。
顶