系统编程概念

库函数:C语言标准库中ANSI C、ISO C、GNU C、POSIX

ANSI C标准同时出现的就是ISO组织,将ANSI C加入了ISO的大家庭,定义了ISO C。除了在格式和排版等方面存在一些差别外,其他都与ANSI C相同。即ANSI C与ISO C 对于我们开发者来说完全相同

GNU 是为了实现自由开源目的一个基金会,它提供了很多基于POSIX标准的软件和库,比如glibc、gcc、emacs等等。
GNU C又叫做glibc,是Linux上的一个基础库,glibc C实现了POSIX C标准的库函数功能,有些POSIX标准是单独的库函数存在的。glibc是linux上最常用的实现。

POSIX标准的诞生是为了统一个操作系统的接口,方便开发者开发程序,写出可移植的代码程序。基于POSIX标准的库函数都是可以在持之此标准的操作系统平台上移植。

系统调用流程

C语言标准库有诸多库函数组成
库函数fopen利用系统调用open来执行打开文件的实际操作,设计库函数是为了提供比底层系统调用更为方便的调用接口。
例如:printf函数可提供格式化输出和数据缓存功能,而write()系统调用只能输出字节块。同理,malloc和free与底层的brk系统调动相比,对内存的释放和分配页容易许多

系统调用和库函数的错误

每个系统调用通过手册页都有调用返回的可能值,并指出那些值表示错误。

man手册中标号:(查看库函数的参数使用man 3)
1.一般命令 2.系统调用 3.库函数,涵盖C标准函数库 4.特殊文件(通常是/dev中的设备)和驱动程序 5.文件格式和约定 6.游戏和屏保 7.杂项 8.系统管理命令和守护进程。

打印错误值使用perror和strerror。通常系统调用错误时,返回值表示为-1表示出错,并且设置全局整形变量errno设置为一个正值。

少数系统调用如getpriority()调用成功后,也会返回-1.判断此类是否成功,通常在调用前将errno设置为0,调用函数后再检查errno。

#include <stdio.h>
void perror(const char *msg)

使用它的简单的例子:函数strerror会针对errnum参数中给定的错误号,返回相应的错误字符串。

fd = open(pathname. flags, mode);
if(fd == -1)
{perror("open");//char* str = strerror(errno);//printf("%s", str);exit(EXIT_FAILURE);
}

一、POSIX文件I/O编程

1.1 文件描述符

POSIX文件操作同样也是以文件描述符来标识一个文件,与ANSI C文件描述符不同的是,POSIX文件描述符是int类型的一个整数值。
POSIX文件描述符仅是一个索引值, 代表内核打开文件记录表的记录索引。在一个系统中,文件打开关闭比较频繁,因此同一个POSIX 文件描述符的值在不同时间可能代表不同的文件。

Linux系统下默认一个进程最多可以打开1024个文件,用户可以通过ulimit -n查看系统允许打开文件的数量。
stdin、stdout、stderr 文件描述符分比为0,1,2。
使用fileno()函数可以返回一个流对应的文件描述符,使用read、write之类的I/O系统调用处理。
使用fdopen可以将一个文件描述符转换成文件流,就可以使用stdio库函数中fread、fwrite之类的处理。

int fileno(FILE *stream);
FILE *fdopen(int fd, const char *mode);

1.2 创建/打开/关闭文件

open()函数打开一个文件,在指定一定参数(O_CREAT - 创建文件)的情况下,会隐含调用creat()函数创建文件。open和creat函数定义如下:

其中,sys/types.h包含基本系统数据类型;sys/stat.h包含文件状态;fcntl.h包含文件控制定义。

(1) open函数

flags参数指定打开文件的方式,参数如下表所示:

前三个为文件访问模式标志,三个flags参数只能使用其中一个,不可同时使用。

O_TRUNC -文件存在且允许写,则清空文件

其中open函数给出两种定义,第二种多了mode参数,在flag参数指定为O_CREAT参数时,mode参数用于设置文件的权限,如下表所示:

