2017-2018-1 20155334 《信息安全系统设计基础》第六周学习总结
1. fork()
函数:
1. 一个进程,包括代码、数据和分配给进程的资源。
2. fork() 函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
3. 一个进程调用fork() 函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。
在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。
fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
1)在父进程中,fork返回新创建子进程的进程ID;
2)在子进程中,fork返回0;
3)如果出现错误,fork返回一个负值;
我们可以通过fork返回的值来判断当前进程是子进程还是父进程
有如下代码:
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int i=0;
printf("i son/pa ppid pid fpid/n");
//ppid指当前进程的父进程pid
//pid指当前进程的pid,
//fpid指fork返回给当前进程的值
for(i=0;i<2;i++){
pid_t fpid=fork();
if(fpid==0)
printf("%d child %4d %4d %4d/n",i,getppid(),getpid(),fpid);
else
printf("%d parent %4d %4d %4d/n",i,getppid(),getpid(),fpid);
}
return 0;
}
运行结果如下:
分析:
第一次fork后,p3224(父进程)的变量为i=0,fpid=3225(fork函数在父进程中返向子进程id)p3225(子进程)的变量为i=0,fpid=0(fork函数在子进程中返回0)
第二步创建了两个进程p3226,p3227,这两个进程执行完printf函数后就结束了,因为这两个进程无法进入第三次循环,无法fork,该执行return 0;了,其他进程也是如此。
2. exec
函数:
1. fork函数是用于创建一个子进程,该子进程几乎是父进程的副本,而有时我们希望子进程去执行另外的程序,exec函数族就提供了一个在进程中启动另一个程序执行的方法。
2. 它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。
3. 这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行脚本文件。
exec函数族使用注意点:
在使用exec函数族时,一定要加上错误判断语句。因为exec很容易执行失败,其中最常见的原因有:
① 找不到文件或路径,此时errno被设置为ENOENT。
② 数组argv和envp忘记用NULL结束,此时errno被设置为EFAULT。
③ 没有对应可执行文件的运行权限,此时errno被设置为EACCES。
在Linux中使用exec函数族主要有以下两种情况 :
如果一个进程想执行另一个程序,那么它就可以调用fork函数新建一个进程,然后调用任何一个exec函数使子进程重生。
当进程认为自己不能再为系统和用户做出任何贡献时,就可以调用任何exec 函数族让自己重生。
execlp.c文件如下:
#include <stdio.h>
#include <unistd.h>
int main()
{
if(fork()==0){
if(execlp("/usr/bin/env","env",NULL)<0)
{
perror("execlp error!");
return -1 ;
}
}
return 0 ;
}
执行结果如图:
由执行结果看出,execlp函数使执行码重生时继承了Shell进程的所有环境变量,其他三个不以e结尾的函数同理。
3. wait()
函数:
1. wait()会暂时停止目前进程的执行, 直到有信号来到或子进程结束.
2. 如果在调用wait()时子进程已经结束, 则wait()会立即返回子进程结束状态值.
3. 子进程的结束状态值会由参数status 返回, 而子进程的进程识别码也会一快返回
4使用fork,exec,wait实现mybash:
部分代码展示,全部代码在码云里
int main()
{
char cmdline[MAX];
while(1){
printf("bsetixx@besrixx-VirtualBox:~/XINAN/mybash/$ ");
fgets(cmdline,MAX,stdin);
if(feof(stdin))
{
printf("error");
exit(0);
}
eval(cmdline);
}
}
void eval(char *cmdline)
{
char *argv[MAX];
char buf[MAX];
int bg;
pid_t pid;
strcpy(buf,cmdline);
bg = parseline(buf,argv);
if(argv[0]==NULL)
return;
if(!builtin_command(argv))
{
if((pid=fork()) == 0)
{
if(execvp(argv[0],argv) < 0) {
printf("%s : Command not found.
",argv[0]);
exit(0);
}
}
if(!bg){
int status;
if(waitpid(-1,&status,0) < 0)
printf("waitfg: waitpid error!");
}
else
printf("%d %s",pid, cmdline);
return;
}
}
第十章 教材学习内容总结
1.打开和关闭文件
(1)是通过调用open函数来创建一个新文件或者打开一个已存在的文件:
include <sys/types.h>
include <sys/stat.h>
(2)创建一个新文件,要使得文件的拥有者有读写权限,但是所有其他的用户都有只读权限
umask(DEF_UMASK);
fg=Open("foo.txt",O_CREAT|O_TRUNC|O_WEONLY,DEF_MODE);
(3)通过调用close函数关闭一个打开的文件
int close(int fd);
返回值成功为0,出错为-1
2.读和写文件
①读时遇到EOF。假设我们读一个文件,该文件从当前文件位置开始只含有20多个字节,而我们以50个字节的片进行读取。这样一来,下一个read返回的不足值为20,此后的read将通过返回不足值0来发出EOF信号。
②从终端读文本行。如果打开文件是与终端相关联的(如键盘和显示器),那么每个read函数将以此传送一个文本行,返回的不足值等于文本行的大小。
③读和写网络套接字。如果打开的文件对应于网络套接字,那么内部缓冲约束和较长的网络延迟会引起read和write返回不足值。对Unix管道调用read和write时,也有可能出现不足值,这种进程间的通信机制不在我们讨论的范围之内。
实际上,除了EOF,在读磁盘文件时,将不会遇到不足值,而且在写磁盘文件时,也不会遇到不足值。如果想创建简装的诸如web服务器这样的网络应用,就必须通过反复调用read和write处理不足值,直到所有需要的字节都传送完毕。
3.读取文件元数据
(1)应用程序能够通过调用stat和fstat函数,检索到关于文件的信息(元数据)
(2)文件类型
①目录文件:包含其他文件的信息,宏指令:S_ISDIR()
②普通文件:二进制或文本数据,宏指令:S_ISREG()
③套接字:通过网络和其他进程通信的文件,宏指令:S_ISSOCK()
4.I/0重定向
(1)重定向使用dup2函数
(2)Unix外壳提供了I/O重定向操作符,允许用户将磁盘文件和标准输入输出联系起来
代码提交截图
代码提交连接