1.练习open/read/write/close等文件相关系统调用接口,纵向对比fd与FILE结构体
open
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
int open(const char *pathname,int flags);
int open(coonst char *pathname,int flag,mode_t mode);
pathname:要打开或创建的目标文件
flags:打开文件时,可以传入多个参数选项,用下面一个或多个常量进行“或”运算,构成flags
参数:
O_RDONLY : 只打开
O_WRONLY : 只写打开
O_RDWR : 读,写打开
这三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它,需要使用mode选项,来指明新文件的访问权限
write
size_t write(int fd,const void *buf,size_t nbyte)
参数:
fd:文件描述符
buf:指定的缓冲区,即要写入的指针
nbyte:要写入文件指定的字节数
返回值:
成功返回写的字节数,失败返回-1
read
size_t read(int fd,const void *buf,size_t nbyte)
参数:
fd:文件描述符
buf:指定的缓冲区
nbyte:要读取文件指定的字节数
返回值:
成功时返回读取到的字节数(为零表示读到文件描述符),此返回值受文件剩余字节数限制,当返回值小于指定的字节数(比如已经接近文件结尾,或者正在从管道或者终端读取数据,或者read()被信号中断),发生错误时返回-1。
close
int close(int fd);
参数:
fd:文件标识符
返回值:
成功返回0,失败返回-1
fd:文件描述符(OS层)
fd只是一个整数,在open时产生。起到一个索引的作用,进程通过PCB中的文件描述符表找到该fd所指向的文件指针file,因此在Linux系统下面,文件描述符主要是被用来标识一个文件。内核通过文件对象表来管理系统中各种各样的文件,而文件表则是通过指针来指向打开的文件,进而达到管理整个文件系统的目的。
FILE:文件指针(lib层,即C库)
1
C中使用文件指针来做为I/O的句柄,文件指针指向进程的用户空间中的一个FILE机构体//C语言文件指针域文件描述符之间可以相互转换
int fileno(FILE * stream)
FILE * fdopen(int fd, const char * mode)
struct _iobuf {
char *_ptr; //缓冲区当前指针
int _cnt;
char *_base; //缓冲区基址
int _flag; //文件读写模式
int _file; //文件描述符
int _charbuf; //缓冲区剩余自己个数
int _bufsiz; //缓冲区大小
char *_tmpfname;
};
typedef struct _iobuf FILE;
2.对之前编写的自主shell进行修改,使其支持输入/输出/追加重定向
我们知道对于Linux来说,shell就是一个命令行解释器,当我们输入相关的命令,会去执行相关的操作。
比如当我们在输入ls -a -l命令,shell就会打印出当前目录的内容,这是如何实现的呢,shell自己就是一个进程,当我们输入类似于ls的命令,它会通过fork,exec去创建一个新的子进程去执行相关操作,因此我们可以利用这个来实现一个简单的shell,当然这个shell足够的简单,并不像Linux内置的shell功能那么强大,支持各种操作,报错等等。
我们先来再聊聊如何去实现一个shell?
1、首先是提示符,root@host以及当前路径,我们可以使用调用系统api直接打印,这里为了方便,我直接用printf函数打印。
2、解析命令,对于ls -a -l这种操作,我们只需要存入到指针数组中,char* shell_argv[32],ls存到shell_argv[0]当中,-a存到shell_argv[1],-l存到shell_argv[2]……………最后设置为一个NULL
3、利用execvp进行调用新的程序。
代码如下:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>
#include <ctype.h>//isspace函数的头文件
#include <fcntl.h>
int main(){
for(;;)
{
printf("myshell@host:");
fflush(stdout);//
//解析输入到shell上的字符串 ls -a -l
char buffer[1024];
int read_size=read(1,buffer,sizeof(buffer));
if(read_size>0){
buffer[read_size-1]=0;
}
char *shell_argv[32]={NULL};
int shell_index=0;
char *start=buffer;
while(*start!='\0'){
while(*start!='\0'&& isspace(*start)){
*start='\0';
start++;
}
shell_argv[shell_index++]=start;
while(*start!='\0' && !isspace(*start)){//
start++;
}
}
//创建子进程的exec
pid_t pid=vfork();
if(pid<0){
printf("vfork failure\n");
exit(1);
}
else if(pid==0){
//考虑重定向
//从字符串数组中找重定向标志
int i=0;
int flag=0;
for(;shell_argv[i]!=NULL;++i){
if(strcmp(">",shell_argv[i])==0){
flag=1;
break;
}
}
int copyfd;
shell_argv[i]=NULL;
if(flag)
{
if(shell_argv[i+1]==NULL){
printf("command error\n");
exit(1);
}
close(1);
int fd=open(shell_argv[i+1],O_WRONLY | O_CREAT,0777);
copyfd=dup2(1,fd);//把标准输出重新定向到一个文件中
}
execvp(shell_argv[0],shell_argv);
if(flag){
close(1);
dup2(copyfd,1);
}
exit(1);
}
else //father process
{
int status=0;
int ret=waitpid(pid,&status,0);
if(ret==pid){
if(WIFEXITED(status)){
}
else if(WIFSIGNALED(status)){
printf("signal is %d\n",WTERMSIG(status));
}
}
}
}
return 0;
}
3.编写简单的add/sub/mul/div函数,并打包成动/静态库,并分别使用。
//代码
add.h
#ifndef _ADD_H
#define _ADD_H
int myadd(int x,int y);
#endif
add.c
#include<stdio.h>
#include"add.h"
int myadd(int x,int y)
{
return x+y;
}
sub.h
#ifndef _SUB_H
#define _SUB_H
int mysub(int x,int y);
#endif
sub.c
#include<stdio.h>
#include"sub.h"
int mysub(int x,int y)
{
return x-y;
}
mul.h
#ifndef _MUL_H
#define _MUL_H
int mymul(int x,int y);
#endif
mul.c
#include<stdio.h>
#include"mul.h"
int mymul(int x,int y)
{
return x*y;
}
div.h
#ifndef _DIV_H
#define _DIV_H
int mydiv(int x ,int y);
#endif
div.c
#include<stdio.h>
#include"div.h"
int mydiv(int x,int y)
{
return x/y;
}
test.c
#include<stdio.h>
#include"add.h"
#include"sub.h"
#include"mul.h"
#include"div.h"
int main()
{
int x = 10;
int y = 20;
int c = myadd(x,y);
int h = mysub(x,y);
int m = mymul(x,y);
int d = mydiv(x,y);
printf("%d\t%d\t%d\t%d\n",c,h,m,d);
return 0;
}
静态库:生成
[root@lrs lib.a]# gcc -c add.c -o add.o
[root@lrs lib.a]# gcc -c sub.c -o sub.o
[root@lrs lib.a]# gcc -c mul.c -o mul.o
[root@lrs lib.a]# gcc -c div.c -o div.o
[root@lrs lib.a]# ar -rc libmymath.a add.o sub.o mul.o div.o
[root@lrs lib.a]# ls
add.c add.h add.o div.c div.h div.o libmymath.a mul.c mul.h mul.o sub.c sub.h sub.o test.c
静态库:使用
[root@lrs lib.a]# mkdir test
[root@lrs lib.a]# cd test/
[root@lrs test]# cp ../*.h .
[root@lrs test]# ls
add.h div.h mul.h sub.h
[root@lrs test]# cp ../test.c .
[root@lrs test]# ls
add.h div.h mul.h sub.h test.c
[root@lrs test]# cp ../libmymath.a .
[root@lrs test]# ls
add.h div.h libmymath.a mul.h sub.h test.c
新建一个文件test,将所有的头文件和test.c文件和libmymath.a文件拷贝至test文件,
[root@lrs test]# gcc test.c -L. -lmymath
//生成可执行的a.out文件
[root@lrs test]# ls
add.h a.out div.h libmymath.a mul.h sub.h test.c
-L:指定库路径
-l:指定库名
动态库:生成
[root@lrs lib.so]# ls
add.c add.h div.c div.h mul.c mul.h sub.c sub.h test.c
[root@lrs lib.so]# gcc -fPIC -c add.c sub.c mul.c div.c
[root@lrs lib.so]# gcc -shared -o libmymath.so *.o
[root@lrs lib.so]# ls
add.c add.h add.o div.c div.h div.o libmymath.so mul.c mul.h mul.o sub.c sub.h sub.o test.c
shared:表示生成共享库格式
fPIC:产生位置无关码
库名规则:libxxx.so
l:链接动态库,只要库名即可(去掉lib及版本号)
L:链接库所在路径
动态库:使用
将此目录下的libmymath.so文件拷贝至/usr/lib目录下
[root@lrs lib]# cp /home/duyuetao/code/practice/code/lib.so/libmymath.so .
[root@lrs lib]# ls
alsa debug gcc java-1.7.0 jvm-exports libmymath.so mozilla sendmail
binfmt.d dracut grub java-1.8.0 jvm-private locale NetworkManager cpp
[root@lrs lib.so]# gcc test.c -lmymath
[root@lrs lib.so]# ls
add.c add.h add.o a.out div.c div.h div.o libmymath.so mul.c mul.h mul.o sub.c sub.h sub.o test.c