在创建新文件时,参数mode指定了文件的权限,但是通常会被umask修改,实际创建时的权限为mode&(~umask)。注意,mode仅在创建新文件时有效

open打开失败时,会返回-1,并且设置预定的全局变量errno,下表为出错代码及意义。

创建新文件后,文件的atime(上次访问时间)、ctime(创建时间)、mtime(修改时间)都被修改为当前时间,文件的上层目录的atime和ctime也被修改。另外,如果打开时使用了O_TRUNC参数,则ctime和mtime被设置为当前时间。

(2)creat函数

(3)close函数


与其他系统调用一样,应对close调用做错误检查。
企图关闭一个未打开的文件描述符或两次关闭同一个文件描述符,会导致错误。
使用NFS网络文件系统时保存文件,建议关闭文件时检查返回值,防止文件写入错误。如果NFS出现提交失败,意味着数据没有抵达远程磁盘,close会调用失败,出错代码如下:

1.3 读写文件内容

(1)write函数


write函数调用从buffer中读取count字节的数据写入由fd指代的已打开文件中。参数buf是写入数据的缓冲开始地址,参数count表示要写入多少字节的数据。
write调用的返回值为实际写入文件中的字节数有可能小于count,失败返回-1且设置errno代码,错误代码如下:


write调用成功并不能保证数据已经写入磁盘,内核会缓存磁盘的I/O操作。

(2)read函数


返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0
参数count是请求读取的字节数,读上来的数据保存在缓冲区buf中,同时文件的当前读写位置向后移。注意这个读写位置和使用ANSI C标准I/O库时的读写位置有可能不同,这个读写位置是记在内核中的,而使用C标准I/O库时的读写位置是用户空间I/O缓冲区中的位置。比如用fgetc读一个字节,fgetc有可能从内核中预读1024个字节到I/O缓冲区中,再返回第一个字节,这时该文件在内核中记录的读写位置是1024,而在FILE结构体中记录的读写位置是1。注意返回值类型是ssize_t,表示有符号的size_t,这样既可以返回正的字节数、0(表示到达文件末尾)也可以返回负值-1(表示出错)。
read函数返回时,返回值说明了buf中前多少个字节是刚读上来的。有些情况下,实际读到的字节数(返回值)会小于请求读的字节数count,例如:读常规文件时,在读到count个字节之前已到达文件末尾。例如,距文件末尾还有30个字节而请求读100个字节,则read返回30,下次read将返回0。

1.4 文件内容定位

通过lseek函数设置文件偏移量

使用参数whence指定的方式,按照whence指定的偏移位置开始+偏移量offset。
whence的三种设置方式:

SEEK_END为文件最后一个字节的下一个字节。
whence为SEEK_SET时,offset为非负数。
lseek()调用成功后返回新的文件偏移量。下面的调用只是获取文件偏移量的当前位置,没有修改它。

curr = lseek(fd, 0, SEEK_CUR);

对于一个管道、FIFO、socket或者终端 ,使用lseek函数会返回-1,errono设置为EPIPE.

1.5 文件空洞

如果文件偏移量已经跨越了文件结尾,如果执行read则返回0,调用write可以在文件结尾后的任意位置写入数据。
从文件结尾后到新写入数据间的这段空间称为文件空洞。文件空洞中是存在字节的,读取空洞将返回以0(字节)填充的缓冲区。
而文件空洞不占用磁盘空间,直道文件空洞写入数据才会为之分配磁盘块。

1.6 修改已打开文件的属性

fcntl函数获取或改变已打开文件的性质,cmd支持的操作范围很广,后续内容再补充。

其中cmd为F_GETFL可以获取文件状态表示。
判定文件的访问模式需要与O_ACCMODE与fcntl范围的值相与进行判断。

flag = fcntl(fd, F_GETFL);
accessMode = flag & O_ACCMODE;
if( accessMode  == O_WRONLY(O_RDONLY/O_RDWR) )
{}

F_SETFL可以设置文件的状态标识。允许更改的标志有O_APPEND,O_NONBLOCK,O_NOATIME,O_ASYNC,O_DIRECT.

1.7 独占方式创建一个文件

情况1:

同时制定O_EXCL与O_CREAT作为open()的标志位时,如果打开的文件已经存在,则open将返回一个错误。这个机制,可以保证进程是打开文件的创建者,防止其他进程竞争。

情况2:

向文件尾部追加数据

if( lseek(fd, 0, SEEK_END) == -1 )
{}
if(write() != len )
{}

如果第一个进程运行到lseek和write之间时,被相同代码的第二个进程打断,则两个进程在写入数据前,将文件偏移量设置为相同位置,第一个进程再调度时,会覆盖第二个进程已写入的数据。此时,出现竞争状态,为避免在打开文件时需要加入O_APPEND标志。

而NFS文件系统不支持O_APPEND,从而可能导致上述脏写入问题。

1.8 文件描述符和打开文件之间的关系

多个文件描述符指向同一打开文件。这些文件描述符可在相同或不同的进程中打开。

内核维护的3个数据结构

  • 进程级的文件描述符表
  • 系统级的打开文件表
  • 文件系统的i-node表

如下图所示:

针对每个进程,内核为其维护打开文件的文件描述符表。该表的每一条目都记录了单个文件描述符的相关信息,如下所示:

  • 控制文件描述符操作的一组标志。
  • 对打开文件句柄的引用。

内核对所有打开的文件维护有一个系统级的描述表格。也称之为打开文件表,并将表中各条目称为打开文件句柄。一个打开文件句柄存储了与一个打开文件相关的全部信息,如下所示:

  • 当前文件偏移量(调用read()和write()时更新,或使用lseek()直接修改)。
  • 打开文件时所使用的状态标志(open()的flag参数)。
  • 文件访问模式(调用open()时所设置的只读模式,只写模式或读写模式)。
  • 与信号驱动I/O相关的设置。
  • 对该文件的i-node对象的引用。

每个文件系统都会为驻留其上的所有文件建立一个i-node表。i-node信息,具体如下:

  • 文件类型(例如,常规文件,套接字或FIFO)和访问权限。
  • 一个指针,指向该文件所持有的锁的列表。
  • 文件的各种属性,包括文件大小以及与不同类型操作相关的时间戳。

图中,(1) 在进程A中,文件描述符1和20都指向同一个打开的文件句柄(标号为23).这可能是通过调用dup(),dup2()或fcntl()形成的。

(2) 进程A的描述符2和进程B的文件描述符2都指向同一个打开的文件句柄(标号为73)。这种情形可能是在调用fork后出现(即,进程A和进程B之间是父子关系),或者当某进程通过UNIX域套接字将一个打开的文件描述符传递给另一进程时,也会发生。

(3) 此外,进程A的描述符0和进程B的描述符3分别指向不同的打开文件句柄,但这些句柄均指向i-node表中的相同条目(1976),换言之,指向同一文件。发生这种情况是因为每个进程各自对同一文件发起了open()调用。同一个进程两次打开同一文件,也会发生类似情况。

上述讨论揭示出如下要点:

  • 两个不同的文件描述符。若指向同一打开文件句柄,将共享同一文件偏移量(上面的情况1、2)。因此,如果通过其中之一文件描述符来修改文件偏移量(由调用read(),write(),lseek()所致),那么从另一文件描述符也会观察到这一变化。无论这两个文件描述符分属于不同进程,还是同属于一个进程,情况都是如此。
  • 要获取和修改打开的文件标志(例如,O_APPEND,O_NONBLOCK,O_ASYNC),可执行fcntl()的F_GETFL和F_SETFL操作,其对作用域的约束与上一条类似。
  • 文件描述符标志为进程和文件描述符所私有。对这一标志的修改不会影响到同一进程或不同进程中的其他文件描述符。

1.9 复制文件描述符

(1) dup

dup调用赋值一个打开的文件描述符oldfd,并返回一个新描述符,二者都指向同一个文件句柄。系统分配的是编号值最低的未用文件描述符。

#include <unistd.h>
int dup(int oldfd);

(2) dup2

dup2系统调用为oldfd参数所指向的文件描述符创建副本,其编号由newfd参数指定。

