化妆品手机端网站模板,关键词如何快速排名,海珠区居家办公,河南省罗山县做网站的公司1.系统调用
由操作系统实现并提供给外部应用程序的编程接口。(Application Programming Interface#xff0c;API)。是应用程序同系统之间数据交互的桥梁。
C标准函数和系统函数调用关系。一个helloworld如何打印到屏幕。 man手册中一共有九卷#xff0c;其中一卷就有讲到系…1.系统调用
由操作系统实现并提供给外部应用程序的编程接口。(Application Programming InterfaceAPI)。是应用程序同系统之间数据交互的桥梁。
C标准函数和系统函数调用关系。一个helloworld如何打印到屏幕。 man手册中一共有九卷其中一卷就有讲到系统调用内核就当前操作系统的核心程序系统的本质都是个程序。内核和硬件打交道提供的函数只能给上层应用所使用。
提供的系统调用函数实际上在linux内核当中是没有的只不过却有与之对应的一样功能的函数比如open在内核当中的源码对应的是sys_open虽然名字不同但是几乎是一模一样的
sys_open 浅封装 open操作系统避免与用户进行交互但又不想让用户真正窥探到内核因此使用了浅封装给内核中的sys_open包了个保护壳变成open函数让用户可以去调用系统而又不会导致让用户窥探到本质 2.open/close
函数原型
要导入头文件 #includeunistd.h
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode)
int close(int fd);
int creat(const char *pathname, mode_t mode);# 可以通过 man open 2 指令去查看配置的文档# pathname文件路径# flags打开方式# mode: 权限mode_t是一个八进制的整型# 返回的是一个文件描述符如果执行错误会返回-1# 同时返回一个errno操作系统的全局变量需要导入 errno.h# 返回的errno的数字想要知道是什么含义可以通过 man strerror 去查看
常用参数 -- flags
要导入头文件#includefcntl.h O_RDONLY 读O_WRONLY写O_RDWR读写O_APPEND追加O_CREAT创建O_EXCL存放O_TRUNC截断O_NONBLOCK非阻塞
创建文件时指定文件访问权限。权限同时受umask影响。结论为
文件权限 mode ~umask
使用头文件fcntl.h O_CREAT -- 如果没有该文件就进行创建 创建文件时其权限与umask挂钩比如umask 0002取反后就是775然后与mode进行按位与二进制最后才得出创建文件的真正权限 O_TRUNC --- int ftruncate(int fd, off_t length); -- 把文件截断成0 open常见错误
1. 打开文件不存在
2. 以写方式打开只读文件(打开文件没有对应权限)
3. 以只写方式打开目录 perror、strerror、errno 运用
[....]# cd /linux_01
[....]# mkdir -a ./file_IO_test/test
[....]# cd ./file_IO_test/test
[....]# touch dict.txt
[....]# touch ./makefile
[....]# mkdir ./dirct
[....]# vim open.c#includeunistd.h#includestdlib.h#includefcntl.h#includestring.hint main(int argc,char *agrv[]){int fd 0;fd open(./dict.txt,O_RDONLY);printf(fd %d\n,fd);int fd1 0;fd1 open(./dict.cp,O_RDONLY | O_CREAT,0644);printf(fd1 %d\n,fd);//1.打开文件不存在int fd2 0;fd2 open(./dict.cp1,O_RDONLY); printf(fd2 %d, error %d:%s\n,fd2,error,strerror(error)));//2.以写方式打开只读文件(打开文件没有对应权限)int fd3 0; open(./dict.cp2,O_RDONLY | O_CREAT,0411); //创建一个只读文件fd3 open(./dict.cp2,O_WRONLY);printf(fd3 %d, error %d:%s\n,fd3,error,strerror(error)));//fd -1errno13:Permissiondenied//3.以只写方式打开目录int fd4 0;fd4 open(./dict.cp2,O_WRONLY);printf(fd4 %d, error %d:%s\n,fd4,error,strerror(error)));// fd -1, errno21:Is a directory close(fd);......return 0;} 3.read/write函数
ssize_t read(int fd, void *buf, size_t count); 从指定位置fd读然后将读取的东西存入缓冲区buf待写出数据的缓冲区count是数据的大小成功的话会返回读取到的字节数读取到文件尾部会返回0失败的话会返回-1同时设置errno
ssize_t write(int fd, const void *buf, size_t count); 练习编写程序实现简单的cp功能。
[....]# vim cptest.c#includeStdio.h#includestdlib.h //perror所用到的头文件....#includefcntl.hint main(int agrc,int **argv){char buf[1024];int n 0;int fd1 open(argv[1],O_RDONLY); //readif(fd1 -1){perror(open argv1 error\n);// 如果读取出错就打印出自定义提示 exit(1); }int fd2 open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0644);# fd2是创建打开一个文件进行进行写入操作如果该文件中有我内容则清0# 如果没有该文件就创建该文件目的是将fd1的内容写入到fd2if(fd2 -1){perror(open argv2 error\n);// 如果读取出错就打印出自定义提示 exit(1); }//写入操作:while((n read(fd1,buf,1024))!0){if(n 0){perror(read error);break; }write(fd2,buf,n);// 第三个参数为n是为了防止缓冲区资源的浪费 }close(fd1);close(fd2);return 0;}
gcc cptest.c -o cptest
./cptest.c open.c open2.c
# open.c是要拷贝过去的内容open2.c是拷贝目的地
比较如果一个只读一个字节实现文件拷贝使用read、write效率高还是使用对应的标库函数(fgetc、fputc)效率高呢 理想上是write比fputc效率更高 不管是fputc还是write最终目的都是要从用户空间进入到系统内核然后系统内核去驱动磁盘进行写入写出工作但是只有系统函数才能才去调用系统内核让驱动工作因此write效率更高因为write本身就是系统调用提供的函数。而fputc并不是系统调用提供的函数它底层是先去调用write然后从用户空间进入到内核空间借助驱动去驱动磁盘工作 但真是这样吗 但实际上却是fputc比wirte效率更快 strace ./可执行文件名称 可以看函数的调用过程 用read和writeread一次读一个write一次写一个一次只读取一个字节 用fgetc和fputc并不是一个字节一个字节的操作而是4096个字节的进行一次操作 探究 fputc有自己的缓冲区当缓冲区的东西满了后才会去调用write进入到内核将数据放入到系统级缓冲区调用驱动进入到磁盘 而write和read没有自己的缓冲区又因为人为的将buf缓冲区设置为1因此只能不断地来回反复操作只读取或写入一个字节 ----因此可以将buf缓冲区的大小重新设置好就能实现wirte和read效率更高 read、write函数常常被称为Unbuffered I/O。指的是无用户及缓冲区。但不保证不使用内核缓冲区。-----预读入和缓输出 4.文件描述符
PCB进程控制块 使用命令查看其位置
[....]# locate sched.h
/usr/src/linux-headers-3.16.0-30/include/linux/sched.hPCB进程控制块就像一个结构体
struct task_struct { 结构体 } 文件描述符
结构体PCB 的成员变量file_struct *file 指向文件描述符表。
从应用程序使用角度该指针可理解记忆成一个字符指针数组下标0/1/2/3/4...找到文件结构体。
本质是一个键值对0、1、2...都分别对应具体地址。但键值对使用的特性是自动映射我们只操作键不直接使用值。
value中就是个指针指向了一个文件结构体这个文件结构体中记录了进行操作文件的内容比如属组、属主、路径等但操作系统不想暴露这些value因此只返回了key的值 -- 文件描述符
新打开文件返回文件描述符表中未使用的最小文件描述符。
STDIN_FILENO 0STDOUT_FILENO 1STDERR_FILENO 2
最大打开文件数 一个进程默认打开文件的个数1024。 命令ulimit -a 查看open files 对应值。默认为1024可以使用ulimit -n 4096 修改当然也可以通过修改系统配置文件永久修改该值但是不建议这样操作。 cat /proc/sys/fs/file-max 可以查看该电脑最大可以打开的文件个数。受内存大小影响 FILE结构体
主要包含文件描述符、文件读写位置、IO缓冲区三部分内容。
struct file {...文件的偏移量文件的访问权限文件的打开标志文件内核缓冲区的首地址struct operations * f_op;...}; 查看方法 (1) /usr/src/linux-headers-3.16.0-30/include/linux/fs.h (2) lxr百度 lxr → lxr.oss.org.cn → 选择内核版本(如3.10) → 点击File Search进行搜索 → 关键字“include/linux/fs.h” → CtrlF 查找 “struct file {” → 得到文件内核中结构体定义 → “struct file_operations”文件内容操作函数指针 → “struct inode_operations”文件属性操作函数指针 5.阻塞和非阻塞
读常规文件是不会阻塞的不管读多少字节read一定会在有限的时间内返回。从终端设备或网络读则不一定如果从终端输入的数据没有换行符调用read读终端设备就会阻塞如果网络上没有接收到数据包调用read从网络读就会阻塞至于会阻塞多长时间也是不确定的如果一直没有数据到达就一直阻塞在那里。同样写常规文件是不会阻塞的而向终端设备或网络写则不一定。
现在明确一下阻塞Block这个概念。当进程调用一个阻塞的系统函数时该进程被置于睡眠Sleep状态这时内核调度其它进程运行直到该进程等待的事件发生了比如网络上接收到数据包或者调用sleep指定的睡眠时间到了它才有可能继续运行。与睡眠状态相对的是运行Running状态在Linux内核中处于运行状态的进程分为两种情况
正在被调度执行。CPU处于该进程的上下文环境中程序计数器eip里保存着该进程的指令地址通用寄存器里保存着该进程运算过程的中间结果正在执行该进程的指令正在读写该进程的地址空间。
就绪状态。该进程不需要等待什么事件发生随时都可以执行但CPU暂时还在执行另一个进程所以该进程在一个就绪队列中等待被内核调度。系统中可能同时有多个就绪的进程那么该调度谁执行呢内核的调度算法是基于优先级和时间片的而且会根据每个进程的运行情况动态调整它的优先级和时间片让每个进程都能比较公平地得到机会执行同时要兼顾用户体验不能让和用户交互的进程响应太慢。
阻塞读终端【block_readtty.c】非阻塞读终端【nonblock_readtty.c】非阻塞读终端和等待超时【nonblock_timeout.c】
注意阻塞与非阻塞是对于文件而言的。而不是read、write等的属性。read终端默认阻塞读。 总结read 函数返回值
返回非零值 实际read到的字节数返回-1 errno ! EAGAIN (或! EWOULDBLOCK) read出错errno EAGAIN (或 EWOULDBLOCK) 设置了非阻塞读并且没有数据到达。read在以非阻塞方式读一个设备文件网络文件并且文件无数据返回0读到文件末尾 例子 /dev/tty -- 终端文件STDIN_FILENO和STDOUT_FILENO都是终端文件中的内容实际上终端文件是已经跟着打开的了
标准输入和输出当运行该文件时如果不按键盘输入东西和点击回车这时候终端就在等待称为阻塞
阻塞是设备文件、网络文件的属性不要误以为阻塞是read和write的特性并且读常规文件无阻塞属性
那么能不能用非阻塞的方式去操作终端文件 用open中的O_NONBLOCK非阻塞的方式打开tty终端文件这时候只会反复的读读到了就输出到屏幕读不到就继续读终端并不会进入等待状态阻塞 6.fcntl函数
【fcntl.c】 想改变文件的访问控制属性比如从阻塞状态变成非阻塞状态那就得关闭文件重新打开文件进行操作而使用 fcntl 函数可以直接改变
改变一个【已经打开】的文件的访问控制属性重点掌握这两个参数的使用F_GETEL 和 F_SETFL F_GETFL 读取到标准输入终端文件的状态后通过 | 添加上非阻塞属性然后用F_SETFL 将其重新设置使标准输入变为非阻塞 7.lseek函数
理解
官方定义重新定位读或写的文件偏移量。
Linux中可使用系统函数lseek来修改文件偏移量(读写位置)
每当打开一个文件都会有一个叫做“当前文件偏移量”的东西如果难理解也可以将他理解为指针。 除非打开文件时指定O_APPEND选项否则文件偏移量默认设置为0。当我们发生了一次读或者写操作时都会使这个当前文件偏移量发生变化读/写多少字节当前偏移量就会向后移动多少。因此当我们对一个新文件进行完写操作后进行读操作会发现什么都读不到是因为偏移量经过写操作后移到了文件尾部此时进行读操作肯定什么都读不到了也就是读和写操作用的是同一个偏移量文件指针
lseek和标准I/O库的fseek函数类似可以移动当前读写位置或者叫偏移量。
回忆fseek的作用及常用参数
SEEK_SET、SEEK_CUR、SEEK_END
int fseek(FILE *stream, long offset, int whence);
成功返回0失败返回-1
特别的超出文件末尾位置返回0往回超出文件头位置返回-1includelseek.c
off_t lseek(int fd, off_t offset, int whence); 参数fd //文件描述符可以通过open函数得到通过这个fd可以操作某个文件参数: offset //文件偏移量是一个整形数与whence对应的位置继续往后偏移参数whence //偏移类型下列三个值中选一个。SEEK_SET:该文件的偏移量设为离文件开头offset个字节.SEEK_CUR:该文件的偏移量设为其当前值加offset(PS :offest可正负).SEEK_END:该文件的偏移量设为文件长度加offset
特别的lseek允许超过文件结尾设置偏移量文件会因此被拓展。注意文件“读”和“写”使用同一偏移位置。
lseek函数返回的偏移量(off_t)总是相对于文件头而言的。 作用
移动文件指针到文件头lseek(fd, 0, SEEK_SET)获取当前文件指针的位置lseek(fd, 0, SEEK_SUR)获取文件长度lseek(fd, 0, SEEK_END)拓展文件的长度当前文件10b、110b增加了100个字节lseed(fd, 100, SEEK_END)需要注意拓展完需要再写一次数据否侧拓展无效
还有另一种方式也可以来拓展文件的大小
[....]# main 2truncate # 可以查找到另一个函数truncate
int truncate(const char* path, off_t length)
例子
1.移动文件指针到文件头和获取当前文件指针的位置
//导入所有需要的头文件
#includestdio.h
#includestdlib.h
#includeunistd.h
#includestring.h
#includefcntl.h//我们的目的是移动文件指针到文件头
int main()
{//获取文件的文件描述符int fd open(text.txt, O_RDWR);if (fd -1){perror(open);return -1;}//输出当前文件的偏移量long long int loc lseek(fd, 0, SEEK_CUR);printf(%lld\n, loc); //0//使用read函数读3个字节的数据char buf[3] {0};int rnum read(fd, buf, sizeof(buf));printf(%d\n, rnum); //3//再次查看文件的偏移量获取当前文件指针的位置long long int loc1 lseek(fd, 0, SEEK_CUR);printf(%lld\n, loc1); //3//移动文件指针到文件头long long int loc2 lseek(fd, 0, SEEK_SET);printf(%lld\n, loc2); //0return 0;
} 2.获取文件长度
#includestdio.h
#includestdlib.h
#includeunistd.h
#includestring.h
#includefcntl.h
int main()
{//获取文件的文件描述符int fd open(hello.txt, O_RDWR);//获取文件长度long long int loc1 lseek(fd, 0, SEEK_END);printf(%lld\n, loc1);return 0;
}3.拓展文件的长度注拓展完成后需要再写一次数据否则拓展无效
int main()
{//获取文件的文件描述符int fd open(hello.txt, O_RDWR);//获取文件长度long long int loc1 lseek(fd, 0, SEEK_END);printf(%lld\n, loc1);//拓展文件的长度需要引起IO操作后文件的大小才会改变//可以通过 ll 指令查看文件的详细属性long long int loc2 lseek(fd, 100, SEEK_END);write(fd, ,1);//写入一个空数据//该空数据前的100个字节是文件空洞这是系统自动帮我们填补的^printf(%lld\n, loc2);return 0;
} od -tcx filename 查看文件的16进制表示形式
od -tcd filename 查看文件的10进制表示形式 8.传入传出参数
传入参数: 1.指针作为函数参数 2.通常有const关键字修饰 3.指针指向有效区域, 在函数内部做读操作 char *strcpy(cnost char *src, char *dst); 传出参数 1.指针作为函数参数 2.在函数调用之前指针指向的空间可以无意义但必须有效如不能指向未初始化的空间 3.在函数内部做写操作 4.函数调用结束后充当函数返回值 传入传出参数 1.指针作为函数参数 2.在函数调用之前指针指向的空间有实际意义 3.在函数内部先做读操作后做写操作 4.函数调用结束后充当函数返回值 char strtok(char str, const char delim, char ** saveptr) 其中第三个参数就是传入传出参数