Linux系统编程Day04

1.系统调用

1.1 什么是系统调用	
	系统调用,顾名思义,说的是操作系统提供给用户程序调用的一组“特殊”接口。用户
程序可以通过这组“特殊”接口来获得操作系统内核提供的服务,比如用户可以通过文件系
统相关的调用请求系统打开文件、关闭文件或读写文件,可以通过时钟相关的系统调用获
得系统时间或设置定时器等。	
	从逻辑上来说,系统调用可被看成是一个内核与用户空间程序交互的接口——它好比
一个中间人,把用户进程的请求传达给内核,待内核把请求处理完毕后再将处理结果送回
给用户空间。

1.2 系统调用的实现
	系统调用是属于操作系统内核的一部分的,必须以某种方式提供给进程让它们去调
用。CPU 可以在不同的特权级别下运行,而相应的操作系统也有不同的运行级别,用户
态和内核态。运行在内核态的进程可以毫无限制的访问各种资源,而在用户态下的用户
进程的各种操作都有着限制,比如不能随意的访问内存、不能开闭中断以及切换运行的
特权级别。显然,属于内核的系统调用一定是运行在内核态下,但是如何切换到内核态
呢?
	答案是软件中断。软件中断和我们常说的中断(硬件中断)不同之处在于,它是通过
软件指令触发而并非外设引发的中断,也就是说,又是编程人员开发出的一种异常(该
异常为正常的异常)。操作系统一般是通过软件中断从用户态切换到内核态。

1.3 系统调用和库函数的区别
Linux 下对文件操作有两种方式:系统调用和库函数调用。
库函数由两类函数组成:
	1)不需要调用系统调用
		不需要切换到内核空间即可完成函数全部功能,并且将结果反馈给应用程序,
	如strcpy、bzero 等字符串操作函数。

	2)需要调用系统调用
		需要切换到内核空间,这类函数通过封装系统调用去实现相应功能,如 printf、
	fread等。
	系统调用是需要时间的,程序中频繁的使用系统调用会降低程序的运行效率。
当运行内核代码时,CPU工作在内核态,在系统调用发生前需要保存用户态的栈和内存
环境,然后转入内核态工作。系统调用结束后,又要切换回用户态。这种环境的切换会
消耗掉许多时间 

1.4  C库中IO函数工作流程
	库函数访问文件的时候根据需要,设置不同类型的缓冲区,从而减少了直接调用 IO 
系统调用的次数,提高了访问效率。

1.5 错误处理函数
	errno 是记录系统的最后一次错误代码。代码是一个int型的值,在errno.h中定义。
查看错误代码errno是调试程序的一个重要方法。
	当Linux C api函数发生异常时,一般会将errno全局变量赋一个整数值,不同的值
表示不同的含义,可以通过查看该值推测出错的原因。
#include<stdio.h>
#include<string.h>
#include<errno.h>

int main()
{
        FILE* fp = fopen("txt","r");

        if(NULL == fp)
        {
                printf("fopen error...\n");
                printf("errno:%d\n",errno);
                printf("fopen:%s\n",strerror(errno));

                //根据errno的值输出错误信息
                //提示字符串:出错原因
                perror("fopen");

                return 1;
        }
        return 0;
}
1.6 虚拟地址空间
	每个进程都会分配虚拟地址空间,在32位机器上,该地址空间为4G 。
	在进程里平时所说的指针变量,保存的就是虚拟地址。当应用程序使用虚拟地址访问
内存时,处理器(CPU)会将其转化成物理地址(MMU)。
	MMU:将虚拟的地址转化为物理地址。
	
	这样做的好处在于:
		1.进程隔离,更好的保护系统安全运行
		2.屏蔽物理差异带来的麻烦,方便操作系统和编译器安排进程地址

1.7 文件描述符
	1.在 Linux 的世界里,一切设备皆文件。我们可以系统调用中 I/O 的函数(I:input,
输入;O:output,输出),对文件进行相应的操作( open()、close()、write() 、
read() 等)。
	2.打开现存文件或新建文件时,系统(内核)会返回一个文件描述符,文件描述符用
来指定已打开的文件。这个文件描述符相当于这个已打开文件的标号,文件描述符是非
负整数,是文件的标识,操作这个文件描述符相当于操作这个描述符所指定的文件。
	3.程序运行起来后(每个进程)都有一张文件描述符的表,标准输入、标准输出、标准
