首页 > 代码库 > 系统调用

系统调用

进程:运行中的程序,它有一些与值关联的文件描述符,有多少个文件描述符取决于系统配置情况。
    当一个程序开始运行时,一般会打开三个文件描述符:
        0:标准输入
        1:标准输出
        2:标准错误
        可以通过系统调用(open)把其他文件描述符与文件和设备相关联。
       
    write系统调用:
        作用:把缓冲区(buf)的前nbytes个字节写入与文件描述符(fildes)关联的文件。
        write返回实际写入的字节数,如果文件描述符有错误或者底层设备的驱动程序对数据长度比较敏感,表示在write调用中出现了
        错误,返回值可能会小于nbytes。如果函数返回0,表示未写入任何数据;返回-1表示write调用中出现了错误,错误代码保存在
        全局变量errno中。
       
        #include <stdio.h>
    #include <unistd.h>    // write调用原型所在的头文件

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

      return 0;
    }
    
    root@ubuntu:/home/linlin/linlin/c_code# ./simple_write.out
        Here is some data

    
    write可能会报告写入的字节比要求的少,这不一定时错误,在程序中,需要检查errno以发现错误,然后再次调用write写入剩
    余数据
    
  read系统调用:
      作用:从文件描述符(fildes)相关联的文件里读入nbytes个字节的数据,并把它们放到数据区buf中。
      read返回实际读入的字节数,这可能会小于请求的字节数,如果read调用返回0,表示未读入任何数据,已到达了文件尾;如果返
      回-1,表示read调用出现了错误。
     
      #include <stdio.h>
    #include <unistd.h>

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

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

      if ( (write(1, buffer, nread)) != nread )
        write(2, "A write error has occurred\n", 27);

      return 0;
    }
    
    root@ubuntu:/home/linlin/linlin/c_code# echo hello there | ./simple_read.out
        hello there
       
    open调用:
        作用:创建一个新的文件描述符。
       
        #include <sys/types.h>
        #include <sys/stat.h>
        #include <fcntl.h>
 
        int open(const char *pathname, int flags);
        int open(const char *pathname, int flags, mode_t mode);

        open建立了一条到文件或设备的访问路径,如果调用成功,它可以返回一个可以被read,write和其它系统调用使用的文件描述符
        这个文件描述符时唯一的,它不会与任何其他运行中的进程共享。
        准备打开的文件或设备的名字作为参数path传递给函数,oflags参数用于指定打开文件所采取的动作。
        oflags参数是通过必须文件访问模式与其它可选模式相结合的方式指定的:
            模式                       说明
            O_RDONLY                   以只读方式打开
            O_WRONLY                   以只写方式打开
            O_RDWR                     以读写方式打开
       
        open调用还可以在oflags参数中包含可选模式的组合(用按位或操作):
            O_APPEND: 把写入数据追加在文件末尾
            O_TRUNC:把文件长度设置为0,丢弃已有的内容
            O_CREAT: 如果需要,就按照参数mode中给出的访问模式创建文件
            O_EXCL:与O_CREAT一起使用,确保调用者创建出文件。open调用是一个原子操作,它只执行一个函数调用。使用这个可选模
            式可以防止两个程序同时创建同一个文件。如果文件已存在,open调用将失败。
           
   
    访问权限的初始值:
        当使用代用O_CREAT标志的open调用来创建文件时,必须使用带有三个参数格式的open调用,第三个参数mode是几个标志按位或得
        到的,这些标志在sys/stat.h中定义:
            S_IRUSR: 读权限,文件属主
            S_IWUSR:写权限,文件属主
            S_IXUSR:执行权限,文件属主
            S_IRGRP: 读权限,文件属组
            S_IWGRP:写权限,文件属组
            S_IXGRP:执行权限,文件属组
            S_IROTH: 读权限,其他用户
            S_IWOTH:写权限,其他用户
            S_IXOTH:执行权限,其他用户
           
        root@ubuntu:/home/linlin/linlin/c_code# cat open_power.c
    #include <stdio.h>
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/stat.h>

    int main(void)
    {
      open("test", O_CREAT, S_IRUSR|S_IXOTH);

      return 0;
    }
    
    root@ubuntu:/home/linlin/linlin/c_code# ls
    ar_lib       hello.c    open_power.c    simple_read.c    simple_write.c
    GetString.c  hello.out  open_power.out  simple_read.out  simple_write.out
    root@ubuntu:/home/linlin/linlin/c_code# ./open_power.out
    root@ubuntu:/home/linlin/linlin/c_code# ls
    ar_lib       hello.c    open_power.c    simple_read.c    simple_write.c    test
    GetString.c  hello.out  open_power.out  simple_read.out  simple_write.out
    root@ubuntu:/home/linlin/linlin/c_code# ls -l test
    -r-------x 1 root root 0 7月  14 17:16 test
    
    文件访问权限的影响因素:
        指定的文件权限只有在创建文件时才会使用
        umask(用户掩码)会影响到被创建文件的访问权限,open调用中给出的mode值将与当时的用户掩码的反值做AND操作
        例如umask为001,open调用指定了S_IXOTH模式标志,那么创建的文件对其他用户不会拥有写执行权限。
       
    open和creat调用中的标志实际上是发出设置文件访问权限的请求,所请求的权限是否会被设置取决于当时的umask值。
    
    复制文件的两个不同实现:
    #include <unistd.h>
    #include <sys/stat.h>
    #include <fcntl.h>

    int main(void)
    {
      char block[1024];
      int in, out;
      int nread;

      in = open("file.in", O_RDONLY);
      out = open("file.m.out", O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
      while ((nread = read(in, block, sizeof(block))) > 0)
        write(out, block, nread);

      return 0;
    }

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

    int main(void)
    {
      char c;
      int in, out;

      in = open("file.in", O_RDONLY);
      out = open("file.out", O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
      while (read(in, &c, 1) == 1)
        write(out, &c, 1);

      return 0;
    }

    
    
    close系统调用:
        使用close调用终止文件描述符fildes与其对应文件之间的关联。文件描述符能被释放并能够重新使用。
        close调用成功时返回0,出错时返回-1
       
        #include <unistd.h>
       
        int close(int fildes);
       
        检查close调用的返回结果非常重要,有的文件系统,特别是网络文件系统,可能不会再关闭文件之前报告文件写操作中
        出现的错误,这是因为在执行操作时数据可能未被正确写入。
       
   
    ioctl系统调用:
        作用;提供一个控制设备及其描述符行为和配置底层服务的接口。
        终端,文件描述符,套接字设置磁带机都可以有为它们定义的ioctl。
       
        #include <unistd.h>
       
        int ioctl(int fildes, int cmd, ...);
       
        ioctl对描述符fildes引用的对象执行cmd参数中给出的操作。根据特定设备支持操作的不同,它可能会有一个可选的第三参数。
       
其它与文件管理有关的调用:用于控制文件的使用方式和返回文件的状态信息
   
    lseek系统调用:对文件描述符fildes的读写指针进行设置。即用来设置文件的下一个读写位置。
    设置时既可以设置为文件中的某个绝对位置,也可以把它设置为相对于当前位置或文件尾的某个位置。
    #include <unistd.h>
    #include <sys/types.h>
   
    off_t lseek(int fildes, off_t offset, int whence);
    offset用来指定位置,whence参数定义该偏移值的用法。
    whence的取值:
        SEEK_SET:offset是一个绝对位置
        SEEK_CUR:相对于当前位置的一个相对位置
        SEEK_END:相对于文件尾的一个相对位置
        lseek返回从文件头到文件指针被设置处的偏移值,失败是返回-1,参数offset的类型off_t是一个与具体实现有关的整数类型
       
    fstat、stat、lstat系统调用:
   
    fstat系统调用返回与打开的文件描述符相关的文件状态信息,该信息将会写到一个buf结构中,buf的地址以参数传递给fstat
    #include <unistd.h>
    #include <sys/stat.h>
    #include <sys/types.h>
   
    int stat(const char *pathname, struct stat *buf);
    int fstat(int fd, struct stat *buf);
    int lstat(const char *pathname, struct stat *buf);
       
        包含头文件sys/types.h是可选的,但由于系统调用的定义指针那些某天可能会做出调整的标准类型使用了别名,所以在程序中
        使用系统调用时,建议包含此头文件
   
    函数stat和lstat返回的是通过文件名查到的状态信息。一般它们产生的结果相同,但当文件是一个符号链接时,lstat返回的是符号
    链接本身的信息,而stat返回的是该链接指向的文件信息。
   
    dup和dup2系统调用:
    dup系统调用提供了一种复制文件描述符的方法,使我们能够通过更多的文件描述符来访问同一个文件。
    dup2系统调用通过明确指定目标描述符来把一个文件描述符复制为另一个
    #include <unistd.h>

    int dup(int oldfd);
    int dup2(int oldfd, int newfd);

系统调用