linux保留01数字中的0,Linux0.01分析笔记(4)

代表字符串开始的命令

然后,shell会将输入的命令字符串切换成为令牌(token)字符串。在引导中的所有字符串成为一个令牌。空格是隔开各个令牌的分割符。

然后,shell使用如下的规则处理命令行的通配符:

包含通配符(当前设定为*)的令牌被假定成文件名,通配符被扩展成为匹配的文件名(*匹配任何字符)。

shell执行I/O重定向(I/O readirections):

'> word' 重定向标准输出(redirect stdout)

'>> word' 重定向标准输出,添加在指定的文件后

'>& amp;word' 重定向标准输出和标准错误输出(redirects stdout and stderr)

'< word' 重定向标准输入(redirects stdin)

shell处理的文件描述符一般有三个:文件句柄0(stdin,标准输入),文件句柄1(stdout,标准输出),文件句柄2(stderr,标准错误输出)。

dup

()系统调用的功能是返回一个新的文件描述符,它指向传入参数文件描述符指向的文件流。dup()使用进程文件分配表中的第一个没有使用的文件句柄复制为

新的文件句柄。dup()经常使用的一个功能就是完成标准I/O的重定向。使用dup()进行I/O重定向的代码片段如下:

int fd,pid;

fd = open(path,mode);

if((pid=fork())==0) {

close(0);/*Close stdin.*/

/* Make stdin come from fd.Dup() will reuse the lowest.

*number unused file descriptor,0 in this case.*/

dup(fd);

close(fd); /*No longer needed.*/

execve(...); /*Run command,with input from fd..*/

} else {

/*Parent.Wait for child processes to exit.*/

while(wait(NULL)>0);

}

pipe

()函数可以创建一个FIFO流(First In First Out

stream,先进先出流)来完成进程间的通信。pipe()系统调用生成俩个文件句柄,一个可以向FIFO流中写入,一个可以从FIFO流中读取。可以

通过在父进程中使用pipe()函数来创建管道,然后再生成fork()子进程,每一个生成的子进程中都有父进程调用pipe()函数生成的文件句柄的复

制。在子进程中,>可以把父进程中通过pipe()生成的句柄使用dup()函数来重定向子进程的标准输入(stdin),标准输出

(stdout)和标准错误输出,这样父进程就可以控制子进程的标准I/O了。

int i,pid,mypipe[2];/*Two fds,for pipe ends.*/

pipe(mypipe); /*Create pipe.*/

/*Create reader.*/

if((pid=fork())==0) {

close(0); /*Close stdin.*/

dup(pipe[0]); /*Stdin comes from pipe.*/

close(pipe[0]); /*No longer needed.*/

close(pipe[1]); /*Not needed.*/

execve(...); /*Run command,with input from pipe.*/

}

/*Create write.*/

if((pid=fork())==0) {

close(1); /*Close stout.*/

dup(pipe[1]); /*stdout goes to pipe.*/

close(pipe[1]); /*No longer needed.*/

close(pipe[0]); /*Not needed.*/

execve(...); /*Run command,with output to pipe.*/

}

/*Parent.*/

close(pipe[0]);

close(pipe[1]);

while(wait(NULL)>0); /*Wait for child processes to exit.*/

一个例子:

[gaowei@localhost 10SHELL编程技术和实例]$ gcc -o dup dup.c

[gaowei@localhost 10SHELL编程技术和实例]$ ls

10 a.out dup dup.c sh1.c sh2 sh2.c

[gaowei@localhost 10SHELL编程技术和实例]$ ./dup

This is to stdout

This is to stderr

[gaowei@localhost 10SHELL编程技术和实例]$ ls

10 a.out dup dup.c err out sh1.c sh2 sh2.c

[gaowei@localhost 10SHELL编程技术和实例]$ cat ./out

This is to stdout

[gaowei@localhost 10SHELL编程技术和实例]$ cat ./err

This is to stderr

[gaowei@localhost 10SHELL编程技术和实例]$ ls -a

. .. 10 .10.swp a.out dup dup.c err out sh1.c sh2 sh2.c

[gaowei@localhost 10SHELL编程技术和实例]$ ls

10 a.out dup dup.c err out sh1.c sh2 sh2.c

[gaowei@localhost 10SHELL编程技术和实例]$