错误输出设备文件被打开,对应的文件描述符 0、1、2 记录在表中。程序运行起来后这
三个文件描述符是默认打开的。
	#define STDIN_FILENO  0 //标准输入的文件描述符
	#define STDOUT_FILENO 1 //标准输出的文件描述符
	#define STDERR_FILENO 2 //标准错误的文件描述符

2.常用io函数

2.1 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);
	功能:
	    打开文件,如果文件不存在则可以选择创建。
	参数:
	    pathname:文件的路径及文件名
	    flags:打开文件的行为标志,必选项 O_RDONLY, O_WRONLY, O_RDWR
	    mode:这个参数,只有在文件不存在时有效,指新建文件时指定文件的权限
	返回值:
	    成功:成功返回打开的文件描述符
	    失败:-1
	 
	
	flags详细说明:
	必选项:		
	取值				含义
	O_RDONLY	以只读的方式打开
	O_WRONLY	以只写的方式打开
	O_RDWR		以可读、可写的方式打开
	可选项,和必选项按位或起来
	
	取值				含义
	O_CREAT		文件不存在则创建文件,使用此选项时需使用mode说明文件的权限
	O_EXCL		如果同时指定了O_CREAT,且文件已经存在,则出错
	O_TRUNC		如果文件存在,则清空文件内容
	O_APPEND	写文件时,数据添加到文件末尾
	O_NONBLOCK	对于设备文件, 以O_NONBLOCK方式打开可以做非阻塞I/O
	
	mode补充说明	:
	1) 文件最终权限:mode & ~umask
	
	2) shell进程的umask掩码可以用umask命令查看		
	Ø umask:查看掩码(补码)		
	Ø umask mode:设置掩码,mode为八进制数		
	Ø umask -S:查看各组用户的默认操作权限
	
	取值		八进制	含义
	S_IRWXU	00700	文件所有者的读、写、可执行权限
	S_IRUSR	00400	文件所有者的读权限
	S_IWUSR	00200	文件所有者的写权限
	S_IXUSR	00100	文件所有者的可执行权限
	S_IRWXG	00070	文件所有者同组用户的读、写、可执行权限
	S_IRGRP	00040	文件所有者同组用户的读权限
	S_IWGRP	00020	文件所有者同组用户的写权限
	S_IXGRP	00010	文件所有者同组用户的可执行权限
	S_IRWXO	00007	其他组用户的读、写、可执行权限
	S_IROTH	00004	其他组用户的读权限
	S_IWOTH	00002	其他组用户的写权限
	S_IXOTH	00001	其他组用户的可执行权限
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>

//打开和关闭文件
int main()
{
        int fd = -1;

        //1.以只读的方式打开一个文件,如果文件不存在就报错
        //fd = open("txt",O_RDONLY);

        //2.以只写的方式打开一个文件,如果文件存在就直接打开,如果文件不存在就新建
        //fd = open("txt",O_WRONLY | O_CREAT,0644);

        //3.以只写的方式打开一个文件,如果文件存在就报错,如果文件不存在就新建一个文件
        //fd = open("txt",O_WRONLY | O_CREAT | O_EXCL, 0644);

        //4.以读写的方式打开一个文件,如果文件存在就打开,如果文件不存在就新建一个文件
        //fd = open("txt",O_RDWR | O_CREAT,0644);

        //5.O_TRUNC表示清空文件内容
        //fd = open("txt",O_WRONLY | O_TRUNC | O_CREAT,0644);

        //6.O_APPEND以只写并且追加的方式打开一个文件
        fd = open("txt",O_WRONLY | O_APPEND,0644);


        if(-1 == fd)
        {
                perror("open");
                return 1;
        }

        printf("fd = %d\n",fd);

        //关闭文件
        close(fd);

        return 0;
}


2.2 close函数
	#include <unistd.h>		​
	int close(int fd);
	功能:
	    关闭已打开的文件
	参数:
	    fd : 文件描述符,open()的返回值
	返回值:
	    成功:0
	    失败: -1, 并设置errno
	需要说明的是,当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用
close关闭,所以即使用户程序不调用close,在终止时内核也会自动关闭它打开的所有
文件。

