20155339 第七周加分项目 mybash的实现

mybash的实现

要求

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

学习相关知识

fork函数

  • 查看帮助文档可以知道fork函数的头文件,函数原型,以及函数的功能等,如下图
    1071519-20171209132252808-1238183277.png

  • fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
  • 一个进程调用fork()函数后,系统先给新的进程分配资源。
  • fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值。
    (1)在父进程中,fork返回新创建子进程的进程ID;
    (2)在子进程中,fork返回0;
    (3)如果出现错误,fork返回一个负值。
  • 我们可以通过fork返回的值来判断当前进程是子进程还是父进程。
    练习代码:


#include <unistd.h>  
#include <stdio.h>   
int main ()   
{   
        pid_t fpid; //fpid表示fork函数返回的值  
            int count=0;  
                fpid=fork();   
                    if (fpid < 0)   
                                printf("error in fork!");   
                        else if (fpid == 0) {  
                                    printf("i am the child process, my process id is %d\n",getpid());   
                                            printf("我是子进程\n");//  
                                                    count++;  
                                                        }  
                            else {  
                                        printf("i am the parent process, my process id is %d\n",getpid());   
                                                printf("我是父进程\n");  
                                                        count++;  
                                                            }  
                                printf("统计结果是: %d\n",count);  
                                    return 0;  
}  

运行结果:
1071519-20171209132309042-923584720.png

  • 问题:为什么count值没有受到影响呢?
  • 解决:因为在创建了新进程之后,所有变量都存在不同的地址中,不是共用的,所有各有各的增减变化,互不影响。

exec函数族

  • 帮助文档
    1071519-20171209132322574-732093391.png

  • exec函数族提供了一个在进程中启动另一个程序执行的方法。
  • 以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段。
  • 可执行文件既可以是二进制文件,也可以是Linux下任何可执行脚本文件。
  • 在该函数族中使用execl、execv、execlp、execvp函数使执行码重生时,Shell进程会将所有环境变量复制给生成的新进程;而使用execle、execve时新进程不继承任何Shell进程的环境变量,而由envp[]数组自行设置环境变量。
  • 练习代码,实现ls

#include<unistd.h>
main()
{
    char *argv[ ]={"ls", NULL};
    char *envp[ ]={"PATH=/bin", NULL};
    execve("/bin/ls", argv, envp);
}
  • 运行结果
    1071519-20171209132334636-987274061.png

wait函数

  • wait()会暂时停止现在进程的执行,直到有信号来到或子进程结束。假如在调用wait()时子进程已结束,则wait()会立即返回子进程结束状态值。
  • waitpid提供了一个 wait的非阻塞版本,有时希望取得一个子进程的状态, 但不想进程阻塞。

mybash实现

  • 伪代码:

while(1)
{
    输出用户的ID,等数据;  
    用户输入命令;  
    将命令分割;
    调用fork;
    调用exec函数;
}
  • 产品代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/wait.h>
#define N 100
#define order_length 128
#define order_num 64
#define empty 0
#define chars 1
void main()
{ 
   while(1) {
    struct passwd *my_info;
    char path[N];
    my_info = getpwuid(getuid());
    getcwd(path, sizeof(path));
    printf("[%s@%s]$ ", my_info->pw_name, path);

    char str[N];
    char a[N];
    char *argv[N]={NULL};
    char *envp[]={0,NULL};
        int i,j = 0, flag=1;
    fgets(str,N,stdin);
    str[N - 1] = '\0';
    if(feof(stdin))
        {
            printf("error");
            exit(0);
        }
    for(i=0;str[i]!='\0'&&i<N&&j<N;i++) {
        if(str[i] == ' ' || str[i] == '\n') {
          flag=1;
          str[i] = '\0';
      }
      else if(flag==1) {
          argv[j++] = &str[i];
          flag=0;
      }
     }
     if(fork() != 0) wait(NULL);
        else {
    execvp(argv[0], argv);
    perror("execlp error");
    exit(0);
     }
 }
}
  • 测试结果:
    1071519-20171209132347511-113023942.png

