系统调用是操作系统为用户态运行的进程与硬件设备(如CPU、磁盘、打印机等)进行交互提供的一组接口函数;
库函数:顾名思义是把函数放到库里。是把一些常用到的函数编完放到一个库文件里,供别人用,别人用的时候把它所在的文件名用#include<>加到里面就可以了。可分为两类,一类是c语言标准规定的库函数,一类是编译器特定的库函数。
man 手册第一部分是命令 man 1 printf
man 手册第二部分是系统调用 man 2 write
man 手册第三部分是库函数 man 3 printf
对于内核而言,所有打开的文件、设备、网络socket都是通过文件描述符引用,文件描述符是一个非负整数。当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符。当读、写一个文件时,使用open或creat返回的文件描述符标识该文件,将其作为参数传给read或write。
按照惯例,UNIX系统shell把文件描述符与进程的标准输入关联,文件描述符1与标准输出关联,文件描述符2与标准错误关联。这是各种shell以及很多应用程序使用的惯例,与UNIX内核无关。如果不遵循这种惯例,很多UNIX系统应用程序就不能正常工作。
文件描述符的变化范围是0~OPEN_MAX-1。
应用程序运行的时候,系统将会为该进程默认打开三个文件描述符:
标准输入: STDIN_FILENO (0)
标准输出: STDOUT_FILENO (1)
标准出错: STDERR_FILENO (2)
文件描述符的变化范围是: 0~OPEN_MAX-1
int open(const char *path, int oflag, ... /*mode_t mode*/);
int openat(int fd,const char *path, int oflag, ... /*mode_t mode*/);
//两函数的返回值:若成功,返回文件描述符;若出错,返回-1
返回值 int fd 文件描述符(file description), open系统调用返回的文件描述符一定是最小的、未使用的文件描述符数值。
对于open函数而言,仅当创建新文件时才使用这个函数。
参数:
path: 要打开的文件或者创建文杰的名字、设备的路径名
oflag: 由多个选项进行“或”运算构造oflag参数
必选: O_RDONLY (只读)、 O_WRONLY(只写)、 O_RDWR(读写)
可选: O_APPEND 每次写时都追加到文件的尾端。
O_CREAT 文件不存在则创建它,使用该选项需要第三个参数mode
O_NONBLOCK 如果path是一个FIFO、块设备、字符特殊文件则此选项为文件的本次打开和后续的I/O操作设置非阻塞模式方式。
O_TRUNC 如果文件存在,而且为只写或读写成功打开,则将其长度截取为0;
O_EXEC、O_SEARCH、O_CLOEXEC、O_NOCTTY....
mode: oflag带O_CREAT选项时,必须带该参数用来指定打开文件的权限模式,如066。
例: int fd; fd = open(“text.txt”, O_RDWR|O_CREAT|O_TRUNC, 0666);
fd = open(“text.txt”, O_WRONLY|O_APPEND);
int creat(const char *path, mode_t mode);
//返回值:若成功,返回为只写打开的文件描述符;若出错,返回-1
此函数用来创建一个新文件并返回其fd。它等价于
open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);
creat的一个不足之处是它以只写方式打开创建的文件。
例: int fd; fd=creat(“text.txt”, 0644);
该函数用来关闭一个打开的文件,关闭一个文件时还会释放该进程加在该文件上的所有记录锁。当一个进程终止时,内核自动关闭它所有打开的文件。
#include
int close(int fd);
//返回值:若成功,返回0;若出错,返回-1
每打开一个文件时都有一个与其关联的“当前文件偏移量”。它通常是一个非负整数,用以度量从文件开始处计算的字节数。通常,读、写操作都是从当前文件偏移量处开始,并使用偏移量增加所读写的字节数。
按系统默认的情况,当打开一个文件时,除非指定O_APPEND选项,否则该偏移量被设置为0.
#include
off_t lseek(int fd, off_t offset, int whence);
//返回值:若成功,返回新的文件偏移量;若出错,返回-1
对参数offset的解释与参数whence的值相关:
whence: SEEK_SET, 则将该文件偏移量设置为距文件开始处offset个字节;
SEEK_CUR,则该文件的偏移量设置为当前值加offset,offset可为正或负;
SEEK_END,则将该文件偏移量设置为长度加offset,offset可正可负;
若lseek成功执行,则返回新的文件偏移量,为此可以用下列方式确定打开文件偏移量:
off_t pos;
pos = lseek(fd, 0, SEEK_CUR);
这种方法也可以用来确定所涉及的文件是否可以设置偏移量。如果文件描述符指向的是一个管道、FIFO或网络套接字,则lseek返回-1,并将errno设置为ESPIPE。
例如:
所示的程序用于测试对其标准输入是否设置偏移量。
#include "apue.h"
int
main(void)
{
if(lseek(STDIN_FILENO,0,SEEK_CUR)==-1)
printf("cannot seek\n");
else
printf("seek OK\n");
exit(0);
}
read()函数用来从打开的文件中读取数据:
#include
ssize_t read(int fd, void *buf, size_t nbytes);
//返回值:读到的字节数,若以读到文件尾,返回0;若出错,返回-1
如read成功,则返回读到的字节数。如已到达文件的尾端,则返回0。
write()函数用来往打开的文件中写入数据:
#include
ssize_t write(int fd, const void *buf, size_t nbytes);
//返回值:若成功,返回已写的字节数;若出错,返回-1
如write成功,则返回实际写入的字节数,失败返回-1。
write出错的一个常见原因是磁盘已写满,或者超过了一个给定进程的文件长度限制。
对于普通文件,写操作从当前文件的当前偏移量出开始。
大部分的Linux系统调用返回值都是0表示成功,-1表示失败。在库函数中有个整形类型的errno变量,每个errno值对应着以字符串表示的错误类型。如果系统调用出错,则该函数将重新设置errno的值,通过该信息我们可以查看系统调用出错的具体原因。
perror() 用来将上一个函数发生错误的原因输出到标准出错上(stderr)。参数 s 所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量errno的值来决定要输出的字符串。
strerror通过标准错误的标号,获得错误的描述字符串 ,将单纯的错误标号转为字符串描述,方便用户查找错误。
#include
#include
int fd;
if( (fd=open(“none_exsit_file.xt”, O_RDONLY)) )
{
perror(“Oen file failure”);
printf(“Open file failure: %s\n”, strerror(errno));
}
例如:
int main(int argc, char *argv)
{
int fd = -1;
char buf[1024];
//char *buf;
fd = open("file.txt", O_RDWR|O_CREAT|O_TRUNC);
if(fd < 0)
{
perror("Open failure");
printf("Open %s failure:[%d] %s\n", "file.txt", errno, strerror(errno));
return ;
}
write(fd, "Hello", 5);
memset(buf, 0, sizeof(buf));
read(fd, buf, sizeof(buf));
printf("file content: %s\n", buf);
close(fd);
}
以下两个函数都可以用来复制一个现有的文件描述符。
int dup(int fd);
int dup2(int fd, int fd2);
由dup返回的新文件描述符一定是当前可用文件描述符中的最小数值。
dup2可以用fd2参数指定新描述符的值。如果fd2已经打开,则先关闭。如fd等于fd2, 则dup2返回fd2, 而不关闭它。
使用这两个函数可以用来实现标注输入、标准输出、标准出错重定向:
int fd = -1;
fd = open(“std.txt”, O_RDWR|O_CREAT|O_TRUNC, 0666);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
printf(“Hello Word!\n”);
例如;
int main(int argc, char *argv)
{
int fd = -1;
fd = open("file.txt", O_RDWR|O_CREAT|O_TRUNC);
if(fd < 0)
{
printf("Open %s failure:[%d] %s\n", "file.txt", errno, strerror(errno));
return ;
}
dup2(fd, 1);
printf("fd=%d", fd);
close(fd);
}
ioctl()函数一直是I/O操作的杂物箱,不能用本章中其他函数表示的I/O操作通常都能用ioctl()表示。终端I/O、设备I/O是使用ioctl()最多的地方。
int ioctl(int fd, int cmd, ...);
fd: 文件描述符
cmd: 命令字,这个参数需要与设备驱动中的cmd保持一致。
第三个参数可选
今后的LED驱动,我们将会在驱动中实现该函数,在应用程序空间再调用该函数控制LED的亮和灭。
int fd = -1;
fd = open(“/dev/led”, O_RDWR);
ioctl(fd, TURN_OFF, 3);
close(fd);
stat系列函数用来返回文件或目录的相关信息:
int stat(const char * restrict path, struct stat *restrict buf);
int fstat(int fd, struct stat *buf);
struct stat
{
mode_t st_mode; struct timespec st_atime;
ino_t st_ino; struct timespec st_mtime;
dev_t st_dev; struct timespec st_ctime;
dev_t st_rdev; blksize_t st_blksize;
nlink_t st_nlink; blkcnt_t st_blocks;
uid_t st_uid; }
gid_t st_gid;
off_t st_size;
文件类型: 普通文件(-)、目录文件(d)、块设备(b)、字符设备(c)、FIFO(p)、套接字socket(s)、符号链接(l)
#include
#include
#include
int main (int argc, char **argv)
{
struct stat stbuf;
stat("file.txt", &stbuf);
printf("File size: %d bytes UID:%d GID:%d\n",
stbuf.st_size, stbuf.st_uid, stbuf.st_gid);
return 0;
}
access()
access可以用来测试文件是否存在或测试其权限位:
int access(const char *path, int mode);
mode: F_OK、R_OK、W_OK、X_OK
测试文件是否存在:
if( !(access(“test.txt”, F_OK)) ) // 返回0表示存在;返回-1表示不存在。
{
printf(“File exist\n”);
}
测试文件是否可读可写:
if( !(access(”test.txt”, R_OK|W_OK)) ) // 返回0表示存在;返回-1表示不存在。
{
printf(“File mode is read/write \n”);
}
int unlink(const char *path);
调用该函数将path指定的文件的链接数减1,如果对该文件还有其他链接存在,则仍可以通过其他链接访问该文件的数据。
只有当链接记数达到0时,该文件的内容才可被删除。如果有进程打开了该文件,其内容也不能被删除。关闭一个文件时,内核首先检查打开该文件的进程个数,如果这个记数达到0,内核再去检查它的链接记数,如果记数也是0,那么就删除该文件内容。
int rename(const char *oldname, const char *newname);
调用该函数可以实现文件的重命名。
创建文件夹:
int mkdir(const char *pathname, mode_t mode);
删除文件夹:
int rmdir(const char *pathname);
打开文件夹:
DIR *opendir(const char *pathname);
读文件夹:
struct dirent * readdir(DIR *dp);
关闭文件夹:
int closedir(IDR *dp);
改变工作目录:
int chdir(const char * pathname);
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define EEPROM_SIZE 256
#define EEPROM_DEVPATH "eeprom.txt"
#define SN_OFFSET 0x0
#define SN_LEN 14 /* Serial Number: LY-FL2440-001 */
#define MAC_OFFSET 0x10
#define MAC_LEN 18 /* MAC address : 00:05:69:00:00:01 */
#define USER_OFFSET 0x30
#define USER_LEN 24 /* Ower name : guowenxue */
#define MODE_NONE 0
#define MODE_WR 1
#define MODE_RD 2
int write_eeprom(int fd, int offset, char *data, int len);
int read_eeprom(int fd, int offset, char *buf, int size);
static void prog_usage(const char *progname)
{
printf("Usage: %s [OPTION]...\n", progname);
printf("This program used to write/read SN,MAC,Name into/from EEPROM\n");
printf(" -r[read ] read SN,MAC,Name from EEPROM\n");
printf(" -s[sn ] Specify intgegrate serial number, such as 10. Will use it generate SN and MAC address.\n");
printf(" -u[sn ] Specify user name, string such as \"lingyun\"\n");
printf(" -h[help ] Display this help information\n");
return ;
}
int main (int argc, char **argv)
{
int fd = -1;
int sn = 0;
char *user = NULL;
char buf[EEPROM_SIZE];
const char *progname=NULL;
int opt;
int rwmode = MODE_NONE;
int oflags;
struct option long_options[] =
{
{"read", no_argument, NULL, 'r'},
{"sn", required_argument, NULL, 's'},
{"user", required_argument, NULL, 'u'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
progname = basename(argv[0]);
/* Parser the command line parameters */
while ((opt = getopt_long(argc, argv, "rs:u:h", long_options, NULL)) != -1)
{
switch (opt)
{
case 'r':
rwmode = MODE_RD;
break;
case 's': /* serial number */
sn = atoi(optarg);
rwmode = MODE_WR;
break;
case 'u': /* user name */
user= optarg;
rwmode = MODE_WR;
break;
case 'h': /* Get help information */
prog_usage(progname);
return 0;
default:
break;
} /* end of "switch(opt)" */
}
if( MODE_NONE == rwmode )
{
prog_usage(progname);
return 0;
}
if( MODE_WR==rwmode )
oflags=O_RDWR|O_CREAT|O_TRUNC;
else
oflags=O_RDWR;
if( (fd=open(EEPROM_DEVPATH, oflags, 0644)) < 0 )
{
printf("Open EEPROM device '%s' failure: %s\n", EEPROM_DEVPATH, strerror(errno));
return -1;
}
if( MODE_WR == rwmode )
{
if( sn )
{
/* Generate and write serial number into EEPROM */
snprintf(buf, SN_LEN, "LY-FL2440-%03X", sn);
if( write_eeprom(fd, SN_OFFSET, buf, SN_LEN) < 0)
{
printf("Write serial number into EEPROM failure\n");
goto cleanup;
}
/* Generate and write MAC address into EEPROM */
snprintf(buf, MAC_LEN, "00:05:69:00:00:%02d", sn);
if( write_eeprom(fd, MAC_OFFSET, buf, MAC_LEN) < 0)
{
printf("Write MAC address into EEPROM failure\n");
goto cleanup;
}
}
if( user )
{
/* Write board user name */
if( write_eeprom(fd, USER_OFFSET, user, strlen(user)) < 0)
{
printf("Write user name into EEPROM failure\n");
goto cleanup;
}
}
}
else if( MODE_RD == rwmode )
{
char sn[SN_LEN];
char mac[MAC_LEN];
char user[USER_LEN];
/* Read serial number from EEPROM */
memset(sn, 0, sizeof(sn));
if( read_eeprom(fd, SN_OFFSET, sn, SN_LEN) < 0)
{
printf("Read serial number from EEPROM failure\n");
goto cleanup;
}
printf("Serial Number: %s\n", sn);
/* Read MAC address from EEPROM */
memset(mac, 0, sizeof(mac));
if( read_eeprom(fd, MAC_OFFSET, mac, MAC_LEN) < 0)
{
printf("Read MAC address from EEPROM failure\n");
goto cleanup;
}
printf("MAC Address : %s\n", mac);
/* Read user name from EEPROM */
memset(user, 0, sizeof(user));
if( read_eeprom(fd, USER_OFFSET, user, USER_LEN) < 0)
{
printf("Read user name from EEPROM failure\n");
goto cleanup;
}
printf("User Name : %s\n", user);
}
cleanup:
close(fd);
return 0;
}
int write_eeprom(int fd, int offset, char *data, int len)
{
if( fd<0 || (offset<0||offset>=EEPROM_SIZE) || !data || len<=0 )
{
printf("Invalid input arguments for %s()\n", __func__);
return -1;
}
lseek(fd, offset, SEEK_SET);
if( write(fd, data, len) != len )
{
printf("write data into EEPROM failure: %s\n", strerror(errno));
return -2;
}
return 0;
}
int read_eeprom(int fd, int offset, char *buf, int size)
{
if( fd<0 || (offset<0||offset>=EEPROM_SIZE) || !buf || size<=0 )
{
printf("Invalid input arguments for %s()\n", __func__);
return -1;
}
lseek(fd, offset, SEEK_SET);
if( read(fd, buf, size) < 0 )
{
printf("read data from EEPROM failure: %s\n", strerror(errno));
return -2;
}
return 0;
}