一个成熟的shell应该支持管道操作总体结构如下:

(cmd1 | cmd2) /* cmd1的标准输出为cmd2的标准输入 */

pipe[fdarr]; /*定义管道 */

if((pid1 = fork()) == 0) {

close(1); /*关闭cmd1的stdout */

dup(fdarr[1]); /* 设置fdarr[1]为cmd1的stdout */

close(fdarr[0]);

aptr[0]="cmd1"; aptr[1]=NULL;

execve("cmd1",aptr,eptr);

}

if((pid2=fork()) ==0) {

close(0); /*关闭cmd2的stdin */

dup(fdarr[0]); /*设置fdarr[0]为cmd2的stdin */

close(fdarr[0]);

close(fdarr[1]);

aptr[0]="cmd2";aptr[1]=NULL;

execve("cmd2",aptr,eptr);

}

close(fdarr[0]);

close(fdarr[1]);

wait() for both pid1 and pid2;

shell程序的主程序组成如下:

#include "def.h"

main(void)

{

int i;

initcold();

for(;;) {

initwarm();

if(getline())

if(i=parse())

execute(i);

}

}

第十一章

分析Linux0.01中实现系统调用的俩个文件system_call.s和sys.c来展示Linux0.01的系统调用的实现方式。标准的C语言库函数,在不同的操作系统上有不同的内部实现。

应用程序可以通过一个固定的过程,从而调用内核提供的功能,在Intel体系结构的计算机中,这是通过执行中断0x80h实现的。

用程序通常是一个进程,进程在调用内核时,跳转到内核代码中的位置一般标记为system_call(在Linux0.01中,system_call是

汇编程序system_call.s中的一段代码的入口点的标记)。在system_call位置的代码将检查系统调用号,依据系统调用号告诉系统内核进

程请求的系统服务是什么。然后,它再查找系统调用表sys_call_table[],找到希望调用的内核函数的地址,并调用此函数,最后将控制权返回应

用程序。编写一个自己的函数,然后改变sys_call_table[]中的指针并指向该函数。

Linux用于实现系统调用异常的实际指令是:int $0x80

定义系统调用的预定义宏为: _syscallN(parameters)

在include/unistd.h中可以找到_syscallN(parameters)还有__NR_name的形式定义了66个常数,这些常数就是系统调用函数name的函数指针在系统的调用表中的偏移量。

系统调用表是一张表格,按照顺序定义了系统中所有的系统调用的入口函数地址。在Linux0.01中,系统调用表定义在include/linux/sys.h中,如下所示:

fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,

sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,

sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,

sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,

sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,

sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,

sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,

sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,

sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,

sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,

sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,

sys_getpgrp,sys_setsid};

数组sys_call_table[]中的每一个元素都是一个函数指针(在C语言中,函数名代表指向函数入口的指针),按系统调用号(即前面提到的

_NR_name)排列了所有系统调用函数的指针,以供系统调用入口函数查找。从这张表可以看出,Linux给它所支持的系统调用函数取名叫

sys_name。

系统调用入口函数定义在/linux/kernel/system_call.s文件中。是使用汇编语言书写的,包含了系统调用的主要处理程序,同时也包含了时钟中断处理程序,硬盘中断处理程序也包含在本文件中。

从system_call入口的汇编程序的主要功能是:

检查是否为合法的系统调用。

保存寄存器的当前值。

据系统调用表_sys_call_table和EAX寄存器持有的系统调用号找出并转入系统调用响应函数。从该响应函数返回后,让EAX寄存器保存函数返

回值,跳转至ret_from_sys_call,>当ret_from_sys_call结束后,将执行进程调度。

在执行位于用户程序中系统调用命令后面余下的指令之前,若INT 0x80h的返回值非负,则直接按类型type返回;否则,将INT 0x80h的返回值取绝对值,保留在errno变量中,返回-

第十二章

关于这一章我记录的比较少

尽管在Linux0.01中没有写网络的部分,但是现在是个网络的时代,这本书中也讲了关于网络的部分,而且我又是学习网络的,所以就在写一写了。

Linux是一个网络操作系统。

TCP/IP协议是一套数据通信协议,其名字是由这些协议中的俩个主要的协议组成的,即传输控制协议(Transmission Control Protocol,TCP)和网间协议(Internet Protocol,IP)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值