OS-Experiment for NUAA

这篇博客详细介绍了在Linux环境下使用C语言实现文件读写、复制(myecho、mycat、mycp)以及多进程(mysys、sh3)和多线程(pi1、pi2、sort、pc1、pc2)的实战技巧。作者通过代码示例展示了如何使用系统调用实现文件操作,如何创建和管理进程以及线程,同时还涉及到了进程间通信和同步机制。
摘要由CSDN通过智能技术生成

文件读写

myecho

1.实现功能

myecho.c的功能与系统echo程序相同,接受命令行参数,并将参数打印出来。

2.实现思路

main函数有两个参数:argc和argv,其中argc记录了main 函数的命令行参数总个数,包括可执行程序名;argv[] 是一个字符串数组,每个元素指向一个参数,在命令行输入的字符串会被自动分割为字符串数组,其长度为argc,argv[0]=可执行程序名,argv[1…argc-1]=可执行程序参数,argv[argc] = NULL。

利用main函数的两个参数可以实现echo功能:首先根据argc的值判断是否有需要打印的字符串,如果argc==1,说明只有可执行程序名,没有参数,直接输出 Character not received! 并返回;

/* user haven't input any char */
if(argc == 1){
   
	printf("Character not received!\n");
	return 0;
}

如果不为0,输出 argv 字符串数组中的值即可。需要注意的是:argv[0]中存储的是可执行程序名称,不应该被输出,所以下标应从 1 开始:

/* print char from argv */
for(int i = 1 ;i < argc ; i++)
	printf("%s ",argv[i]);
printf("\n");

mycat

1.实现功能

mycat.c的功能与系统cat程序相同,mycat将指定的文件内容输出到屏幕,可以处理无参数和多参数的情况,要求使用系统调用open/read/write/close实现。

2.实现思路

mycat在输出文件内容时主要通过函数调用read/write实现,read函数返回值为rd,如果读取失败,返回-1,如果读取成功,返回实际读取的字节个数;返回0则代表读取到了文件末尾,可以用if语句作为是否读完的条件判断,再将读到的rd个字节用write写出到标准输出 STDOUT_FILENO

mycat在没有参数时,也就是argc==1,只有一个程序名,那程序会将用户在屏幕输入的内容原封不动打印出来,此时read的文件描述符参数就是STDIN_FILENO,表示从屏幕输入。由于输入的内容大小不确定,因此需要开辟一个不是很大的缓冲区来接收字符,通过循环来不断读,这样可以避免输入过大或过小造成的缓冲区溢出或浪费:

if(argc == 1)
{
   
	while(1)
	{
   
		int rd;
		char buf[10];
		if(rd = read(STDIN_FILENO,buf,MAX) > 0)
            write(STDOUT_FILENO,buf,rd);
		else
			break;
	}
}

mycat有多个参数时,会将这些文件依次打开并输出到标准输出,因此当 argc>1 时,对 argv 中的文件逐个打开,调用read 读取打开的文件,参数为fd,然后用 write 函数将当前文件的字符写到标准输出中去。同样,由于输入的内容大小不确定,因此需要开辟一个不是很大的缓冲区来接收字符,通过循环来不断读,这样可以避免输入过大或过小造成的缓冲区溢出或浪费:

/* show multiple files */
for(int i = 1;i < argc;i++){
   
	int fd = open(argv[i],O_RDONLY);
	if(fd < 0)
		panic("open file fail!");
	while(1)
	{
   
		char buf[10];
		memset(buf,0,MAX);
		int rd;
		if((rd = read(fd,buf,MAX)) > 0)
			/* write to screen */
			write(STDOUT_FILENO,buf,rd);
		else
			break;
	}
	close(fd);
}

mycp

1.实现功能

mycp.c的功能与系统cp程序相同, 将源文件复制到目标文件,要求使用系统调用open/read/write/close实现

2.实现思路

对于mycp,和之前两个程序不同,mpcp需要有可执行程序名argv[0],源文件argv[1],目的文件argv[2],当它的参数小于3个的时候一定是参数错误,因此要先判断 if(argc < 3)

if(argc != 3)
	panic("Parameter error!");