2.3 write函数
	#include <unistd.h>
	ssize_t write(int fd, const void *buf, size_t count);
	功能:
	    把指定数目的数据写到文件(fd)
	参数:
	    fd :  文件描述符
	    buf : 数据首地址
	    count : 写入数据的长度(字节)
	返回值:
	    成功:实际写入数据的字节个数
	    失败: - 1
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>

//写文件
int main()
{
        int fp = -1;
        int ret = -1;

        const char* str = "helloworld!";

        fp = open("txt",O_WRONLY | O_CREAT,0664);
        if(-1 == fp)
        {
                perror("open");
                return 1;
        }

        printf("Open:%d\n",fp);

        //写入内容
        ret = write(fp, str, strlen(str));
        if(-1 == ret)
        {
                perror("write");
                return 1;
        }

        printf("Write:%d\n",ret);
        close(fp);

        return 0;
}

2.4 read函数
	#include <unistd.h>
	​
	ssize_t read(int fd, void *buf, size_t count);
	功能:
	    把指定数目的数据读到内存(缓冲区)
	参数:
	    fd : 文件描述符
	    buf : 内存首地址
	    count : 读取的字节个数
	返回值:
	    成功:实际读取到的字节个数
	    失败: - 1
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>

//写文件
int main()
{
        int fp = -1;
        int ret = -1;
        char buffer[1024] = {0};

        fp = open("txt",O_RDONLY);
        if(-1 == fp)
        {
                perror("open");
                return 1;
        }

        printf("Open:%d\n",fp);

        //读取内容
        ret = read(fp, buffer, 1024);
        if(-1 == ret)
        {
                perror("write");
                return 1;
        }

        printf("Buffer:%s\n",buffer);
        printf("Write:%d\n",ret);
        close(fp);

        return 0;
}

2.5 阻塞和非阻塞的概念
	读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回。
	从终端设备或网络读则不一定,如果从终端输入的数据没有换行符,调用read读终
端设备就会阻塞,如果网络上没有接收到数据包,调用read从网络读就会阻塞,至于
会阻塞多长时间也是不确定的,如果一直没有数据到达就一直阻塞在那里。
	同样,写常规文件是不会阻塞的,而向终端设备或网络写则不一定。

2.6 seek函数
	#include <sys/types.h>
	#include <unistd.h>
	​
	off_t lseek(int fd, off_t offset, int whence);
	功能:
	    改变文件的偏移量
	参数:
	    fd:文件描述符
	    offset:根据whence来移动的位移数(偏移量),可以是正数,也可以负数,如果正数,则相对于whence往右移动,如果是负数,则相对于whence往左移动。如果向前移动的字节数超过了文件开头则出错返回,如果向后移动的字节数超过了文件末尾,再次写入时将增大文件尺寸。
	​
	    whence:其取值如下:
	        SEEK_SET:从文件开头移动offset个字节
	        SEEK_CUR:从当前位置移动offset个字节
	        SEEK_END:从文件末尾移动offset个字节
	返回值:
	    若lseek成功执行, 则返回新的偏移量
	    如果失败, 返回-1
	 		
	所有打开的文件都有一个当前文件偏移量(current file offset),以下简称为 cfo。
cfo 通常是一个非负整数,用于表明文件开始处到文件当前位置的字节数。	
	读写操作通常开始于 cfo,并且使 cfo 增大,增量为读写的字节数。文件被打开时,
cfo 会被初始化为 0,除非使用了 O_APPEND 。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>

int main()
{
        int fd = -1;
        int ret = -1;

        char buffer[1024] = {0};
        //打开文件
        fd = open("lseek",O_RDWR | O_CREAT,0664);
        if(-1 == fd)
        {
                perror("open:");
                return 1;
        }

        printf("Open:%d\n",fd);

        //lseek操作
        write(fd,"ABCDEFG",7);
        ret = lseek(fd,32,SEEK_SET);

        if(1 == ret)
        {
                perror("Lseek");
                return 1;
        }
        write(fd,"1234567890",10);
        //关闭文件
        close(fd);
        return 0;
}

3.文件操作相关函数

