CS162 shell

news2024/9/19 11:08:56

本文记录我在做shell这个作业时用到有关资源,如Linux系统调用、Linux基础知识、C语言知识等。

这里只是非常简略地记录了一下,并且可能有理解不正确的地方,你可以把本文当作一个索引和没有思路时的启发,详细的信息可以再去查,我也给出了一些可能会用到的文档链接。

另一方面,我不想这篇文章干扰了你的思路,如果你发现你完全没有用我说到的东西就实现了功能,那也不要怀疑自己,毕竟,shell肯定有多种实现的方法。

判分问题:我在判分的时候一直是8分,直到完成了signal handling后才变成了9分,所以不要担心分数为什么一直不变。

对于各个系统调用,推荐你看官方的Linux manual或者可以在命令行直接用man查看。

文章目录

    • support for cd and pwd
      • chdir
      • getcwd
      • errno
      • tokens
      • 函数指针cmd_fun_t
      • unused
      • getenv
    • program execution
      • C语言的 ...(ellipse)语法
      • exec
      • symbolic link
        • 创建链接
        • 删除链接
      • stat
      • fork
      • wait
      • exit vs kill
      • gdb debug在父子进程间切换
      • 在shell中启动另一个shell产生SIGTTIN报错
      • process group
      • file descriptor
      • isatty
    • Path resolution
      • access
      • strtok和strdup
    • Redirection
      • IO Redrection
      • read write
      • dup
      • open
      • close
      • linux文件类型
      • uid
    • Pipes
      • pipe
      • 优先级问题
      • 关闭不用的文件描述符
    • Signal handling
      • getpgid和getpgrp
      • setpgid和setpgrp
      • tcgetpgrp和tcsetpgrp
      • signal
      • sigaction

support for cd and pwd

chdir

#include<unistd.h>
int chdir(const char *path);
int fchdir(int fd);

chdir()改变当前进程的工作目录到path指定的位置处。

fchdir()与前者唯一不同的是传入的参数是一个打开文件描述符open file descriptor

返回值:若成功则返回0,否则返回-1,并且errno被设置以反映错误。

通过fork()创建的子进程继承父进程当前的目录。

getcwd

#include<unistd.h>
char *getcwd(char *buf, size_t size);
char *getwd(char *buf);
char *get_current_dir_name(void);

返回一个包含绝对路径的以null结尾的字符串,该路径即当前进程的工作目录,该值通过返回值和参数buf返回(如果有)。

  1. getcwd:如果路径长度超过了size,则返回NULL,并且errno设置为ERANGE,程序应该检查该错误,若需要,可以分配一个更大的buf
  2. get_currrent_dir_name:会申请一个足够大的空间存放目录,如果环境变量PWD被设置了,并且其值正确,则返回PWD,使用者应该手动释放buf

errno

#include<errno.h>

表示上次错误的一个数字int,由系统调用(system call)设置,不同的数字可以表示不同的错误,其值都是正数。

tokens

struct tokens {
    size_t tokens_length;
    char** tokens;
    size_t buffers_length;
    char** buffers;
}

存放目录字符的结构体,其中包括一个缓冲区buffers

函数指针cmd_fun_t

typedef int cmd_fun_t(struct tokens* tokens);

该行代码定义了一个函数指针cmd_fun_t,其指向一个函数,参数为tokens*,返回值类型为int

unused

该关键字标识的参数可能在函数中未使用。

getenv

#include <stdlib.h>

char *getenv(const char *name);

getenv("HOME")可以得到/home/username目录。

program execution

C语言的 …(ellipse)语法

函数参数最后可以使用...,这个是为了让函数可以传入可变数量的参数,下面是一个例子:

int a_function(int x, ...) {
    va_list list;	// 存放参数的列表
    va_start(list, x);	// 初始化list
    va_arg(list, int); // 返回list中的第一个参数,以int形式返回
    va_arg(list, int); // 返回list中的第二个参数,以int形式返回
    ...					// 不断地取
    va_end(list);// 取完后清空list
}

a_function(3, 1, 2, 3);  // 调用函数,可以传入不同数量的参数
a_function(5, 2, 9, 1, 8, 7);