int dup2(int oldfd, int newfd);

如果由newfd参数所指定的文件描述符之前已经打开了,dup2会将其关闭。(dup2会忽略关闭newfd带来的错误,所以安全的做法是在调用dup2之前,若newfd打开了,使用close进行关闭)

1、调用dup2成功,则返回副本的文件描述符编号(即newfd)。
2、如果oldfd为无效文件描述符,则调用失败返回错误EBADF,且不关闭newfd。
3、如果oldfd有效,且与newfd相等,则什么都不做,不关闭newfd,并将其作为调用结果返回。

(3) fcntl()的F_DUPFD和F_DUPFD_CLOEXEC

fcntl()的F_DUPFD是复制文件的另一个接口,更为灵活性。
F_DUPFD命令要求返回的文件描述符会清除对应的FD_CLOEXEC标志;F_DUPFD_CLOEXEC要求设置新描述符的FD_CLOEXEC标志。

newfd = fcntl(oldfd, F_DUPFD, startfd);

调用oldfd创建一个副本,且使用大于等于startfd的最小未用值作为描述符编号

问题1:fwrite都是带缓冲区的,wrtie不带缓冲区,了解一下具体的区别?

(4) 执行时关闭标志FD_CLOEXEC

在执行exec()之前,程序有时需要确保关闭某些特定的文件描述符。从安全的角度出发,应当在加载新程序之前确保关闭哪些不必要的文件描述符。对所有此类描述符执行close()调用就可以实现。然而这一做法存在如下局限。

  • 某些描述符可能是由库函数打开的。但库函数无法使主程序在执行exec()之前关闭相应的文件描述符。作为基本原则,库函数应总是为其打开的文件设置执行时关闭(close-on-exec)标志。
  • 如果exec()因某种原因而调用失败,可能还需要使描述符保持打开状态。如果这些描述符已经关闭,将他们重新打开并指向相同文件的难度很大,基本上不可能。

内核为每个文件描述符提供了执行时关闭标志。

如果设置了这一标志,那么在成功执行exec()时,会自动关闭该文件描述符,如果调用exec()失败,文件描述符会保持打开状态。

但是,当使用dup(),dup2()或fcntl()为一文件描述符创建副本时,总是会清除副本描述符的执行时关闭标志。

调用open函数O_CLOEXEC模式打开的文件描述符,或是使用fcntl设置FD_CLOEXEC选项,这二者得到(处理)的描述符在通过fork调用产生的子进程中均不被关闭。浅析open函数O_CLOEXEC模式和fcntl函数FD_CLOEXEC选项

(5) dup3

因此dup3新增flag参数,且该参数目前只支持O_CLOEXEC,使得内核为新文件设置close-on-exe标志(FD_CLOEXEC)。

int dup3(int oldfd, int newfd, int flags);

1.10 在文件特定偏移量处的I/O:pread()和pwrite()

ssize_t pread( int fd, void* buf, size_t count, off_t offset);ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);   

在特定偏移量处进行读写,不会改变文件的偏移量。
pread()调用等于将如下调用纳入同一原子操作:

off_t orig;
orig = lseek(fd, 0, SEEK_CUR);
s = read(fd, buf, len);
lseek(fd, orig, SEEK_SET);

对于多线程编程,已打开的文件偏移量为所有线程所共享,当调用pread()或pwrite()时,多线程对同意文件描述符性质IO操作,且不会收到其他线程修改文件偏移量而受到影响

如果试图用上面的代码替代的话,会引发竞争状态,而pread()和pwrite()都是原子操作。

1.11 分散输入和集中输出:readv()和writev()

函数原型

ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);

其中,iov中每个成员都是如下形式的数据结构:

struct iovec{void *iov_base;     /*Start address of buffer*/size_t iov_len;      /*Number of bytes to transfer to/from buffer*/
};

系统实现可以通过<limits.h>文件中IOV_MAX来通告这一限额,也可以在系统运行时调用sysconf(_SC_IOV_MAX)来获取这一限额。要求该限额不得少于16。Linux将IOV_MAX的值定义为1024。