3.1 stat函数
	#include <sys/types.h>
	#include <sys/stat.h>
	#include <unistd.h>
	​
	int stat(const char *path, struct stat *buf);
	int lstat(const char *pathname, struct stat *buf);
	功能:
	    获取文件状态信息
	    stat和lstat的区别:
	        当文件是一个符号链接时,lstat返回的是该符号链接本身的信息;
	        而stat返回的是该链接指向的文件的信息。
	参数:
	    path:文件名
	    buf:保存文件信息的结构体
	返回值:
	    成功: 0
	    失败: -1

	struct stat结构体说明:
	struct stat {
	    dev_t           st_dev;     	//文件的设备编号
	    ino_t           st_ino;     	//节点
	    mode_t          st_mode;        //文件的类型和存取的权限
	    nlink_t         st_nlink;       //连到该文件的硬连接数目,刚建立的文件值为1
	    uid_t           st_uid;     	//用户ID
	    gid_t           st_gid;     	//组ID
	    dev_t           st_rdev;        //(设备类型)若此文件为设备文件,则为其设备编号
	    off_t           st_size;        //文件字节数(文件大小)
	    blksize_t       st_blksize;     //块大小(文件系统的I/O 缓冲区大小)
	    blkcnt_t        st_blocks;      //块数
	    time_t          st_atime;       //最后一次访问时间
	    time_t          st_mtime;       //最后一次修改时间
	    time_t          st_ctime;       //最后一次改变时间(指属性)
	};

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>

int main()
{
        int ret = -1;
        struct stat s;

        //获取指定文件的属性
        ret = stat("txt",&s);
        if(-1 == ret)
        {
                perror("stat");
                return 1;
        }
        printf("st_dev:%lu\n",s.st_dev);
        printf("st_ino:%ld\n",s.st_ino);
        printf("st_nlink:%lu\n",s.st_nlink);
        printf("st_uid:%d\n",s.st_uid);
        printf("st_gid:%d\n",s.st_gid);
        printf("st_size:%ld\n",s.st_size);

        return 0;
}

	2.文件种类判断
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>

//显示文件类型的第一个版本
int show_file_type(struct stat *s)
{
        switch(s->st_mode & S_IFMT)
        {
                //普通文件
                case S_IFREG:
                        printf("普通文件!");
                        break;
                //目录
                case S_IFDIR:
                        printf("目录文件!");
                        break;
                //字符设备
                case S_IFCHR:
                        printf("字符设备!");
                        break;
                //块设备
                case S_IFBLK:
                        printf("块设备");
                        break;
                //套接字
                case S_IFSOCK:
                        printf("套接字文件");
                        break;
                //管道
                case S_IFIFO:
                        printf("管道设备!");
                        break;
                //符号链接
                case S_IFLNK:
                        printf("符号链接");
                        break;
                default:
                        break;
        }
        return 0;
}

int show_file_type1(struct stat *s)
{
        //普通文件
        if(S_ISREG(s->st_mode))
        {
                printf("普通文件\n");
        }
        //目录
        if(S_ISDIR(s->st_mode))
        {
                printf("目录\n");
        }
        //块文件
        if(S_ISBLK(s->st_mode))
        {
                printf("块设备!\n");
        }
        //字符设备
        if(S_ISCHR(s->st_mode))
        {
                printf("字符设备!\n");
        }
        //符号链接
        if(S_ISLNK(s->st_mode))
        {
                printf("符号链接!\n");
        }
        //管道文件
        if(S_ISFIFO(s->st_mode))
        {
                printf("管道连接!\n");
        }
        //套接字
        if(S_ISSOCK(s->st_mode))
        {
                printf("套接字!\n");
        }

        return 0;
}

//获取文件的类型和权限
int main(int argc,char** argv)
{
        int ret = -1;
        struct stat s;

        if(2 != argc)
        {
                printf("usage: ./a.out filename\n");
                return 1;
        }

        ret = stat(argv[1],&s);
        if(-1 == ret)
        {
                perror("stat");
                return 1;
        }
        //显示文件类型
        show_file_type(&s);
        show_file_type1(&s);

        return 0;
}

3.2 access函数
	#include <unistd.h>
	​
	int access(const char *pathname, int mode);
	功能:测试指定文件是否具有某种属性
	参数:
	    pathname:文件名
	    mode:文件权限,4种权限
	        R_OK:   是否有读权限
	        W_OK:   是否有写权限
	        X_OK:   是否有执行权限
	        F_OK:   测试文件是否存在
	返回值:
	    0:  有某种权限,或者文件存在
	    -1:没有,或文件不存在
	access("txt", F_OK);