要注意的是必须明确地知道每个参数的类型,才能保证取数的正确。

exec

include <unistd.h>
extern char **environ;

int execl(const char *pathname, const char *arg, ...
          /*, (char *) NULL */);
int execlp(const char *file, const char *arg, ...
           /*, (char *) NULL */);
int execle(const char *pathname, const char *arg, ...
           /*, (char *) NULL, char *const envp[] */);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);

在任务2中不使用execvp

exec族的函数将当前进程替换为pathnamefile指定的进程,其中arg, ...argv[]存放参数,例如,对于ls来说,-a-l就是其参数,要注意的是,argv[0]需要放路径,如对于执行ls,可以这样写:

char ** args = { "/bin/ls", "-a", "-s", NULL };
execl(args[0], args[0], args[1], args[2], args[3]);
// 或
execv(args[0], args);

symbolic link

我认为可以将symbolic link当作引用理解。

创建链接

linux中可以创建链接,每个链接指向一个地址,对该链接的修改等同于对原地址内容的修改。

ln -s /home/transactions.txt school/trans.txt

上面的命令创建了一个链接school/trans.txt,对trans.txt的修改即对transactions.txt的修改。

要注意的是链接所在的文件夹(本例中为school)在创建连接前必须已经创建,否则报错。

也可以对文件夹创建链接:

ln -s /home/junhao junhao

链接中会包含所有原文件夹的内容,对链接中文件的修改也会反映到原文件夹。

删除链接

首先可以使用如下命令检查某个文件是否为链接,如果为链接,可以看到有xxx -> xxx形式的输出。

ls -l pathname

例子如下:

可以使用如下命令删除链接:

unlink linkname

stat

#include <sys/stat.h>

int stat(const char *restrict pathname,
         struct stat *restrict statbuf);

该函数返回文件pathname的相关信息,存放在statbuf中,

fork

#include <unistd.h>

pid_t fork(void);

该函数用于创建子进程,如果创建子进程成功,则函数在子进程中返回0,在父进程中返回子进程的pid,如果失败则在父进程中返回-1,并且errno被设置。

wait

#include <sys/wait.h>

pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);

int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
/* This is the glibc and POSIX interface; see
                          NOTES for information on the raw system call. */

wait族的函数用于等待子进程状态的改变,并且获取其信息,状态改变有以下几种情况:

  1. 子进程终止
  2. 子进程暂停
  3. 子进程恢复运行

wait()将父进程挂起,直到其子进程之一终止,其中wait(&wstatus)waitpid(-1, &wstatus, 0)作用一致。

waitpid()将父进程挂起,直到由pid指定的子进程状态改变,默认地,该方法等待子进程终止,可以通过设置参数options指定子进程的行为。

exit vs kill

exit是进程自己结束,而kill是进程关闭其他进程。

gdb debug在父子进程间切换

为了对子进程进行debug,需要在gdb中输入以下命令:

(gdb) set follow-fork-mode child

输入如下命令切换回父进程:

(gdb) set follow-fork-mode parent

在shell中启动另一个shell产生SIGTTIN报错

这个问题在后面的Signal Handling部分中会被解决,目前可以不管。

process group

若干个进程组成一组,指向该组的信号量可以统一控制该组中的所有进程。每个组有一个id,值与创建该组的进程的id一致。

fork出来的子进程与父进程在同一组中。

file descriptor

文件描述符是一个数字,为一个已打开文件的唯一标识。

isatty

#include <unistd.h>

int isatty(int fd);

Path resolution

access

#include <unistd.h>

int access(const char *pathname, int mode);

检查当前结成是否可以访问pathname文件,mode中设置检查的方式,其值可以为:

  1. F_OK:检查文件是否存在。
  2. 由R_OK、W_OK和X_OK中的若干个按或(OR)运算得到的掩码:检查文件是否存在,并且赋予读(R_OK)、写(W_OK)和执行权限(X_OK)。

返回值:若成功(文件存在,授予权限成功),则返回0,若失败,返回-1。

strtok和strdup

在解析$PATH的时候我用了strtok()进行字符串,结果这个东西直接把我的$PATH给改了。。。,为了不让strtok()修改$PATH,把用getenv()得到的$PATH用strdup进行复制,用复制品进行解析。

