Unix-Linux编程实践教程读书笔记(一)

文件操作:

  • cp文件复制

    cp shooping_list last.week.list将shopping_list复制一份,新的文件名为last.week.list

  • mv重命名或移动文件

    mv prog1.c first_p.c将文件prog1.c改名

    1
    2
    mkdir mycode
    mv first_p.c mycode

    新建一个文档,然后将文件移动到这个目录中。

Unix系统编程三个重要方面:

  • 通信

    某个用户或进程如何与其他用户或进程交换信息

  • 协助

    在同一个时刻,程序如何来协调多个进程使他们能够没有冲突地访问共享资源?

  • 网络访问

    在这个例子中,互相独立的计算机通过网络连接到一起,那么计算机中的程序是如何来使用网络呢?

后台暂停Ctrl-Z操作:

​ (1) CTRL+Z停止进程并放入后台

​ (2) jobs 显示当前暂停的进程

​ (3) bg %N 使第N个任务在后台运行(%前有空格)

​ (4) fg %N 使第N个任务在前台运行

​ 默认bg,fg不带%N时表示对最后一个进程操作!

标准输入流和直接从输入设备读区别:

一般我们获取输入,都是从标准输入中读数据(例如:getchar()),问题是当用文件通过管道作为输入流传给程序的时候,他不仅数据从标准输入流中获取,后面的命令键值也从标准输入流(就是传入的文件中获取了)

这显然是有问题的。

解决的办法就是从标准输入中读取要处理的数据,直接从键盘读取用户的输入。

Linux下输入设备作为文件:

1
2
3
4
FILE *fp_tty;
fp_tty=fopen("/dev/tty","r");
c=getc(fp_tty);

Man的使用:

​ man -k 可以搜索联机帮助中所有的相关章节。

​ man是按照手册的章节号的顺序进行搜索的,比如:man sleep

​ 只会显示sleep命令的手册,如果想查看库函数sleep,就要输入:man 3 sleep

​ ps:从阅读man手册的description可以大概知道系统命令的实现方式,再去利用man一层层查询。例如:man who会告诉你他是通过检查/var/adm/utmp来得到用户的信息实现的who,如果要查utmp则进一步查询。

​ 例如查询如何从文件中读取数据结构:可以尝试man -k file | grep read

使用:open、read、close)

​ 这几个文件操作都是系统调用,是内核提供的服务,如果在过程中内核检测到任何错误,这个系统调用就会返回-1,而具发生什么错误,需要进一步错误处理

提高文件I/O效率的方法:使用缓冲

​ 例如实现一个cp的功能,从一个文件中读多少,写多少到指定的另一个文件。那么如何让程序更有效率呢?

​ 系统调用时需要开销的,程序中频繁的系统调用会降低程序的运行效率。

​ 例如:

​ 文件大小 =2500字节

​ 如果缓存区大小 = 100字节

​ 那么需要25次read( )和25次write( )

​ 如果缓存区大小 = 1000字节

​ 那么需要3次read( )和3次write( )

​ 关于系统调用为什么开销大:首先操作系统工作环境分为内核模式和用户模式,内核模式和用户模式能做的事情不同,这是为了安全起见,因为在CPU的所有指令中,有一些指令是非常危险的,如果错用,将导致整个系统崩溃。比如:清内存、设置时钟等。如果所有的程序都能使用这些指令,那么你的系统一天死机n回就不足为奇了。所以当发生系统调用的时候,会从用户态转换到内核态,不仅要转移相关的数据,还要建立一些特殊的堆栈和内存环境(上下文),系统调用结束后还要把上下文环境恢复成用户程序运行的状态,这种切换是有比较大开销的。

内核缓冲技术

​ 内核模式和用户模式之间的切换需要消耗时间,相比之下,磁盘的I/O操作消耗时间更多,为了效率,内核也使用缓冲技术来提高对磁盘的访问速度。

​ 当进程所要求的数据块不再内核缓冲区时,内核会把相应的数据块加入到请求数据列表中,然后把该进程挂起,接着为其他进程服务。一段时间之后(很短),内核把相应的数据块从磁盘读取到内核缓存区,然后再把数据复制到进程的缓冲区中,最后唤醒被挂起的进程。

