Linux系统编程阶段的学习内容:
- 文件I/O
- 文件系统
- 进程
- 进程间通信
- 信号!
- 进程间关系,守护进程
- 线程
- 线程同步!
- 网络基础
- socket编程
- 高并发服务器!
- shell编程,正则表达式
参考书:
《Unix环境高级编程》W. Richard Stevens(!)
《TCP/IP详解》(3卷)(!)
《UNIX网络编程》(2卷)(!)
《Linux系统编程》Robert Love
《Linux/UNIX系统编程手册》Michael Kerrisk
《Unix内核源码剖析》青柳隆宏
《Linux内核源代码情景分析》
《Linux内核设计与实现》
《深入理解Linux内核》
权威资源:
man pages
开源中国论坛
O’REILLY出版社书籍
- write函数并不是真正的系统调用,而是对系统调用sys_write的浅封装,即write函数内部的实现细节和sys_write内部一样,只是改了名字而已。这样做是为了对用户程序隐藏系统调用的实现细节。
open
函数:
- 在open函数中通过宏
O_CREAT
创建新文件时,新文件的权限和第三个参数mode以及系统的umask值相关,权限为:mode & ~umask
。 - 文件读写权限:
O_RDONLY\O_WRONLY\O_RDWR
三选一 - 参数2的其他重要宏:
O_TRUNC\O_NONBLOCK\O_EXCL\O_APPED
open
函数常见错误:(遇到不同错误时open函数返回的文件描述符值也不同)
1)打开文件不存在
2) 以写方式打开只读文件(打开文件没有对应权限)
3)以只写方式打开目录
- 文件描述符:
- 进程控制块(PCB)
- 文件描述符表
结构体PCB 的成员变量file_struct *file
指向文件描述符表。从应用程序使用角度,该指针可理解记忆成一个字符指针数组,下标0/1/2/3/4…找到文件结构体。本质是一个键值对,0、1、2…都分别对应具体地址。但键值对使用的特性是自动映射,我们只操作键不直接使用值。新打开文件返回文件描述符表中未使用的最小文件描述符。
-
预读入缓输出机制:
read、write函数被称为Unbuffered I/O,指的是无用户级缓冲,但不保证不使用内核缓冲区。比如,同样是实现文件拷贝命令cp的实现,若用系统调用read、wirte每次拷贝一个字节来实现比用C标准库函数fgetc、fputc来实现的运行时间要慢得多,原因就是read和write没有(用户级)缓冲区,而用fgetc、fputc来实现虽然每次只能拷贝一个字节但它们有缓冲区,这样就大大减少了用户区向内核区的切换。这种机制称为预读入缓输出。用户级缓冲区和系统缓冲区默认大小都是4096字节。 -
阻塞和非阻塞:
总结read
函数返回值:-
返回非零值: 实际read到的字节数
-
返回 -1:
- errno != EAGAIN (或!= EWOULDBLOCK) read出错;
- errno == EAGAIN (或== EWOULDBLOCK) 设置了非阻塞读,并且没有数据到达。
-
返回0:读到文件末尾
-
-
lseek
函数:
常用应用:-
使用lseek拓展文件:write操作才能实质性的拓展文件。单lseek是不能进行拓展的。
一般:write(fd, “a”, 1);
od -tcx filename 查看文件的16进制表示形式
od -tcd filename 查看文件的10进制表示形式 -
通过lseek获取文件的大小:lseek(fd, 0, SEEK_END);
-
【最后注意】:lseek函数返回的偏移量总是相对于文件头而言。
-
-
fcntl
函数
改变一个【已经打开】的文件的 访问控制属性。
重点掌握两个参数的使用,F_GETFL
和F_SETFL
。
例如:修改打开的文件fd的读写属性为非阻塞
flags = fcntl(fd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
//或可简写为:
fcntl(fd, F_SETFL, O_NONBLOCK);
-
位图(bitmap):linux中采用位图描述文件属性。
fcntl
函数的返回值是一个int值,表示一个32位的bitmap,其中的每一位描述该文件的一种文件属性,当要设置文件的某一属性时,采用位或操作。比如:假设flags
是fcntl
函数操作某一文件的返回值,flags |= O_NONBLOCK
。 -
传入传出参数(value-result)
-
传入参数: const 关键字修饰的 指针变量 在函数内部读操作。 比如,
char *strcpy(cnost char *src, char *dst);
中的第一个参数。 -
传出参数:
- 指针做为函数参数
- 函数调用前,指针指向的空间可以无意义,调用后指针指向的空间有意义,且作为函数的返回值传出
- 在函数内部写操作。
比如,char *strcpy(cnost char *src, char *dst);
中的第二个参数。
-
传入传出参数:
- 调用前指向的空间有实际意义
- 调用期间在函数内读、写(改变原值)操作
- 作为函数返回值传出。
- 标准I/O提供了三种类型的缓冲:全缓冲、行缓冲、无缓冲,目的是尽量减少使用read和write的次数。
(1)无缓冲,比如标准错误stderr
,任何传输到它的出错信息都会立刻打印出来;
(2)行缓冲,比如printf
函数,在换行之前会一直把打印的内容存到标准输出缓冲区中,直至换行或者调用fflush
函数刷新标准输出stdout
;
(3)全缓冲,把缓冲区填满后才输出。