除此之外,还要注意strtok()不会申请新的空间,因此不需要free,珍爱生命,远离strtok()

这个strdup()strcpy()差不多,都是复制,不同的是,它可以自动malloc()一段内存,不用自己申请了,但是依然要手动free

Redirection

IO Redrection

输入输出重定向的介绍和例子

实现思路

read write

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

从文件描述符fd指向的文件中读出count字节的数据,放入缓冲buf中。如果成功,则返回读取的字节数,并且文件中的位置向前那么多。若失败,则返回-1。

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

write与read类似。

dup

#include <unistd.h>

int dup(int oldfd);
int dup2(int oldfd, int newfd);

dup()创建一个新的文件描述符,其指向的文件与oldfd相同,新的文件描述符的值是未被使用的值中的最小值。

dup2()的功能与dup()相同,区别是令描述符newfd指向oldfd指向的那个文件。

若成功则返回新描述符的值,失败则返回-1

open

#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

open()打开文件pathname,若不存在,则会选择性地进行创建文件(若flags中有O_CREAT则创建),flags由若干个标识符的按位或运算组成,如O_CERAT | O_RDONLY

如果flags中没有O_CREATO_TMPFILE,那么mode会被忽略,否则,其必须存在,

若成功,返回文件描述符,若失败,返回-1。

除此之外,你可能会用到creat(pathname, mode),并看到mode值是0600,这个数为0400 | 0200的结果,即创建文件的用户拥有对该文件的读写权限。

close

#include <unistd.h>

int close(int fd);

关闭一个文件描述符,让它不再指向任何文件且不再被使用。若成功,返回0,若失败,返回1。

linux文件类型

有如下3类文件:

uid

即user identifier,linux上的每个用户都有一个唯一的标识符。使用getuid()可以查看当前进程的uid。

uid的介绍。

Pipes

实现思路。

pipe

#include <unistd.h>
int pipe(int pipefd[2]);

创建一个数据单向流通的管道,用于进程间的交互。pipefd数组存放返回的2个文件描述符,pipefd[0]指向最后读取的文件,而pipefd[1]指向最后被写入的文件。

若成功,则返回0,若失败,则返回1。

优先级问题

这里有一个redirectionpipes的优先级问题,这可能是个有用的链接:pipe-redirection-precedence。下图也大致说明了二者的优先级。

关闭不用的文件描述符

这个非常重要,如果不关闭,子进程可能陷入一直等待输入的状态。如果你发现子进程无法终止,很可能是这个问题。

具体地,你可以看:

  1. 香港中文大学的小实验的2.2节
  2. CS 162的Section 3的答案
  3. 前面实现思路的链接中也有说明这个问题。

Signal handling

老师作业文档里的那个tutorial链接很有用,值得一看,这里就不重复给链接了。

getpgid和getpgrp

#include <unistd.h>
pid_t getpgid(pid_t pid);
pid_t getpgrp(void);

返回id为pid的进程所在的组的group id,如果pid为0,则返回调用该方法的进程所在组的group id,若失败,则返回-1。

getpgrp()getpgid(0)作用相同。

setpgid和setpgrp

#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);
pid_t sepgrp(void);

将id为pid的进程所属的组切换为pgid,同理,若pid为0,则调用该方法的进程的组切换为pgid

setpgrp()setpgid(0, 0)作用相同。

tcgetpgrp和tcsetpgrp

#include <unistd.h>
pid_t tcgetpgrp(int fd);
int tcsetpgrp(int fd, pid_t pgrp);

tcgetpgrp()返回前台进程组的group id。

tcsetpgrp()将前台进程组设为pgrp那组,若fd为0,则用以控制标准输出。

对于以上内容,可以查看手册和下图:

signal

每个信号(signal)都有一个当前的配置(disposition),其决定了进程接收到该信号时所执行的行为,具体可看文档。

sigaction

#include <signal.h>
int sigaction(int signum, const struct sigaction *restrict act,
              struct sigaction *restrict oldact);