3.3 chmod函数
	#include <sys/stat.h>
	​
	int chmod(const char *pathname, mode_t mode);
	功能:修改文件权限
	参数:
	    filename:文件名
	    mode:权限(8进制数)
	返回值:
	    成功:0
	    失败:-1


3.4 chown函数
	#include <unistd.h>
	​
	int chown(const char *pathname, uid_t owner, gid_t group);
	功能:修改文件所有者和所属组
	参数:
	    pathname:文件或目录名
	    owner:文件所有者id,通过查看 /etc/passwd 得到所有者id
	    group:文件所属组id,通过查看 /etc/group 得到用户组id
	返回值:
	    成功:0
	    失败:-1
	    
3.5 truncate函数
	#include <unistd.h>
	#include <sys/types.h>
	​
	int truncate(const char *path, off_t length);
	功能:修改文件大小
	参数:
	    path:文件文件名字
	    length:指定的文件大小
	        a)比原来小, 删掉后边的部分
	        b)比原来大, 向后拓展
	返回值:
	    成功:0
	    失败:-1


3.6 link函数
	#include <unistd.h>
	​
	int link(const char *oldpath, const char *newpath);
	功能:创建一个硬链接
	参数:
	    oldpath:源文件名字
	    newpath:硬链接名字
	返回值:
	    成功:0
	    失败:-1

3.7 symlink函数
	#include <unistd.h>
	​
	int symlink(const char *target, const char *linkpath);
	功能:创建一个软链接
	参数:
	    target:源文件名字
	    linkpath:软链接名字
	返回值:
	    成功:0
	    失败:-1
	    
3.8 readlink函数
	#include <unistd.h>
	​
	ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
	功能:读软连接对应的文件名,不是读内容(该函数只能读软链接文件)
	参数:
	    pathname:软连接名
	    buf:存放软件对应的文件名
	    bufsiz :缓冲区大小(第二个参数存放的最大字节数)
	返回值:
	    成功:>0,读到buf中的字符个数
	    失败:-1​
	    
3.9 unlink函数
	#include <unistd.h>
	​
	int unlink(const char *pathname);
	功能:删除一个文件(软硬链接文件)
	参数:
	    pathname:删除的文件名字
	返回值:
	    成功:0
	    失败:-1

3.10 rename函数
	#include <stdio.h>
	​
	int rename(const char *oldpath, const char *newpath);
	功能:把oldpath的文件名改为newpath
	参数:
	oldpath:旧文件名
	newpath:新文件名
	返回值:
	成功:0
	失败:-1

4.文件描述符复制

4.1 dup函数
	#include <unistd.h>
	​
	int dup(int oldfd);
	功能:
	    通过 oldfd 复制出一个新的文件描述符,新的文件描述符是调用进程文件描述符表中最小可用的文件描述符,最终 oldfd 和新的文件描述符都指向同一个文件。
	参数:
	    oldfd : 需要复制的文件描述符 oldfd
	返回值:
	        成功:新文件描述符
	        失败: -1

4.2 dup2函数
	#include <unistd.h>
	​
	int dup2(int oldfd, int newfd);
	功能:
	    通过 oldfd 复制出一个新的文件描述符 newfd,如果成功,newfd 和函数返回值是同一个返回值,最终 oldfd 和新的文件描述符 newfd 都指向同一个文件。
	参数:
	    oldfd : 需要复制的文件描述符
	    newfd : 新的文件描述符,这个描述符可以人为指定一个合法数字(0 - 1023),如果指定的数字已经被占用(和某个文件有关联),此函数会自动关闭 close() 断开这个数字和某个文件的关联,再来使用这个合法数字。
	返回值:
	    成功:返回 newfd
	    失败:返回 -1
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int main()
{

        int fd = -1;
        int newfd = -1;
        //1.打开文件
        fd = open("txt",O_RDWR | O_CREAT,0644);
        if(-1 == fd)
        {
                perror("open");
                return 1;
        }

        //文件描述符的复制
        // newfd = dup(fd);

        newfd = 2;
        //如果文件描述符2已经跟某个文件关联了,那么就先接触与该文件的关联,然后该文件描述符与fd指向同一个文件

         if(-1 == newfd)
         {
                 perror("dup");
                 return 1;
         }

        printf("newfd = %d\n",newfd);

        //2.操作
        write(fd,"ABCDEFG",7);
        write(newfd,"1234567",7);


        //3.关闭文件描述符
        close(fd);
        close(newfd);

        return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值