遇到的问题

  • 问题一:对于用户的信息直接输出的话代码的适用性就不高,那怎么获取用户的信息呢?
  • 解决:上网查询,知道了可以通过getpwuid函数获得用户的数据,该函数是通过用户的uid查找用户的passwd数据。参考链接在下方给出。
  • 问题二:对于输入的信息,怎么进行分割?
  • 解决:刚开始直接通过一个数组获得了命令,再将原数组直接给了execvp的第二个参数,失败,再次研究,决定将用户输入的数组通过空格分割给argv数组,没分割就加上一个'\0',一个空格之后argv的数组下标加一,也就是将命令存在argv[0]中,参数存在argv[1]以及其之后,这样就可以使用execvp函数了。
  • 问题三,在mybash中执行完./fork之后,Ctrl+C。就会直接退出mybash。
  • 未能解决。

参考资料

转载于:https://www.cnblogs.com/pingcpingcuo/p/8011269.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现连续签到加分的Java代码可以参考以下示例: ``` // 定义签到记录类 public class SignInRecord { private Date signInDate; private int signInScore; public SignInRecord(Date signInDate, int signInScore) { this.signInDate = signInDate; this.signInScore = signInScore; } public Date getSignInDate() { return signInDate; } public int getSignInScore() { return signInScore; } } // 定义签到管理类 public class SignInManager { private List<SignInRecord> signInRecords; private int maxContinuousDays; // 最大连续签到天数 private int continuousDays; // 当前连续签到天数 private int signInScore; // 签到得分 public SignInManager() { signInRecords = new ArrayList<>(); maxContinuousDays = 0; continuousDays = 0; signInScore = 0; } public void signIn() { Date today = new Date(); int score = 1; if (!signInRecords.isEmpty()) { // 判断是否连续签到 Date lastSignInDate = signInRecords.get(signInRecords.size() - 1).getSignInDate(); if (isContinuousSignIn(lastSignInDate, today)) { continuousDays++; score = continuousDays; if (continuousDays > maxContinuousDays) { maxContinuousDays = continuousDays; } } else { continuousDays = 1; } } signInRecords.add(new SignInRecord(today, score)); signInScore += score; } public int getMaxContinuousDays() { return maxContinuousDays; } public int getContinuousDays() { return continuousDays; } public int getSignInScore() { return signInScore; } // 判断是否连续签到 private boolean isContinuousSignIn(Date lastSignInDate, Date today) { Calendar lastCal = Calendar.getInstance(); lastCal.setTime(lastSignInDate); int lastYear = lastCal.get(Calendar.YEAR); int lastDayOfYear = lastCal.get(Calendar.DAY_OF_YEAR); Calendar todayCal = Calendar.getInstance(); todayCal.setTime(today); int todayYear = todayCal.get(Calendar.YEAR); int todayDayOfYear = todayCal.get(Calendar.DAY_OF_YEAR); return lastYear == todayYear && todayDayOfYear - lastDayOfYear == 1; } } ``` 实现App签到功能的Java代码可以参考以下示例: ``` // 定义签到记录类 public class SignInRecord { private Date signInDate; public SignInRecord(Date signInDate) { this.signInDate = signInDate; } public Date getSignInDate() { return signInDate; } } // 定义签到管理类 public class SignInManager { private List<SignInRecord> signInRecords; public SignInManager() { signInRecords = new ArrayList<>(); } public void signIn() { Date today = new Date(); signInRecords.add(new SignInRecord(today)); } public int getSignInCount() { return signInRecords.size(); } public boolean hasSignedInToday() { if (signInRecords.isEmpty()) { return false; } Date lastSignInDate = signInRecords.get(signInRecords.size() - 1).getSignInDate(); Calendar lastCal = Calendar.getInstance(); lastCal.setTime(lastSignInDate); int lastYear = lastCal.get(Calendar.YEAR); int lastDayOfYear = lastCal.get(Calendar.DAY_OF_YEAR); Calendar todayCal = Calendar.getInstance(); int todayYear = todayCal.get(Calendar.YEAR); int todayDayOfYear = todayCal.get(Calendar.DAY_OF_YEAR); return lastYear == todayYear && lastDayOfYear == todayDayOfYear; } } ``` 这两份代码仅供参考,实际应用中还需要根据业务需求进行相应的调整和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值