系统调用与库函数,底层文件访问系统调用

系统调用和库函数与部分常见的底层文件访问系统调用

1.系统调用与库函数的区别

  系统调用是操作系统内核提供的函数,在内核态运行。直接使用系统调用的效率非常低,因为

  1.使用系统调用会影响系统的性能,在执行系统调用时,操作系统必须从用户态切换到内核态,然后再返回用户态。

频繁的切换会消耗大量的资源(需要保存上下文信息)。所以,系统调用的开销会很大。

    2.硬件会限制底层调用一次能读写的数据块大小,例如,磁带机一次能读写10k,如果你的数据量不是10k的倍数,磁

带机仍然以10k为单位卷绕磁带,从而留下空隙。

  库函数提供了一层对系统调用的封装,使得函数调用更方便,安全以及高效。虽然调用库函数,最终还是要调用系统调用,

但库函数会对系统调用的组织方式要比我们对自己调用方式好的多,很对库函数会对调用的执行进行优化。例如malloc,malloc

的本质也是要调用系统调用才能分配内存,但malloc的实现机制使用了一系列的数据结构来组织和维护申请内存的操作,当然会

比使用者直接调用系统函数高效,安全的多。

2.常见的底层文件系统调用

1.write

  首先,看一下write的原型

  

#include <unistd.h>

size_t write(int fildes,const void *buf,size_t nbytes);

 

  系统调用write的作用是将缓冲区buf的前nbyte个字节写入到与文件描述符filedes相关联的文件中。返回值为实际写入的字节数,

如果返回0,表示未写入数据。如果返回-1,表示调用出错,错误代码保存在全局变量errno中。

  

#include <unistd.h>
#include <stdlib.h>

int main()
{
    if(write(1,"Here is some data\n",18))
        write(2,"A write error has occurred on file descriptor 1\n",46);
    exit(0);
}

执行./write

Here is some data

  在write调用中,文件描述符部分设置为1和2的原因是,当一个程序开始运行时,它会默认打开三个文件描述符:

  0 代表标准输入; 1 代表标准输出;2 代表标准错误。

3.read

  read的原型

  

#include <unistd.h>

size_t read(int fildes,void *buf,size_t nbytes);

  系统调用read的作用是从文件描述符fildes相关联的文件中读取nbytes个字节的数据,并将数据放到数据区buf中,返回值为实际读

入的字节数,如果返回值为0,表示未读入任何数据,已到达文件结尾,返回值为-1,表示调用出错。

#include <unistd.h>
#include <stdlib.h>

int main()
{
    char buffer[128];
    int nread;

    nread = read(0,buffer,128);

    if(nread == -1)
        write(2,"A read error has occurred\n",26);
    if((write(1,buffer,nread)) != nread)
        write(2,"A write error has occurred\n",27);
    exit(0);
}

执行 echo hello there | ./read

hello there

  echo命令将字符串写入到标准输入中,read函数从标准输入中读入字符串放到buf中,write将buf写入到标准输出中,标准输出

中的内容显示在终端上。

4.open

  系统调用open用于创建一个新的文件描述符,它的原型如下:

  

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