作用

readv()

从iov[0]开始一次进行填满缓冲区, 成功返回读取字节数,文件结束则返回0,出错返回-1

writev()

将iov所指定的所有缓冲区中数据拼接起来(从iov[0]依次开始),然后写入文件。 成功返回写入字节数,出错返回-1

特点

1、会改变打开文件句柄1的当前文件偏移量
2、是原子操作
readv和writev作用是便捷,可以对多个输出的内容,通过数组顺序一次性发送,不需要逐个逐个发送缓冲区的内容,并且多次发起write调动则不具备原子性。

1.12 在指定的文件偏移量出执行分散输入/集中输出

ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset);
ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset);

preadv()系统调用结合了readv()和pread(2)。 它执行与readv()相同的任务,但是添加了第四个参数offset,该参数指定要在其上执行输入操作的文件偏移量

pwritev()系统调用结合了writev()和pwrite(2)。 它执行与writev()相同的任务,但是添加了第四个参数offset,该参数指定要在其上执行输出操作的文件偏移量

与pread()和pwrite()一样不会更改文件偏移量。

1.13 截断文件:truncate()和ftruncate()

两个函数目的都是将文件大小设置为length参数指定的值

int truncate(const char *pathname, off_t length) //pathname就是路径
int ftruncate(int fd, off_t length); //该系统调用不会修改文件偏移量

文件当前长度大于参数length时,超出部分都会被丢弃;若小于则都会在文件尾部添加一系列空字节或是一个文件空洞。

truncate函数使用前不需要使用open函数打开文件,直接使用路径名字符串。

ftruncate函数则需要在可写状态下打开文件获取文件描述符

1.14 非阻塞I/O

在打开文件使用fcntl()指定O_NONBLOCK标志,目的有二:
(1):若open()调用未能立即打开文件,则返回错误,而非陷入阻塞。
(2):调用open()成功后,后续的I/O操作也是非阻塞的。
管道,FIFO,套接字,设备都支持非阻塞模式。

1.15 /dev/fd目录

对于每一个进程,内核都提供有一个特殊的虚拟目录/dev/fd。该目录中包含/dev/fd/n形式的文件名,其中n是与进程中的打开文件描述符相对应的编号,打开/dev/fd目录中的一个文件等同于复制相应的文件描述符,所以,如下两行代码是等价的

fd = open("/dev/fd/1", O_WRONLY);
fd = dup(1);

1.16 创建临时文件

有些程序需要创建一些临时文件,仅供其在运行期间使用,程序终止后立即删除。

(1) mkstemp

#include <stdlib.h>
int mkstemp(char *template);
/*returns file descriptor if OK, -1 on error*/

mkstemp()函数生成一个唯一文件名并打开该文件,返回一个可用于I/O调用的文件描述符,该模板参数采用路径名形式,其中最后6个字符必须为XXXXXX,这6个字符将被替换,以保证文件名的唯一性,且修改后的参数将通过template参数传回去。

(2) tmpfile

#include <stdio.h>
FILE *tmpfile(void);

二、文件I/O缓冲

2.1 文件I/O的内核缓冲:缓冲区高速缓存

read() 和 write() 系统调用在操作磁盘文件时不会直接发起磁盘访问,而是仅仅在用户空间缓冲区与内核缓冲区高速缓存之间复制数据。

write(fd, "abc", 3);

write() 后立即返回。在后续某个时刻,内核会将其缓冲区中的数据写入磁盘(因此可以说系统调用与磁盘操作并不同步)。如果在此期间,另一进程试图读取该文件的这几个字节,那么内核将自动从缓冲区高速缓存中提供这些数据,而不是从文件中。
于此同理,对于输入而言,内核从磁盘中读取数据并储存到内核缓冲区中。read()调用将从该缓冲区读取数据,直至把缓冲区中的数据取完。这时,内核会将文件的下一段内容读入缓冲区高速缓存(这里有所简化。对于序列化的文件访问,内核通常会尝试执行预读,以确保在需要之前就将文件的下一数据读入缓冲区高速缓存)。