该函数修改进程收到指定的某个信号(signal)时所执行的行为。其中:

  1. signum为指定的信号,可以是除了SIGKILLSIGSTOP外的所有有效信号。
  2. act中用于指定新的动作,不为NULL,否则无法指定新动作。
  3. oldact用于存放旧的动作,不为NULL,否则无法存放旧动作。

其中struct sigaction如下所示:

struct sigaction {
    void     (*sa_handler)(int);
    void     (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t   sa_mask;
    int        sa_flags;
    void     (*sa_restorer)(void);
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/60772.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SVM(支持向量机)基本形式推导

据说在dl之前是SVM撑起了ml的半片天&#xff0c;学习后发现SVM是由纯粹的数学推导、转化、求解、优化“堆砌”而来&#xff0c;不如说是数学撑起了ml&#xff0c;ml是数学的学科。以下根据老师ppt上讲解的思路讲讲个人对SVM基本形式推导的理解。 margin&#xff08;间隔&#x…

[附源码]计算机毕业设计现代诗歌交流平台Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

2. IMU原理及姿态融合算法详解

文章目录2. IMU原理及姿态融合算法详解一、组合二、 原理a) 陀螺仪b) 加速度计c) 磁力计三、 旋转的表达a) 欧拉角b) 旋转矩阵c) 四元数d) 李群 SO(3)\text{SO}(3)SO(3) 及 李代数 so(3)\text{so}(3)so(3)四、 传感器的噪声及去除a) 陀螺仪b) 加速度计c) 磁力计五、姿态解算原理…

CSS基础-选择器进阶,背景相关属性(颜色/图片)

CSS基础-选择器进阶,背景相关属性(颜色/图片) 目标&#xff1a;能够理解 复合选择器 的规则&#xff0c;并使用 复合选择器 在 HTML 中选择元素 学习路径&#xff1a;1. 复合选择器2. 并集选择器3. 交集选择器4. hover伪类选择器5. Emmet语法 本次我们所学的内容&#xff1a; 1…

Kafka - 15 Kafka Offset | 自动和手动提交Offset | 指定Offset消费 | 漏消费和重复消费 | 消息积压

文章目录1. Offset 的默认维护位置2. 自动提交 Offset3. 手动提交 Offset1. 同步提交 offset2. 异步提交 offset4. 指定 Offset 消费5. 指定时间消费6. 漏消费和重复消费7. 消费者事务8. 数据积压&#xff08;消费者如何提高吞吐量&#xff09;1. Offset 的默认维护位置 Kafka…

触发器——SR锁存器

组合逻辑的基本单元电路是门电路 另外一种电路叫做时序逻辑电路&#xff0c;时序逻辑电路的输出不但和输入有关&#xff0c;还和原来的状态有关 在这样的电路中&#xff0c;一定要具有存储功能&#xff0c;存储原来的状态&#xff0c;一定也要有反馈回路&#xff0c;返回原来…

4-7:用Redis优化登陆模块

相关功能 使用Redis存储验证码 验证码需要频繁的访问与刷新&#xff0c;对性能要求较高。验证码不需永久保存&#xff0c;通常在很短的时间后就会失效。 (Redis可以设置有效时间&#xff0c;分布式应用也可以绕过session共享的问题)分布式部署时&#xff0c;存在Session共享的…

2022物联卡排行榜公司有哪些?

科技的发展日新月异&#xff0c;我们国家的发展战略也是支持高新科技公司的发展&#xff0c;所以越来越多的高新科技公司出现&#xff0c;但凡是高新科技公司&#xff0c;在设备的联网中&#xff0c;都会用到物联卡&#xff0c;所以物联卡的市场也愈发火爆&#xff0c;那么今天…

Zygote在Framework中起什么作用?

前言 提到Zygote可能了解一些的小伙伴会说&#xff0c;它是分裂进程用的。没错它最大的作用的确是分裂进程&#xff0c;但是它除了分裂进程外还做了什么呢。还是老规矩&#xff0c;让我们抱着几个问题来看文章。最后在结尾&#xff0c;再对问题进行思考回复。 你能大概描述一…

【springboot进阶】使用aop + 注解方式,简单实现spring cache redis 功能

