服务端
- 多线程或多进程并发
- 引入数据库或者使用当前文件系统
解析文件系统存储 - 流量控制
- 网络套接字socket
- 应为守护进程,脱离终端,IO重定向,系统日志
客户端
- 父进程,从网络上接收数据,传递给子进程
- 子进程,接收数据并播放
- 父、子进程实现及其关系
- 进程通信
参考资料 《UNIX环境高级编程(第3版)》 《UNIX网络编程》 《TCP/IP详解(卷一)》 《深入理解计算机系统》
学习方法介绍
IO
第3、5章节
文件系统
第4、6、7章节
并发
第8、10、11章节
多进程并发(信号)第10章
多线程并发第10、11章节
IPC 进程间通信
进程基础(涉及多进程)第8章
守护进程 第13章
进程间通信 第15、16章节
注意事项:
- 弃用root用户
- 对代码进行
重构 - 课堂重点:项目,课堂代码,面试题,实验性题目,推荐书籍的课后习题
I/O,input & output,是一切实现的基础。
stdio 标准IO
sysio 系统调用IO(文件IO)
优先考虑标准I/O
标准IO
stdio: man 3
fopen(); 文件打开
fclose(); 文件关闭
fgetc(); 读取字符
fputc(); 写字符
fgets(); 读取字符串
fputs(); 写字符串
fread(); 读二进制
fwrite(); 写二进制
printf()族
scanf()族
fseek();
ftell();
rewind();
fflush();
标准IO相关结构体FILE
man fopen
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
参数:
pathname,文件路径名
mode,打开模式
r,只读打开,文件指针在beginning of file (不会创建)
r+,读写打开,文件指针在文件开始处 (不会创建)
w,只写形式打开,有则清空,无则创建,文件指针在文件开始处(文件的第一个有效字节)
w+,读写形式打开,有则清空,无则创建,文件指针在文件开始处(文件的第一个有效字节)
a,追加写(只有追加写,没有追加读),文件最后一个有效字节的下一个位置(end of file)
a+,以读写(追加写)形式打开文件,无则创建,
读:beginning of file,即 文件的第一个有效字节
追加写:end of file,即文件最后一个有效字节的下一个位
注意:区分文件最后一个有效字节与最后一个有效字节的下一个位置
b,二进制,Windows区分字符与二进制
注意,r和r+ 要求文件必须存在,否则结束当前调用,返回错误
返回值:
成功:FILE指针
失败:NULL,errno(errno是全局变量?)
errno定义的位置:
cat /usr/include/asm-generic/errno.h
cat /usr/include/asm-generic/errno-base.h
面试题
已知条件:
char *ptr="abc"; //字符串常量
ptr[0]='x';
问:是否会得到"xbc"?
答:取决于当前平台使用的编译器会把"abc"放到哪里存储?
编程tips
int *p = malloc(sizeof(int));
malloc的返回值是void *
如果不添加头文件#include<stdlib.h>,
gcc会认为所有函数的返回值是int(有例外),将int型赋值给int *必然会报错
补充函数perror与strerror
man perror
perror - print a system error message
man strerror
#include <string.h>
char *strerror(int errnum); //return string describing error number
打开不同的注释,观察执行结果
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
int
main (){
FILE *fp;
fp= fopen("tmp","r");
if(fp==NULL){
perror("fopen()");
exit(1);
}
puts("fopen OK!");
exit(0);
}
思考:
FILE *fopen(const char *pathname, const char *mode);
调用fopen返回的FIEL结构体保存在哪里?
1. 栈?
2. 静态区?
3. 堆?
FILE *fopen(const char *pathname, const char *mode);
{
FILE tmp;
tmp.xx=uu;
...
return tmp;
----------------------------------------
static FILE tmp;
tmp.xx=vv;
...
return tmp;
----------------------------------------
FILE *tmp = NULL;
tmp = malloc (sizeof FILE);
tmp->xx=ww;
...
return &tmp;
}
fopen fclose
函数fclose
man fclose
fclose - close a stream 关闭流
#include <stdio.h> //头文件
int fclose(FILE *stream); //函数原型
- 谁打开谁关闭
- 谁申请谁释放
- 是资源就有上限
在不更改当前默认环境的情况下,一个进程默认打开3个stream流: 可通过ulimit -a 查看,通过ulimit -n xxx 更改对应的值
- stdin
- stdout
- stderr
user@ubuntu:~/codes$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 15374
max locked memory (kbytes, -l) 65536
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 15374
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
文件权限(以umask=0002为例)
0666 & ~umask
110 110 110 ---0666
000 000 010 ---0002
0666 与umask取反相与
111 111 101 ---~umask
110 110 110 ---0666
110 110 100 ---0666 & ~umask 相与的结果
即664 rw-rw-r--
lry@ubuntu:~/codes$ ll tmp
-rw-rw-r-- 1 lry lry 0 9月 30 23:16 tmp
2021/10/1
fgetc、fputc
- 凡是返回值是指针,要思考指向的内容存储在哪个地方。
- 宏只占用编译时间,不占用调用时间,函数则恰恰相反
#include <stdio.h>
int fgetc(FILE *stream); // input of characters and strings
int fputc(int c, FILE *stream); // output of characters and strings
cp命令demo
建议先关闭依赖其他文件的文件
#include <stdlib.h>
#include <stdio.h>
int main(int argc,char **argv){
int ch;
FILE *src_file=NULL;
FILE *dst_file=NULL;
//too few arguments
if(argc<3){
fprintf(stderr,"Usage: %s <src_file> <dst_file> \n",argv[0]);
exit(1);
}
//open source file
src_file=fopen(argv[1],"r");
if(src_file==NULL){
perror("error to open src file");
exit(1);
}
//open destination file
dst_file=fopen(argv[2],"w");
if(dst_file==NULL){
perror("error to open dst file");
fclose(src_file); //avoid out of memory
exit(1);
}
//loop for copy
while(1){
ch=fgetc(src_file);
printf("%3d----%c\n",ch,ch+'\0');//for debug
if(ch==EOF)
break;
fputc(ch,dst_file);
}
//close files opened
fclose(dst_file);
fclose(src_file);
exit(0);
}
统计文件有效字符个数,即文件大小size
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char **argv){
int counter=0;
FILE * fp=NULL;
if(argc<2){
fprintf(stderr,"Usage: %s <file>\n",argv[0]);
exit(1);
}
fp=fopen(argv[1],"r");
if(fp==NULL){
perror("fopen error!");
exit(1);
}
while(fgetc(fp)!=EOF){
counter++;
}
printf("count=%d\n",counter);
fclose(fp);
exit(1);
}
函数fgets、 fputs
函数原型
man fgets
char *fgets(char *s, int size, FILE *stream); //从stream中读取size-1个有效字节,放到s指向的地方
man fputs
int fputs(const char *s, FILE *stream);
int puts(const char *s);
fgets正常结束的两种情况
- 读取了
size-1 个有效字节,剩下一个字节是留给补全为0的 - 读取到
'\n' 即使是最后一行,行尾也会有换行\n
例如文件的最后一行为:
ab
实际读取的内容
'a' 'b' '\n' '\0'
请看下图: 基于fgets fputs实现的cp
#include <stdlib.h>
#include <stdio.h>
#define BUFSIZE 1024
int main(int argc,char **argv){
char buf[BUFSIZE];
FILE *src_file=NULL;
FILE *dst_file=NULL;
//too few arguments
if(argc<3){
fprintf(stderr,"Usage: %s <src_file> <dst_file>",argv[0]);
exit(1);
}
//open source file
src_file=fopen(argv[1],"r");
if(src_file==NULL){
perror("error to open src file");
exit(1);
}
//open destination file
dst_file=fopen(argv[2],"w");
if(dst_file==NULL){
perror("error to open dst file");
fclose(src_file); //avoid out of memory
exit(1);
}
//loop for copy
while(fgets(buf,BUFSIZE,src_file)!=NULL)
fputs(buf,dst_file);
//close files opened
fclose(dst_file);
fclose(src_file);
exit(0);
}
标题函数fread fwrite
NAME
fread, fwrite - binary stream input/output
SYNOPSIS
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
RETURN VALUE
On success, fread() and fwrite() return the number of items read or written
编程tips:推荐使用1字节读取
fread (buf, size, nmemb, fp);
1. 数量足够
fread( buf, 1, 10 ,fp ); //读10个1字节,返回值10
fread( buf, 10, 1, fp );//读1个10字节,返回值10
2. 只有5个字节
fread( buf, 1, 10 ,fp ); //读5个1字节, 返回值5
fread( buf, 10, 1, fp );//读不到1个10字节,不知道读了多少字节(1-9),返回值0
基于fread fwrite实现的cp
#include <stdlib.h>
#include <stdio.h>
#define BUFSIZE 1024
int main(int argc,char **argv){
int n=0;
char buf[BUFSIZE];
FILE *src_file=NULL;
FILE *dst_file=NULL;
//too few arguments
if(argc<3){
fprintf(stderr,"Usage: %s <src_file> <dst_file>",argv[0]);
exit(1);
}
//open source file
src_file=fopen(argv[1],"r");
if(src_file==NULL){
perror("error to open src file");
exit(1);
}
//open destination file
dst_file=fopen(argv[2],"w");
if(dst_file==NULL){
perror("error to open dst file");
fclose(src_file); //avoid out of memory
exit(1);
}
//loop for copy
while((n=fread(buf,1,BUFSIZE,src_file))>0)
fwrite(buf,1,n,dst_file);
//close files opened
fclose(dst_file);
fclose(src_file);
exit(0);
}
函数fprintf族
man fprintf
#include <stdio.h>
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int dprintf(int fd, const char *format, ...);
int sprintf(char *str, const char *format, ...); //atoi的反向功能
int snprintf(char *str, size_t size, const char *format, ...);
man atoi
NAME
atoi, atol, atoll - convert a string to an integer
SYNOPSIS
#include <stdlib.h>
int atoi(const char *nptr);
long atol(const char *nptr);
long long atoll(const char *nptr);
示例
#include <stdio.h>
#include <stdlib.h>
int main(){
char buf[1024];
int year=2021,month=5,day=13;
sprintf(buf,"%d-%d-%d",year,month,day);
puts(buf);
//char str[]="123a456";
//printf("%d\n",atoi(str));//atoi() ends with 'a'
//
exit(1);
}
函数sprintf族
#include <stdio.h>
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
#include <stdarg.h>
int vscanf(const char *format, va_list ap);
int vsscanf(const char *str, const char *format, va_list ap);
int vfscanf(FILE *stream, const char *format, va_list ap);
函数fseek ftell
man fseek
NAME
fgetpos, fseek, fsetpos, ftell, rewind - reposition a stream
SYNOPSIS
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence); //offset -2G~2G
long ftell(FILE *stream); //返回值0-2G
void rewind(FILE *stream);
函数fseeko ftello
NAME
fseeko, ftello - seek to or report file position
SYNOPSIS
#include <stdio.h>
int fseeko(FILE *stream, off_t offset, int whence);
off_t ftello(FILE *stream);
文件指针操作
man fseek
NAME
fgetpos, fseek, fsetpos, ftell, rewind - reposition a stream 重新定位流
SYNOPSIS
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
stream,
offset,偏移量
whence,相对位置,SEEK_SET, SEEK_CUR , SEEK_END
long ftell(FILE *stream); //返回当前文件指针所在的位置
void rewind(FILE *stream); //相当于(void) fseek(stream, 0L, SEEK_SET)
标题计算文件大小
fp=fopen(argv[1] , "r" );
fseek(fp , 0,SEEK_END);
printf("%ld \n", ftell(fp));
空洞文件 '\0' 或者asci码为0
缓冲区的作用
- 合并系统调用
- 行缓冲,①换行时刷新②满的时候刷新③强制刷新(fflush、标准输出)
- 全缓冲,①满的时候刷新②强制刷新(默认,只要不是终端设备)
- 无缓冲,需要立即输出的内容,如stderr
- setvbuf,更爱缓冲模式
The setvbuf() function may be used on any open stream to change its buffer. The mode argument must be one
of the following three macros:
_IONBF unbuffered
_IOLBF line buffered
_IOFBF fully buffered
读取完整的一行getline
NAME
getline, getdelim - delimited string input
SYNOPSIS
#include <stdio.h>
ssize_t getline(char **lineptr, size_t *n, FILE *stream); //reads an entire line from stream, 包括'\n' 但不包括'\0'
ssize_t getdelim(char **lineptr, size_t *n, int delim, FILE *stream);
getline实例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char **argv){
FILE *fp;
char *linebuf;
size_t linesize;
if(argc<2){
fprintf(stderr,"Usage:...");
exit(1);
}
fp=fopen(argv[1],"r");
if(fp ==NULL){
perror("open error");
exit(1);
}
//!!!!非常重要!!!!
linebuf =NULL; //不释放可能会造成内存泄漏
linesize =0;
while(1){
if (getline(&linebuf,&linesize,fp)<0)
break;
printf("%ld\n",strlen(linebuf));
printf("%ld\n",linesize);
}
fclose(fp);
//free(linebuf); 不推荐使用free释放内存
exit(0);
}
临时文件
- 如何不冲突
- 及时销毁
- 函数tmpnam、tmpfile
man tmpnam
#include <stdio.h>
char *tmpnam(char *s);
man tmpfile
#include <stdio.h>
FILE *tmpfile(void); //create a temporary file 创建临时文件(匿名文件)
系统调用IO/文件IO
文件描述符(fd)是贯穿文件IO始终的类型。 什么是文件描述符? 文件描述符的本质是整数,索引下标,优先使用可用范围内的最小值。
怎么使用文件描述符? 文件IO操作:open、close、read、write、lseek
文件IO VS 标准IO
IO的效率问题
文件共享
原子操作
程序中的重定向: fup、dup2
同步:sync,fsync,fdatasync
fcntl() ioctl() /dev/fd/
|