int open(const char *path,int oflags);
int open(const char *path,int oflags,mode_t mode);

  path为文件或设备的名字,oflags为打开文件的动作,主要有

  O_RDONLY  : 只读打开

  O_WRONLY : 只写打开

  O_RDWR    : 读写方式打开

  看一个栗子:

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h> int main() { int fd = open("./myfile",O_RDWR); if(fd == -1) write(2,"A open error",12); else write(fd,"A open sucessed\n",16);   //printf("%d\n",fd); exit(0); }

执行./open,再执行 cat myfile

A open sucessed

  可以看到,我们使得fd获得了myfile的文件描述符,这时,可以通过fd来进行对myfile的操作。

  放开那行注释,可以看到fd的值为3。为什么呢?文件描述的产生是随机的吗?并不是,前面提到过

程序会默认打开三个文件描述符0,1,2。在我们调用open产生新的文件描述符时,系统会检查已存在的

文件描述符,然后新的文件描述符的值为除过已存在的文件描述符的最小整数。通过close系统调用的讲解

我们来进一步了解。

  open调用可以再oflages参数中包含可选模式的组合:

  O_APPEND : 以追加的方式写入数据

  O_TRUNC   :  设置文件长度为0,丢弃已有内容

  O_CREAT   : 如果需要,按照参数mode创建文件

  O_EXCL      : 与O_CREAT一起使用,如果文件存在,创建失败。

当我们使用O_CREAT参数来调用open时,要使用有三个参数的open调用。第三个参数指定创建文件的权限。举个栗子

  

int main()
{
        int fd = open("./myfile2",O_RDWR | O_CREAT | O_EXCL,S_IRUSR | S_IWUSR | S_IWOTH);
    if(fd == -1)
        exit(EXIT_FAILURE);
    exit(EXIT_SUCCESS);
}
    

执行./open

执行 ll  myfile2

-rw-------. 1 zhaozhao zhaozhao 0 3月  29 17:48 myfile2

  文件被创建,而且主用户读写权限被设置,但我们在程序中还设置了其他用户写权限,被没有产生这个权限。

实际上,创建文件时,open中给出的权限设置受到umask值的限定。

5.close

  系统调用close可以关闭文件描述符,它的原型为

  

#include <unistd.h>

int close(int fildes);

  调用成功返回值为0,出错为-1.看一个栗子

  

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
    if(close(1) == -1)
        exit(EXIT_FAILURE);
    int fd = open("./myfile",O_RDWR);

    if(write(1,"A close sucessed\n",16) != 16)
        write(2,"A write error",13);

    printf("%d\n",fd);
    exit(EXIT_SUCCESS);
}

执行./close后,终端无任何显示

执行cat myfile

A close sucessed

1

结果分析:

  调用close关闭了1号文件描述符,这是文件描述符1不再代表标准输出,实际上此时文件描述符1不存在

调用open为myfile创建文件描述符,系统会查询此时已存在的文件描述符,此时0,2存在,新的文件描述符

就是1。在上一个讲解open时的例子里面,文件描述符0,1,2存在,所以新的文件描述符为3。如果再调用

open,新的文件描述符会是4。这就是上文所说的很绕口的那句话的意思。然后新的文件描述符为除过已存在的

文件描述符的最下整数。那么printf的结果又是怎么回事呢?实际上,printf执行输出操作,内部调用了write(1,str,n)

操作!!!我们使得文件描述符1成为了myfile的文件描述符,那么printf的结果不再进入标准输出缓冲区,而是进

入文件myfile中。

6.usmak

  usmak是一个系统变量,也是一条命令,同时它也是一个系统调用函数。

  作为一个系统变量,它的作用是:当文件被创建时,为文件的访问权限设定一个掩码。它的值为xxxx;umask的

后三位代表用户主,组用户,其他用户的权限,当我们创建文件时,umask对应的位置如果有效,则文件不会拥有该权限

比如,我的umask为0002,那么,我创建一个新文件时,新文件绝不会有其他用户写权限。

  umask作为一个命令时,可以查看当前系统的umask值,添加 -p选项,后面跟上新的umask值,可以修改umask。

  作为系统调用时,原型为

  

#include <sys/stat.h>
     mode_t umask(mode_t cmask);

我们可以再程序中改变umask值,使得它不再影响我们创建新文件时的权限。修改open部分的代码

int main()
{
    umask(0000);
    int fd = open("./myfile2",O_RDWR | O_CREAT | O_EXCL,S_IRUSR | S_IWUSR | S_IWOTH);
    if(fd == -1)
        exit(EXIT_FAILURE);
    exit(EXIT_SUCCESS);
}

执行./open

执行ll myfile2

-rw-----w-. 1 zhaozhao zhaozhao 0 3月  29 18:27 myfile2

  这时,新文件的权限完全是我们想要的。

 

posted @ 2017-03-29 18:35  是召不是昭  阅读(446)  评论(0编辑  收藏  举报