从内核2.4开始,不在维护单独的缓冲区高速缓存,将文件I/O缓冲区置于页面高速缓存中,其中还有诸如内存映射文件的页面。

缓冲区大小对I/O调用性能的影响:

如果与文件发生大量的数据传输,通过采用大块空间缓冲数据,以及执行更少的系统调用,可以极大的提高I/O性能。
若强制在数据传输到磁盘前阻塞输出操作,则调用write()所需的时间会显著上升。

2.2 stdio 库的缓冲

使用stdio库可以使编程者免于自行处理对数据的缓冲,如fprintf(),fscanf(),fgets(),fputs,fputc(),fget()。

(1) setvbuf

使用setvbuf函数,可以控制stdio库使用缓冲的方式,setvbuf()调用将影响后续在指定流上进行的所有I/O操作

setvbuf(FILE *stream, char *buf, int mode, size_t size);

stream: 标识将要修改的文件流
buf 和 size 针对stream要使用的缓冲区。(需要动态或静态指定堆上的空间,若buf为NULL,则stdio库会自动分配一个)
mode:
1._IONBF 不缓冲,stderr默认属于此类型,每个stdio库函数立即调用write()或read(),并且忽略buf,size参数。
2._IOLBF 行缓冲,在输入一个换行符之前缓冲数据
3._IOFBF 全缓冲,单次读写数据的大小与缓冲区相同,磁盘的流默认采用此模式。

(2) setbuf

setbuf(FILE *stream, char *buf)

setvbuf(fp,buf ,(buf != NULL)? _IOFBF:_IONBF, BUFSIZ );

(3) 刷新stdio缓冲区

这样的一个代码:

printf("hello");
write(STDOUT_FILENO,"nihao.\n", 7);

I/O系统调用会将数据传递到内核缓冲区高速缓存,而stdio库会等到用户空间的缓冲区填满后,在使用write将其传递到内核缓冲区高速缓存中。

通常情况下,printf函数的输出会在write函数的输出之后出现。将IO系统调用和stdio函数混合使用时,使用fflush可以规避这一问题。或者,使用setvbuf或setbuf使用户缓冲区失效。

#include<stdio.h>
int fflush(FILE *stream);

若参数为NULL,则刷新所有。
刷新输入缓冲区时,将丢弃已缓冲的输入数据。
关闭相应流时,会自动刷新缓冲区。

1.应显式调用fflush(stdout),避免stdin输入导致的stdout缓冲区属性。一个输出操作不能紧跟一个输入操作,需要在二者之间调用fflush或者使用文件定位函数fseek、fsetpos或rewind。
2.反之,一个输入操作同样不能紧跟一个输出操作(C陷阱与缺陷中也提及)

2.3 控制文件I/O的内核缓冲

(1) 用于控制文件I/O内核缓冲的系统调用

fsync(int fd);

该系统调用将使缓冲数据和与打开文件描述符fd相关的所有元数据都刷新到磁盘上

(2) 使所有写入同步:O_SYNC

在调用open()函数时如指定O_SYNC标志,则会使所有后续输出同步。

fd = open(path, O_WRONLY | O_SYNC);

调用open后,每个write调用都会自动将文件数据和元数据刷新到磁盘中。谨慎使用,会影响性能。

(3) I/O缓冲小结