目录 一、实现思路 二、定义缓存注解 三、aop 切面处理 四、使用方式 五、灵活的运用 六、总结 前几天有同学看了 SpringBoot整合RedisTemplate配置多个redis库 这篇文章&#xff0c;提问spring cache 能不能也动态配置多个redis库。介于笔者没怎么接触过&#xff0c;所以…

【Graph】NetworkX官方基础教程

NetworkX官方基础教程图的基础知识1.1 图&#xff08;graph&#xff09;及其分类1.2 节点的度&#xff08;degree&#xff09;1.3 子图&#xff08;subgraph&#xff09;1.4 连通图1.5 图的矩阵表示NetworkX概述NetworkX基础教程1. 创建图2. 节点3. 边4. 清空图5. 图可视化6. 访…

基于javaweb框架的springboot mybatis宠物商城源码含论文设计文档

在互联网高速发展、信息技术步入人类生活的情况下&#xff0c;电子贸易也得到了空前发展。网购几乎成为了人人都会进行的活动。近几年来&#xff0c;养宠物更是成为人们生活中重要的娱乐内容之一&#xff0c; 人们越来越多的讲感情也寄托给了宠物&#xff0c;以给自己另一个感情…

自动驾驶--预测技术

根据百度技术培训中心课程整理( https://bit.baidu.com/productsBuy?id72) 背景简介 无人车系统从算法模块可分为三个部分&#xff0c;首先是感知通过对传感器数据和环境信息进行计算来解决周围有什么的问题&#xff0c;其次是预测&#xff0c;根据感知信息预测环境下一步将…

Java单元测试

1. 序言 1.1 工作中要求进行单元测试 毕业进入公司时&#xff0c;为了锻炼笔者的Java基础&#xff0c;老大给笔者分配了平台化开发的工作&#xff0c;基于Spring Boot Mybatis的Java Web后端开发一个人干后端开发&#xff0c;且以前也没有后端开发的经验&#xff0c;所以只是…

CTF之序列化__toString

序列化简介 本质上serialize()和unserialize&#xff08;&#xff09;在php内部的实现上是没有漏洞的&#xff0c;漏洞的主要产生是由于应用程序在处理对象&#xff0c;魔术函数以及序列化相关问题时导致的。 当传给unserialize()的参数可控时&#xff0c;那么用户就可以注入精…

【应用】Docker Swarm

Docker SwarmDocker Swarm 集群配置配置前准备初始化 SwarmSwarm 常用命令Portainer 集群管理Docker Swarm 集群配置 masternode1node2192.168.86.133192.168.86.131192.168.86.139 配置前准备 关闭各个节点服务器的防火墙 systemctl stop firewalld systemctl disable fire…

ATF问题二则:EL3可能没有实现吗? aarch32中的S-EL1是什么?

最近两个问题&#xff0c;戳到了我的知识盲点&#xff0c;当然我这个菜鸡ATF哪里都是盲点。 问题一&#xff1a;EL3可能没有实现吗&#xff1f; 问题二&#xff1a;bl2是aarch32, 那么bl2是S-EL1&#xff0c;bl31也是S-EL1? 1、EL3可能没有实现吗&#xff1f; The Armv8-A …

基于MATLAB的一级倒立摆控制仿真,带GUI界面操作显示倒立摆动画,控制器控制输出

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 一个可以活动的小车上立着一根不稳定随时会倒下的杆。小车的轮子由电机控制&#xff0c;可以控制小车电机的转动力矩M。同时&#xff0c;也可以获取小车轮子转动的圈数N&#xff08;可以精确到小…

java计算机毕业设计ssm实验室设备管理系统5k648(附源码、数据库)

java计算机毕业设计ssm实验室设备管理系统5k648&#xff08;附源码、数据库&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xf…

162.基于Django-rest_framework的身份验证和权限

1. 概述 到目前为止&#xff0c;程序的API对任何人都可以编辑或删除&#xff0c;没有任何限制。我们希望有一些更高级的行为&#xff0c;进行身份验证和权限分配&#xff0c;以确保&#xff1a; 数据始终与创建者相关联只有经过身份验证的用户才能创建数据只有数据的创建者可…