​ 例如系统调用read和write。read把数据从内核缓冲区复制到进程缓冲区,write把数据从进程缓冲区复制到内核缓冲区,他们并不等价于数据在内核缓冲和磁盘之间的交换。从理论上讲,内核可以在任何时候写磁盘,但并不是所有的write操作都会导致内核的写动作。内核会把要写的数据暂时存在缓冲区中,积累到一定数量后再一次写入。有时候导致意外情况,比如突然断电,内核还来不及把内核缓冲区的数据写到磁盘上,这些更新的数据就会丢失。

文件读写中write

​ write每次只会更新下一条记录,而不是当前的那一条。因为系统每次打开一个文件都会保存一个指向文件当前位置的指针,当读写操作完成时,指针会移到下一个记录位置,这个指针与文件描述符相关联。

问题:在文件操作中,如何改变一个文件的当前读/写位置?

答题:使用系统调用lseek

​ 指针是与文件描述符相关联的,而不是与文件关联,所以如果两个程序同时打开一个文件,这时会有两个指针,两个程序对文件的读写操作不会相互干扰。

对于不同进程线程对文件的操作:来源

下图展示了文件描述符、打开的文件句柄以及i-node之间的关系,图中,两个进程拥有诸多打开的文件描述符。

img

​ 在进程A中,文件描述符1和30都指向了同一个打开的文件句柄(标号23)。这可能是通过调用dup()、dup2()、fcntl()或者对同一个文件多次调用了open()函数而形成的。

​ 进程A的文件描述符2和进程B的文件描述符2都指向了同一个打开的文件句柄(标号73)。这种情形可能是在调用fork()后出现的(即,进程A、B是父子进程关系),或者当某进程通过UNIX域套接字将一个打开的文件描述符传递给另一个进程时,也会发生。再者是不同的进程独自去调用open函数打开了同一个文件,此时进程内部的描述符正好分配到与其他进程打开该文件的描述符一样。

​ 此外,进程A的描述符0和进程B的描述符3分别指向不同的打开文件句柄,但这些句柄均指向i-node表的相同条目(1976),换言之,指向同一个文件。发生这种情况是因为每个进程各自对同一个文件发起了open()调用。同一个进程两次打开同一个文件,也会发生类似情况。

4. 总结

1. 由于进程级文件描述符表的存在,不同的进程中会出现相同的文件描述符,它们可能指向同一个文件,也可能指向不同的文件

​ 2. 两个不同的文件描述符,若指向同一个打开文件句柄,将共享同一文件偏移量。因此,如果通过其中一个文件描述符来修改文件偏移量(由调用read()、write()或lseek()所致),那么从另一个描述符中也会观察到变化,无论这两个文件描述符是否属于不同进程,还是同一个进程,情况都是如此。

​ 3. 要获取和修改打开的文件标志(例如:O_APPEND、O_NONBLOCK和O_ASYNC),可执行fcntl()的F_GETFL和F_SETFL操作,其对作用域的约束与上一条颇为类似。

​ 4. 文件描述符标志(即,close-on-exec)为进程和文件描述符所私有。对这一标志的修改将不会影响同一进程或不同进程中的其他文件描述符

处理系统调用中的错误

文件读写小结

  • 进程对文件读/x写都要通过文件描述符,文件描述符表示文件和进程之间的连接。
  • 每次系统调用都会导致用户模式和内核模式的切换以及执行内核代码,所以减少程序中的系统调用发射的次数可以提高程序的运行效率。
  • 程序可以通过缓冲技术来减少系统调用的次数,仅当写缓冲区满或读缓冲区空时才调用内核服务。
  • 内核可以通过内核缓冲来减少访问磁盘I/O的次数。

目录和文件属性

​ 什么是目录:目录是一种特殊的文件,它的内容是文件和目录的名字。包含很多记录,每个记录的格式由统一的标准定义。每条记录的内容代表一个文件或目录。与普通文件不同的是,目录文件永远不会空,每个目录都至少包含两个特殊的项——“ . ”和” .. “,一个表示当前目录,一个表示上一级目录。

​ 目录可以被标准的文件操作系统调用打开(open,read,close)

​ 但是通过联机帮助可以知道系统提供专门用来读目录的opendir ,readdie ,closedir等一套对目录进行操作的方法。

本文标题:Unix-Linux编程实践教程读书笔记(一)

文章作者:Yang Shuai

发布时间:2018年06月13日 - 15:06

最后更新:2018年06月21日 - 22:06

原始链接:https://ysbbswork.github.io/2018/06/13/Unix-Linux编程实践教程读书笔记(一)/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!