Linux应用程序编程相关推荐

  1. vim编译linux程序,Linux——vim程序编程器

    文书编辑器:emacs.pico.nano.joe.vim.vi vi 三种模式: 一般指令模式: 以vi打开一个文件就是直接进入一般指令模式,删除,复制,粘贴 编辑模式: [i.I.o.O.a.A. ...

  2. Linux服务器程序编程的几个坎

    TCP.IP.SOCKET基础知识 服务器模型(参考http://fanqiang.chinaunix.net/a4/b7/20010508/112359.html) 协议数据包的封包.解包 DB读写 ...

  3. 【linux草鞋应用编程系列】_4_ 应用程序多线程

    一.应用程序多线程 当一个计算机上具有多个CPU核心的时候,每个CPU核心都可以执行代码,此时如果使用单线程,那么这个线程只能在一个 CPU上运行,那么其他的CPU核心就处于空闲状态,浪费了系统资源: ...

  4. linux应用程序接收文件,Linux应用程序学习之文件编程

    Linux应用程序学习之文件系统调用函数编程 上一篇我们了解了Linux系统下函数库的设计方法,这一章我们就来学习一下linux系统下常用的文件操作函数,在进行系统文件操作函数介绍前,我们首先要明白一 ...

  5. linux系统udp通信程序,Linux UDP socket编程(UDP通讯模型) | C/C++程序员之家

    Linux UDP socket编程(UDP通讯模型): UDPClient + UDPService. Linux下大多数网络程序都是基于TCP的,很少基于UDP,简单的通讯模型如下,开发时候备用! ...

  6. 12001.linux应用程序之i2c编程

    linux应用程序之I2C编程 1 内核中关于I2C介绍 1.1 如何实例化一个I2C设备 1.2 实现一个I2C设备驱动 1.3 基本API函数 2 用户空间实现I2C编程 2.1 用户空间编程流程 ...

  7. 基于Linux的网络编程——网络聊天程序

    网络聊天程序是目前应用极为广泛的一种网络软件,对于方便人们的交流沟通非常有效,同时,作为一种典型的网络应用,编写网络聊天程序是学习基于Linux的网络编程的有效方法. 结合任务需求设计该程序,程序采用 ...

  8. 《Linux高性能服务器编程》读书笔记:linux服务器程序规范

    服务器程序规范,需要强化和遵守的,作者给列了4条规范: 1.Linux服务器程序一般以后台进程形式运行,也就是以daemon守护进程的方式,守护进程的父进程通常是init进程(PID进程为1),作者在 ...

  9. Linux下Socket编程

    Linux下Socket编程    网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符.Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的S ...

最新文章

  1. Bootstrap3 表格-带边框的表格
  2. 软件设计原则——依赖倒转原则
  3. 如何让整个表格高度固定_财务预算太费精力?多亏了这73张财务预算表格模板,让你算量快又准!直接用...
  4. c语言编写服务器的优点,用C语言编写的服务器中记录效率最高的文件是什么?...
  5. android跑步软件,手机跑步软件哪个好_安卓手机跑步记录软件_手机跑步app【最新】-太平洋电脑网...
  6. 购物车的收货地址js php,Javascript实现购物车功能的详细代码
  7. 详细描述三个适于瀑布模型的项目_IT项目管理笔记——方法选择和软件评估
  8. 秘钥协议及新兴密码学方向【7】
  9. list复制到另一个list_一文总结saltstack的十一个常用模块,附实例讲解
  10. leaflet 鼠标移动到图层时变_leaflet 图层切换报错
  11. 组策略同步的频率和设置修改
  12. 年终将至,如何轻松搞定工作汇报PPT?
  13. html无限弹窗关不掉,使用cmd命令时出现无限弹窗的故障如何终止
  14. 基于51单片机的指纹考勤系统密码锁门禁原理图PCB程序设计
  15. 微软正版验证_真正纯净无捆绑微软官方原版windows10安装教程无删减完整版
  16. HTML制作诗词,利用 html2canvas 做个简单的诗词卡片生成器
  17. vs2013断点调试
  18. Chrome浏览器通过chrono下载插件设置下载断点续传
  19. 基于微信小程序的基于安卓APP的设计毕设计ADD ME let me help you
  20. 剖析Apple Pay 它与支付宝究竟暧昧什么?

热门文章

  1. 香港电影中的演员:G字头(持续更新中)
  2. 微信小程序后台文本自动换行失效实现换行
  3. Java基本小练习4(画星星)
  4. 史上最全js数组,字符串方法汇总
  5. c语言isnumber函数的使用方法,ISNUMBER函数介绍及用法
  6. Shader Graph 翅膀
  7. 鸿蒙哪些机型可以用,鸿蒙os哪些手机可以用_鸿蒙os可用机型
  8. 2.java的基本语法
  9. nginx代理文件、图片服务器
  10. GitLab服务器IP地址设置