对于目的文件,可能不存在,这时应该创建一个新的文件,因此open函数用 O_CREAT,mode 打开,赋予权限为0777,若两次执行程序都移动到同一文件中,需要覆盖操作,因此open函数还需 O_TRUNC 参数。当文件都打开/创建成功时,调用 read 函数对源文件逐个读取,调用 write 函数写入目标文件中。这里write的文件描述符不再是标准输出STDOUT,而是目标文件的fd2:

int fd1 = open(argv[1], O_RDONLY);
mode_t mode=0777;
int fd2 = open(argv[2], O_RDWR | O_TRUNC | O_CREAT,mode);
if(fd1 < 0 || fd2 < 0)
	panic("Open file 1 failed!");
char buf[MAX];
memset(buf,0,MAX);
int rd;
while(1)
{
   
	rd = read(fd1,buf,MAX);
	if(rd > 0)
		write(fd2,buf,rd);
	else
		break;
}
close(fd1);
close(fd2);

多进程

mysys

1.实现功能

mysys的功能与系统函数system相同,要求用进程管理相关系统调用自己实现一遍,使用fork/exec/wait系统调用。

3.实现思路

mysys的实现主要基于以下三个函数:execvp、strtok、fork

程序main函数读入读入一个字符串 command,传给mysys模块,在mysys模块中,先检查命令长度是否为0,如果没有要执行的命令,则直接返回退出;定义一个myargc表示这个命令的参数个数,定义一个myargv表示参数列表,调用change模块进行字符串处理,返回值为myargc参数个数:

int t=strlen(command);
/* if no command, continue to exec the next one */
if(t == 0)
	return;
int myargc;
char *myargv[MAXARG];
myargc = change(command,myargc,myargv);
myargv[myargc]=NULL;

change模块:strtok不可以直接对字符串常量进行分割,要用strncpy得到一个char型数组buf, 对这个buf数组进行处理即可。用空格作为分隔符,首次调用时,第一个参数指向要分解的字符串,之后再次调用要把第一个参数设成NULL,将分割后的子字符串存入 myargv字符数组中,每次myargc++:

int change(char *command,int myargc,char *myargv[])
{
   
        char buf[MAX];
        memset(buf,0,MAX);
        strncpy(buf,command,strlen(command));
        char *delim=" ";
        myargc=0;
        myargv[myargc]=strtok(buf,delim);
        while(myargv[++myargc]=strtok(NULL,delim));
        return myargc;
}

分割字符串成功后就开始创建一个子进程,调用子进程执行模块chile,让子进程执行execvp,父进程等待子进程结束再退出:

pid_t pid;
pid = fork();
if(pid == 0)
	child(myargc,myargv);
else
	wait(NULL);

子进程模块调用execvp,第一个参数为可执行程序名,存在了myargv[0]中,之后的参数列表通过myargv传入,装入可执行程序,执行命令:

void child(int myargc,char *myargv[])
{
   
        int status = execvp(myargv[0],myargv);
        if(status < 0)
                Perror("Arguments fault!");
        exit(1);
}

sh3

1.实现功能

实现管道和文件重定向。

2.实现构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RfaqS1mh-1593603294451)(C:\Users\lenovo\Desktop\2.PNG)]

3.实现思路

main函数中调用mysys模块:

int main()
{
   
        while (1) mysys();
        return 0;
}

mysys模块实现的是内置命令的处理,其他的命令交给parse_command模块。

首先通过fgets读取字符串,并去掉最后一个换行符变为\0:

char command[ARG_MAX + 1], argv[ARG_MAX + 1];
memset(command, 0, ARG_MAX + 1);
printf("> ");
fgets(command, ARG_MAX, stdin);
command[strlen(command) - 1] = '\0';

先把第一个空格之前的子串分离出来分类讨论

如果是内置命令,就不用fork一个子进程,而是直接执行,exit直接调用exit(0)退出,cd调用chdir(path)切换路径;

如果不是内置命令,就交给parse_command来分析命令:

char *p;
p = command;
/* first command */
char temp[10];
memset(temp,0,10);
int i=0,j=0;
while((*p!=' ')&& (*p))
{
   
	temp[i++]=command[j++];
	++p;
}
if(strcmp(temp,"exit"
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值