20155322 2017-2018-1《信息安全系统设计》第五周 MyBash实现


[博客目录]


实现要求

  • 使用fork,exec,wait实现mybash
  • 写出伪代码,产品代码和测试代码
  • 发表知识理解,实现过程和问题解决的博客(包含代码托管链接)

返回目录

相关知识

bash

bash 是一个为GNU计划编写的Unix shell。它的名字是一系列缩写:Bourne-Again SHell — 这是关于Bourne shell(sh)的一个双关语(Bourne again / born again)

bash是大多数Linux系统以及Mac OS X默认的shell,它能运行于大多数类Unix风格的操作系统之上,甚至被移植到了Microsoft Windows上的Cygwin系统中,以实现Windows的POSIX虚拟接口。此外,它也被DJGPP项目移植到了MS-DOS上。

  • bash参数

    • c字符串

      若用-c参数,则bash从字符串中读入命令,如果字符串后还有变量就被设定为从$0开始的位置参数。
    • i

      若用-i参数,则bash是交互的。
    • s

      若用-s参数,则bash从标准输入中读入命令(在执行完-c带的命令之后。)直到输入exit。

      -“-”

      单一的号表明参数执行完毕,并且屏蔽此后所跟参数,后面的所有变量都被看作是文件名。
    • norc

      如果bash是交互的,则不执行个人初始化文件:-/.bashrc,如果bash作为sh来运行,这个参数缺省是关闭的。
    • noprofile

      不执行系统范围的启动文件/etc/profile也不执行个人的启动文件-/.bash_profile,-/.bash_login或-/.profile,缺省情况下,bash作为登录的shell时以这些文件作为启动文件。
    • -refile文件名

      如果bash是交互的,则以此文件作为bash的启动文件。替代-/.bashrc。
    • version

      在bash开始时显示此bash的版本号。
    • quiet

      不显示版本号和其他信息,这是缺省值。
    • login

      激活bash,伪装为登录shell。
    • nobraceexpansion

      不执行大括号扩展。
    • nolineediting

      在交互状态下不使用GNU的readline库去读取命令。即取消了命令行编辑功能。
    • posix

      改变bash的行为,使其符合Posix 1003.2规定的标准。

返回目录

fork

计算机程序设计中的分叉函数。fork函数将运行着的程序分成2个(几乎)完全一样的进程,每个进程都启动一个从代码的同一位置开始执行的线程。这两个进程中的线程继续执行,就像是两个用户同时启动了该应用程序的两个副本。

  • 函数原型:
pid_t fork( void);
//pid_t 是一个宏定义,其实质是int 被定义在#include<sys/types.h>中
  • 返回值:

    若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1
  • 函数说明:

    一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程(child process)。fork函数被调用一次但返回两次。两次返回的唯一区别是子进程中返回0值而父进程中返回子进程ID。子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。UNIX将复制父进程的地址空间内容给子进程,因此,子进程有了独立的地址空间。在不同的UNIX (Like)系统下,我们无法确定fork之后是子进程先运行还是父进程先运行,这依赖于系统的实现。所以在移植代码的时候我们不应该对此作出任何的假设。

    man查询结果如下:

返回目录

exec

把当前进程映像替换成新的程序文件,而且该程序通常在main函数开始执行。

  • exec函数族:
#include <unistd.h>

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 execve(const char *path, char *const argv[], char *const envp[]);
  • 其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。
  • exec函数的作用是根据指定的文件名找到可执行文件,并用它来取代调用进程的内容,换句话说,就是在调用进程内部执行一个可执行文件。这里的可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件

    man查询结果如下:

返回目录

wait

等待子进程中断或结束(等待直到一个进程标识终止)。

  • 函数原型:

pid_t wait (int * status);

  • 表头文件:
#include<sys/types.h>
#include<sys/wait.h>
  • 调用 wait 函数时,调用进程将会出现下面的情况:

    • 如果其所有子进程都还在运行, 则阻塞。
    • 如果一个子进程已经终止,正等待父进程获取其终止状态,则获取该子进程的终止状态然后立即返回。
    • 如果没有任何子进程,则立即出错返回。

      man查询结果如下:

  • 实例代码:
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h> main()
{
pid_t pid;
int status,i;
if(fork()= =0){
printf(“This is the child process .pid =%d\n”,getpid());
exit(5);
}else{
sleep(1);
printf(“This is the parent process ,wait for child...\n”;
pid=wait(&status);
i=WEXITSTATUS(status);
printf(“child’s pid =%d .exit status=%d\n”,pid,i);
}

结果:

This is the child process.pid=1501
This is the parent process .wait for child...
child’s pid =1501,exit status =5

返回目录

相关问题

  • 问题:为什么fork会返回两次?
  • 通过百度百科我得到了答案:由于在复制时复制了父进程的堆栈段,所以两个进程都停留在fork函数中,等待返回。因此fork函数会返回两次,一次是在父进程中返回,另一次是在子进程中返回,这两次的返回值是不一样的。过程如下图。



    fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:

    • 在父进程中,fork返回新创建子进程的进程ID;
    • 在子进程中,fork返回0;
    • 如果出现错误,fork返回一个负值。

在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID我们可以通过fork返回的值来判断当前进程是子进程还是父进程。

引用一位网友的话来解释fork函数返回的值为什么在父子进程中不同:“其实就相当于链表,进程形成了链表,父进程的fork函数返回的值指向子进程的进程id, 因为子进程没有子进程,所以其fork函数返回的值为0.调用fork之后,数据、堆、栈有两份,代码仍然为一份但是这个代码段成为两个进程的共享代码段都从fork函数中返回,箭头表示各自的执行处。当父子进程有一个想要修改数据或者堆栈时,两个进程真正分裂。

  • 实例代码:
#include<sys/types.h>//对于此程序而言此头文件types.h用不到
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h> int main(int argc,char *argv[])/*整数类型主函数*/
{
pid_t pid=fork();/*传递参数*/
if(pid<0)/*如果(进程标记<0)*/
{
fprintf(stderr,"错误!");
}
else if(pid==0)/*否则如果(进程标记==0)*/
{
printf("这是子进程!");
exit(0);
}
else/*否则*/{
printf("这是父进程!子进程的进程标记为=%d",pid);
}
//可能需要时候wait或waitpid函数等待子进程的结束并获取结束状态
exit(0);
}

返回目录

  • 问题:对于execvp函数原型中的int execvp(const char *file, char *const argv[])*file的定义?
  • 解决:是程序的可执行文件的名字。
    • execlp和execvp的第1个参数 file可以简单到仅仅是一个文件名,如 "ls",这两个函数可以自动到环境变量PATH制定的目录里去寻找。
  • 实例测试:
#include <stdio.h>
#include <unistd.h> int main() {
char *arglist[3]; arglist[0] = "ls";
arglist[1] = 0;
arglist[2] = 0;//NULL
printf("*** I want to exec "ls" ***\n");
execvp("ls", arglist);
printf("*** "ls" is done ***\n"); return 0;
}

结果

返回目录

  • 问题:关于fork函数的理解
  • 解决:这里我参考了刘子健同学的博客中关于fork的demo,并进行了测试,通过sleep函数的来体现出fork的效果,我觉得这个demo非常好,便于理解fork,在此记录:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h> int main() {
int ret_from_fork, mypid;
mypid = getpid();
printf("Before: my pid is %d\n", mypid);
ret_from_fork = fork();
sleep(1);
printf("After: my pid is %d, fork() said %d\n", getpid(), ret_from_fork); return 0;
}
  • 结果:

返回目录

代码链接

返回目录

实现

伪代码(流程):

  • 循环 {

    • 输入指令;
    • 判断是否为回车
      • 是:结束输入,存储指令,fork,执行
      • 否:继续输入

        }

返回目录

产品代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h> #define MAXARGS 20
#define ARGLEN 100 int mybash5322(char *arglist[]);
int execute(char *arglist[]);
char *makestring(char *buf); int main() {
char *arglist[MAXARGS + 1];
int numargs;
char argbuf[ARGLEN]; numargs = 0;
while (numargs < MAXARGS) {
printf("Arg[%d]? ", numargs);
if (fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n')
arglist[numargs++] = makestring(argbuf);
else {
if (numargs > 0) {
arglist[numargs] = NULL;
mybash5322(arglist);
numargs = 0;
}
}
}
return 0;
} int mybash5322(char *arglist[])
{
int pc,pr;
pc=fork();
pr=wait(NULL);
if(pc==0) execute(arglist);
else return 0;
} int execute(char *arglist[])
{
execvp(arglist[0],arglist);
perror("execvp failed");
exit(1);
} char *makestring(char *buf)
{
char *cp;
buf[strlen(buf)-1] = '\0';
cp = malloc( strlen(buf)+1 );
if ( cp == NULL ){
fprintf(stderr,"no memory\n");
exit(1);
}
strcpy(cp, buf);
return cp;
}
  • 实现结果:

返回目录

本周结对学习情况

  • 结对学习博客

    20155302
  • 结对学习图片

  • 结对学习内容
    • 教材第三章

返回目录

参考资料

返回目录

20155322 2017-2018-1 《信息安全系统设计》第五周 MyBash实现的更多相关文章

  1. 20155322 2017-2018-1《信息安全系统设计》第二周 课堂测试 Linux下C语言实现MYOD

    20155322 2017-2018-1<信息安全系统设计>第二周 课堂测试 Linux下C语言实现MYOD [博客目录] 静态库测试 实现方法 相关资料 操作 动态库测试 实现方法 相关 ...

  2. 20155306 2017-2018-1《信息安全系统设计》第二周课堂测试以及myod的实现

    20155306 2017-2018-1<信息安全系统设计>第二周课堂测试以及myod的实现 第二周课堂测验: (注:前两项在课堂已提交,在此不做详解) 第一项: 每个.c一个文件,每个. ...

  3. 第五周 mybash的实现

    第五周 mybash的实现 1. 使用fork,exec,wait实现mybash 2. 写出伪代码,产品代码和测试代码 3. 发表知识理解,实现过程和问题解决的博客(包含代码托管链接) 1. for ...

  4. 20155322 2017-2018-1《信息安全系统设计》第九周 Linux命令:pwd命令学习与简单实现

    pwd命令学习 功能 Linux中用 pwd 命令来查看"当前工作目录"的完整路径. 格式 pwd [选项] 实例 用 pwd 命令查看默认工作目录的完整路径: 用 pwd 命令查 ...

  5. 2017-2018-1 20155215 第五周 mybash的实现

    题目要求 使用fork,exec,wait实现mybash 写出伪代码,产品代码和测试代码 发表知识理解,实现过程和问题解决的博客(包含代码托管链接) 学习fork,exec,wait fork ma ...

  6. 20155322 2017-2018-1《信息安全系统设计》第十周 课下作业-IPC

    20155322 2017-2018-1<信息安全系统设计>课下作业-IPC 作业内容 研究Linux下IPC机制:原理,优缺点,每种机制至少给一个示例,提交研究博客的链接. 共享内存 管 ...

  7. 2018-2019-1-20165221&20165225 《信息安全系统设计》实验五:通讯协议设计

    2018-2019-1-20165221&20165225 <信息安全系统设计>-实验五:通讯协议设计 OpenSSL学习: 简介: OpenSSL是为网络通信提供安全及数据完整性 ...

  8. 20155339 2017-2018-1《信息安全系统设计》第四周课堂测试、Makefile以及myod

    20155339 2017-2018-1<信息安全系统设计>第四周课堂测试.Makefile以及myod 测试1-vi 每个.c一个文件,每个.h一个文件,文件名中最好有自己的学号 用Vi ...

  9. 2018工业信息安全技能大赛华东赛区初赛 第2题 writeup

    2018工业信息安全技能大赛华东赛区初赛 第2题 解题思路 本题主要考察点是对常见工控协议的理解(modbus/tcp和s7comm),题目目标是寻找出报文中某条异常报文流量.很让人疑惑的是,题目中并 ...

随机推荐

  1. JS数据模板分离(告别字符串拼接)-template

    刚开始在写第一个动态网页的demo时,由于html不多,便使用字符串拼接的方法添加到dom来渲染,可是在后来写某外卖app时也需要如此添加,打开代码一看几千行,突然感觉累觉不爱 一行行的拼接有这功夫别 ...

  2. 如何实现本机Windows连接虚拟机中的CentOS

    1.确定CentOS的IP地址,命令为 ifconfig,由此可知,LinuxIP地址为 192.168.85.128 2.WIndows的IP地址为192.168.16.1, 3.保证CentOS和 ...

  3. struts文件上传——文件过大时错误提示的配置问题说明

    开始只在struts.xml文件中加入以下配置 <constant name="struts.multipart.maxSize" value="10000&quo ...

  4. angularjs select下拉搜索框

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. urllib库基本使用

    #导入urllib库 import urllib.request #打开网址 file=urllib.request.urlopen("http://www.sohu.com/", ...

  6. Django中模型(四)

    Django中模型(四) 五.创建对象 1.目的 向数据库中添加数据.当创建对象时,Django不会对数据库进行读写操作,当调用save()方法时,才与数据库交互,将对象保存到数据库中 2.注意 __ ...

  7. selenium + python自动化测试unittest框架学习(七)随机生成姓名

    在自动化测试过程中经常要测试到添加用户的操作,每次都要输入中文,原本是找了十几个中文写成了列表,然后从列表中随机取出填入用户名文本框中,随着测试的增加,发现同名的人搜索出来一大堆,最后在网上找了个随机 ...

  8. NMON监控linux性能

    NMON监控linux性能 一.下载nmon压缩包,下载地址:http://download.csdn.net/download/fhqsse220/6699865 二.安装方法: 1.将nmon_l ...

  9. Android的JNI调用(二)

    Android Studio 2.3在native下已经有了代码提示功能,按照提示下载相应组件就可以debug native代码. 一.Java调用JNI与JNI调用Java 1.1 C调用Java ...

  10. vlc源码分析(二) 播放流程

    当点击播放文件或者输入要播放的文件后,vlc会执行一系列的流程. 首先需要了解视频以及流媒体处理及播放的流程,由链接中的描述,视频以及流媒体处理时,首先要解协议(http,rtmp,rtsp等),然后 ...