系统调用
系统调用是由操作系统核心提供,运行于核心态;普通函数调用由库函数或者用户自定义,处于用户态。那些标准函数都是由系统调用完成的。
查看系统调用的接口:
[edemon@CentOS ~]$ man 2 syscalls
进程不能访问内核,系统调用则是接口,由他们告诉内核进程的请求是什么。
错误码
errno: 1–34存在于: /usr/include/asm-generic/errno-base.h
errno: 35–133记录在: /usr/include/asm-generic/errno.h
#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Argument list too long */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file number */
#define ECHILD 10 /* No child processes */
#define EAGAIN 11 /* Try again */
#define ENOMEM 12 /* Out of memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#define ENOTBLK 15 /* Block device required */
#define EBUSY 16 /* Device or resource busy */
#define EEXIST 17 /* File exists */
#define EXDEV 18 /* Cross-device link */
#define ENODEV 19 /* No such device */
#define ENOTDIR 20 /* Not a directory */
#define EISDIR 21 /* Is a directory */
#define EINVAL 22 /* Invalid argument */
#define ENFILE 23 /* File table overflow */
#define EMFILE 24 /* Too many open files */
#define ENOTTY 25 /* Not a typewriter */
#define ETXTBSY 26 /* Text file busy */
#define EFBIG 27 /* File too large */
#define ENOSPC 28 /* No space left on device */
#define ESPIPE 29 /* Illegal seek */
#define EROFS 30 /* Read-only file system */
#define EMLINK 31 /* Too many links */
#define EPIPE 32 /* Broken pipe */
#define EDOM 33 /* Math argument out of domain of func */
#define ERANGE 34 /* Math result not representable */
#define EDEADLK 35 /* Resource deadlock would occur */
#define ENAMETOOLONG 36 /* File name too long */
#define ENOLCK 37 /* No record locks available */
#define ENOSYS 38 /* Function not implemented */
#define ENOTEMPTY 39 /* Directory not empty */
#define ELOOP 40 /* Too many symbolic links encountered */
#define EWOULDBLOCK EAGAIN /* Operation would block */
#define ENOMSG 42 /* No message of desired type */
#define EIDRM 43 /* Identifier removed */
#define ECHRNG 44 /* Channel number out of range */
#define EL2NSYNC 45 /* Level 2 not synchronized */
#define EL3HLT 46 /* Level 3 halted */
#define EL3RST 47 /* Level 3 reset */
#define ELNRNG 48 /* Link number out of range */
#define EUNATCH 49 /* Protocol driver not attached */
#define ENOCSI 50 /* No CSI structure available */
#define EL2HLT 51 /* Level 2 halted */
#define EBADE 52 /* Invalid exchange */
#define EBADR 53 /* Invalid request descriptor */
#define EXFULL 54 /* Exchange full */
#define ENOANO 55 /* No anode */
#define EBADRQC 56 /* Invalid request code */
#define EBADSLT 57 /* Invalid slot */
#define EDEADLOCK EDEADLK
#define EBFONT 59 /* Bad font file format */
#define ENOSTR 60 /* Device not a stream */
#define ENODATA 61 /* No data available */
#define ETIME 62 /* Timer expired */
#define ENOSR 63 /* Out of streams resources */
#define ENONET 64 /* Machine is not on the network */
#define ENOPKG 65 /* Package not installed */
#define EREMOTE 66 /* Object is remote */
#define ENOLINK 67 /* Link has been severed */
#define EADV 68 /* Advertise error */
#define ESRMNT 69 /* Srmount error */
#define ECOMM 70 /* Communication error on send */
#define EPROTO 71 /* Protocol error */
#define EMULTIHOP 72 /* Multihop attempted */
#define EDOTDOT 73 /* RFS specific error */
#define EBADMSG 74 /* Not a data message */
#define EOVERFLOW 75 /* Value too large for defined data type */
#define ENOTUNIQ 76 /* Name not unique on network */
#define EBADFD 77 /* File descriptor in bad state */
#define EREMCHG 78 /* Remote address changed */
#define ELIBACC 79 /* Can not access a needed shared library */
#define ELIBBAD 80 /* Accessing a corrupted shared library */
#define ELIBSCN 81 /* .lib section in a.out corrupted */
#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
#define ELIBEXEC 83 /* Cannot exec a shared library directly */
#define EILSEQ 84 /* Illegal byte sequence */
#define ERESTART 85 /* Interrupted system call should be restarted */
#define ESTRPIPE 86 /* Streams pipe error */
#define EUSERS 87 /* Too many users */
#define ENOTSOCK 88 /* Socket operation on non-socket */
#define EDESTADDRREQ 89 /* Destination address required */
#define EMSGSIZE 90 /* Message too long */
#define EPROTOTYPE 91 /* Protocol wrong type for socket */
#define ENOPROTOOPT 92 /* Protocol not available */
#define EPROTONOSUPPORT 93 /* Protocol not supported */
#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
#define EPFNOSUPPORT 96 /* Protocol family not supported */
#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
#define EADDRINUSE 98 /* Address already in use */
#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
#define ENETDOWN 100 /* Network is down */
#define ENETUNREACH 101 /* Network is unreachable */
#define ENETRESET 102 /* Network dropped connection because of reset */
#define ECONNABORTED 103 /* Software caused connection abort */
#define ECONNRESET 104 /* Connection reset by peer */
#define ENOBUFS 105 /* No buffer space available */
#define EISCONN 106 /* Transport endpoint is already connected */
#define ENOTCONN 107 /* Transport endpoint is not connected */
#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
#define ETOOMANYREFS 109 /* Too many references: cannot splice */
#define ETIMEDOUT 110 /* Connection timed out */
#define ECONNREFUSED 111 /* Connection refused */
#define EHOSTDOWN 112 /* Host is down */
#define EHOSTUNREACH 113 /* No route to host */
#define EALREADY 114 /* Operation already in progress */
#define EINPROGRESS 115 /* Operation now in progress */
#define ESTALE 116 /* Stale file handle */
#define EUCLEAN 117 /* Structure needs cleaning */
#define ENOTNAM 118 /* Not a XENIX named type file */
#define ENAVAIL 119 /* No XENIX semaphores available */
#define EISNAM 120 /* Is a named type file */
#define EREMOTEIO 121 /* Remote I/O error */
#define EDQUOT 122 /* Quota exceeded */
#define ENOMEDIUM 123 /* No medium found */
#define EMEDIUMTYPE 124 /* Wrong medium type */
#define ECANCELED 125 /* Operation Canceled */
#define ENOKEY 126 /* Required key not available */
#define EKEYEXPIRED 127 /* Key has expired */
#define EKEYREVOKED 128 /* Key has been revoked */
#define EKEYREJECTED 129 /* Key was rejected by service */
/* for robust mutexes */
#define EOWNERDEAD 130 /* Owner died */
#define ENOTRECOVERABLE 131 /* State not recoverable */
#define ERFKILL 132 /* Operation not possible due to RF-kill */
#define EHWPOISON 133 /* Memory page has hardware error */
系统调用部分函数练习
writev
将多个字符串一次性全部写入文件中。
函数相关:
#include <sys/uio.h>
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
struct iovec {
void *iov_base; /* Starting address */
size_t iov_len; /* Number of bytes to transfer */
};
The writev() system call writes iovcnt buffers of data described by iov to the file associated with the file descriptor fd
("gather output").
The writev() system call works just like write(2) except that multiple buffers are written out.
例子:
#include <stdio.h>
#include <stdlib.h>
#include <sys/uio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
char *buff[4];
buff[0] = "My song for you this evening\n";
buff[1] = "It's not to make you sad\n";
buff[2] = "Not for adding to the sorrows\n";
buff[3] = "Over troubled northern land\n";
struct iovec *iov = (struct iovec *)malloc(sizeof(struct iovec)*4);
iov[0].iov_base = buff[0];
iov[0].iov_len = strlen(buff[0]);
iov[1].iov_base = buff[1];
iov[1].iov_len = strlen(buff[1]);
iov[2].iov_base = buff[2];
iov[2].iov_len = strlen(buff[2]);
iov[3].iov_base = buff[3];
iov[3].iov_len = strlen(buff[3]);
int iovcnt = sizeof(iov);
int fd = open("writev.txt",O_RDWR|O_TRUNC|O_CREAT,0644);
if(fd == -1){
perror("open ");
exit(1);
}
ssize_t bytes = writev(fd,iov,iovcnt);
if(bytes < 0){
perror("writev ");
exit(1);
}
close(fd);
free(iov);
return 0;
}
readv
与writev()相反的函数。它一次能读取数据到多个字符数组中。函数和writev一样。
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
#include <stdio.h>
#include <stdlib.h>
#include <sys/uio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
char *buff[4];
int i = 0;
for(i=0;i<4;i++){
buff[i] = (char *)malloc(40);
}
struct iovec *iov = (struct iovec *)malloc(sizeof(struct iovec)*4);
iov[0].iov_base = buff[0];
iov[0].iov_len = 40;
iov[1].iov_base = buff[1];
iov[1].iov_len = 40;
iov[2].iov_base = buff[2];
iov[2].iov_len = 40;
iov[3].iov_base = buff[3];
iov[3].iov_len = 40;
int iovcnt = sizeof(iov);
int fd = open("writev.txt",O_RDONLY,0644);
if(fd == -1){
perror("open ");
exit(1);
}
ssize_t bytes = readv(fd,iov,iovcnt);
if(bytes < 0){
perror("readv ");
exit(1);
}
for(i=0;i<4;i++){
printf("%s",buff[i]);
}
close(fd);
free(iov);
return 0;
}
执行
[edemon@CentOS workspace]$ gcc readv.c
[edemon@CentOS workspace]$ ./a.out
My song for you this evening
It's not to make you sad
Not for adding to the sorrows
Over troubled northern land
lseek, dup
lseek()用于重置光标位置。dup()则是赋值文件描述符。
off_t lseek(int fd, off_t offset, int whence);
int dup(int oldfd);
下面的例子是打开文件txt,描述符是fd,写入”hello world” 复制文件描述符成new_fd,隔行写入”this is new_fd”. 最后读取文件内容。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(){
int fd = open("txt",O_WRONLY|O_CREAT|O_TRUNC,0644);
lseek(fd,0,SEEK_SET);
char *str = "hello world";
int ret = write(fd,str,strlen(str));
if(ret <= 0){
perror("write to fd");
exit(1);
}
int new_fd = dup(fd);
if(new_fd == -1){
perror("dup ");
exit(1);
}
str = (char *)malloc(50);
strcpy(str,"\nthis is new_fd.");
ret = write(new_fd,str,strlen(str));
if(ret <= 0){
perror("write to new_fd");
exit(1);
}
new_fd = open("txt",O_RDONLY);
ret = read(new_fd,str,50);
if(ret <= 0){
perror("read ");
printf("errno is %d\n",errno);
exit(1);
}
printf("%s",str);
close(fd);
close(new_fd);
free(str);
return 0;
}
执行:
hello world
this is new_fd.
poll
poll和select相似,用于等待fd集状态的改变。
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
struct pollfd *fds = (struct pollfd*)calloc(2,sizeof(struct pollfd));
fds[0].fd = open("/tmp/test1",O_RDONLY);
fds[0].events = POLLIN;
fds[1].fd = open("/tmp/test2",O_RDONLY);
fds[1].events = POLLIN;
int milliSec = 500, i;
while(1){
int ret = poll(fds,2,milliSec);
if(ret == -1){
perror("poll ");
exit(1);
}
else if(ret == 0){
puts("waiting a short time...");
sleep(3);
continue;
}
else {
for(i=0;i<ret;i++){
if(fds[i].revents & POLLIN){
char *buff = (char *)calloc(30,1);
if(read(fds[i].fd,buff,30) == -1){
perror("read ");
exit(1);
}
printf("test%d, buff is : %s",i+1,buff);
free(buff);
}
}
break;
}
}
for(i=0;i<2;i++) close(fds[i].fd);
free(fds);
return 0;
}
编译运行:
$ ./a.out
test1, buff is : hello world
test2, buff is : I love linux
access
检查文件是否有相应的权限。int access(const char *pathname, int mode);
比如检查文件/tmp/linux 是否存在:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
const char *str = "/tmp/linux";
int ret = access(str,F_OK);
if(ret == -1){
puts("the file /tmp/linux is not there.");
}
else {
puts("here is /tmp/linux.");
}
return 0;
}
chdir
chdir用以改变程序的工作目录。
int chdir(const char *path);
#include <stdio.h>
#include <stdlib.h>
int main(){
const char *str1 = "/tmp";
system("pwd");
if(chdir(str1) == -1){
perror("chdir ");
exit(1);
}
system("pwd");
return 0;
}
编译执行:
[edemon@CentOS workspace]$ ./a.out
/home/edemon/workspace
/tmp
symlink readlink
symlink用于建立新的(符号)连接,readlink用于查看符号连接内容(对象的名字)。
int symlink(const char *target, const char *linkpath);
ssize_t readlink(const char *path, char *buf, size_t bufsiz);
正常执行后buff即含有内容信息。
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(){
const char *str = "./readlink";
symlink("./read",str);
char *buff = (char *)calloc(20,1);
if(readlink(str,buff,19) == -1){
printf("readlink : %d",errno);
exit(1);
}
printf("read buff: %s",buff);
return 0;
}
执行:
[edemon@CentOS workspace]$ ./a.out
read buff: read
[edemon@CentOS workspace]$ ls -l readlink
lrwxrwxrwx. 1 edemon edemon 4 Dec 15 20:19 readlink -> read
link
link创建一个文件的(硬)连接。
int link(const char *oldpath, const char *newpath);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main(){
if(link("./test.txt","./test2.txt") == -1){
printf("link errno is %d\n",errno);
exit(1);
}
system("ls -l |grep test");
return 0;
}
软连接和硬连接的区别:如果将源文件删除了,软连接文件是不能访问的,但是硬连接文件仍然正常;软连接可以跨磁盘分区,但是硬连接不能。
remove
int remove(const char *pathname);
如果pathname是一个文件,unlink会被调用;如果pathname是文件夹,那么rmdir则会被调用。
#include <stdio.h>
#include <stdlib.h>
int main(){
int tag = 0;
tag = tag|remove("./test_dir");
tag = tag|remove("./test");
if(tag == 0){
printf("remove test_dir and test.txt ok.\n");
}
else printf("remove failed.\n");
return 0;
}
opendir readdir
DIR *opendir(const char *name);
DIR*是一种目录流。
struct dirent *readdir(DIR *dirp);
readdir的返回值是指向struct dirent的指针。
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* not an offset; see NOTES */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all filesystem types */
char d_name[256]; /* filename */
};
在dirent.h中说明了d_type:
/* File types for `d_type'. */
enum
{
DT_UNKNOWN = 0,
# define DT_UNKNOWN DT_UNKNOWN
DT_FIFO = 1,
# define DT_FIFO DT_FIFO
DT_CHR = 2,
# define DT_CHR DT_CHR
DT_DIR = 4,
# define DT_DIR DT_DIR
DT_BLK = 6,
# define DT_BLK DT_BLK
DT_REG = 8,
# define DT_REG DT_REG
DT_LNK = 10,
# define DT_LNK DT_LNK
DT_SOCK = 12,
# define DT_SOCK DT_SOCK
DT_WHT = 14
# define DT_WHT DT_WHT
};
查找当前目录下所有的文件夹:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
int main(){
DIR* here = opendir("./");
struct dirent *dir = NULL;
while((dir=readdir(here)) != NULL){
if(dir->d_type == 4)
printf("type of file is dir, name of file is %s\n",dir->d_name);
}
return 0;
}
/*
$ ./a.out
type of file is dir, name of file is ..
type of file is dir, name of file is .
type of file is dir, name of file is mydir
type of file is dir, name of file is code
type of file is dir, name of file is .metadata
type of file is dir, name of file is test_dir
*/
index, rindex
index用于寻找字符串中第一个出现的指定字符串。
char *index(const char *s, int c);
rindex用于查找字符串中最后一个出现的指定字符。
char *rindex(const char *s, int c);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
const char* str = "0123456789";
char* dex = index(str,'4');
printf("%s\n",dex);
return 0;
}
/*
$ ./a.out
456789
*/
和index相似的函数memchr用于在s所指内存中前n个字节查找有木有c存在。void *memchr(const void *s, int c, size_t n);
memmove
void *memmove(void *dest, const void *src, size_t n);
内存拷贝函数memmove和memcpy都是用于复制src前n个字节到dest中.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
const char* str = "01234543210";
printf("first location of 4: %s\n",index(str,'4'));
printf("last location of 4: %s\n",rindex(str,'4'));
return 0;
}
/*
$ ./a.out
first location of 4: 4543210
last location of 4: 43210
*/
strcasecmp
strcasecmp()是一个可以忽略大小写比较的函数,这在某些数据库密码验证中很有用。
int strcasecmp(const char *s1, const char *s2);
例如:
#include <stdio.h>
#include <stdlib.h>
int main(){
const char* str1 = "ASdF";
const char* str2 = "asDf";
if(strcasecmp(str1,str2) == 0) printf("%s is same as %s\n",str1,str2);
return 0;
}
/*
./a.out
ASdF is same as asDf
*/
strcspn, strspn
size_t strcspn(const char *s, const char *reject);
strcspn用于统计源字符串s中不含指定字符串reject中字符的连续字符个数。
size_t strspn(const char *s, const char *accept);
与之相反是strspn,它用于统计包含accept字符串中字符的连续字符的个数。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
const char* str1 = "a34b_c2>?<2dfh.L";
printf("the length of continuous string without '._?<' is %d\n",strcspn(str1,"._?<"));
printf("the length of continuous string without '_c2a43' is %d\n",strspn(str1,"_c2a43"));
return 0;
}
/*
./a.out
the length of continuous string without '._?<' is 4
the length of continuous string without '_c2a43' is 3
*/
strdup
strdup会先malloc出一块内存,然后将字符串拷贝到该内存,最后将相应的指针返回。该内存可以被free.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
const char* str = "hello world";
char* new = strdup(str);
printf("%s\n",new);
free(new);
return 0;
}
/*
$ ./a.out
hello world
*/
strpbrk
char *strpbrk(const char *s, const char *accept);
strpbrk()用于查找在s中第一个出现在accept中的字符的位置。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
const char* s = "0123456789";
printf("for 2: %s\n",strpbrk(s,"a2"));
printf("for 4: %s\n",(char *)strpbrk(s,"b4"));
return 0;
}
/*
./a.out
for 2: 23456789
for 4: 456789
*/
strstr
char *strstr(const char *haystack, const char *needle);
strstr用于确定子字符串的位置。和index相比,第二个参数不是char,而是string.
strtok
char *strtok(char *str, const char *delim);
strtok()用于将字符串分割成不同的片段,它在参数s字符串中发现delim中的字符后将之变成0,第一次调用时必须给于str字符串参数,之后的调用将其设置成NULL,每一次调用成功将会返回下一个指向待分割字符串的指针。
#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[]){
printf("%s\n",strtok(argv[1],argv[2]));
char *p = NULL;
while((p = strtok(NULL,argv[2])) != NULL) printf("%s\n",p);
return 0;
}
/*
edemon@ubuntu1:~/workspace$ gcc -o strtok strtok.c
edemon@ubuntu1:~/workspace$ ./strtok 12-34.45-78.90-11 .-
12
34
45
78
90
11
*/
ctime
char *ctime(const time_t *timep);
ctime用于将指向time_t的指针信息转化成字符串时间格式。我们需要先获取它的参数,可以通过time_t time(time_t *t);
实现,time() returns the time as the number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(){
time_t time_p;
time(&time_p);
printf("%s",ctime(&time_p));
return 0;
}
/*
$ ./a.out
Tue Dec 20 21:47:00 2016
*/
字符串转化成数值
function | format | introduce |
---|---|---|
atof | double atof(const char *nptr); | convert a string to a double |
atoi | int atoi(const char *nptr); | convert a string to an int integer |
atol | long atol(const char *nptr); | convert a string to an long integer |
atoll | long long atoll(const char *nptr); | convert a string to an long long integer |
gcvt
char *gcvt(double number, size_t ndigit, char *buf);
浮点数转化成字符串,四舍五入。函数返回buf指针所指空间。
#include <stdio.h>
#include <stdlib.h>
int main(){
double number = 123.456;
char* buf = (char* )malloc(10);
printf("3 digits: %s\n",gcvt(number,3,buf));
printf("4 digits: %s\n",gcvt(number,4,buf));
printf("5 digits: %s\n",gcvt(number,5,buf));
return 0;
}
/*
./a.out
3 digits: 123
4 digits: 123.5
5 digits: 123.46
*/
toascii
int toascii(int c);
toascii()用于将参数c转化成转化成7位无符号char型数值,参数的高位将被抹去。
比如:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
int a = toascii(1<<8);
printf("2^8: %d(%c)\n",a,a);
a = toascii((1<<8)-1);
char str[5];
str[0] = a;
str[1] = 0;
printf("2^8-1: %d(%s)\n",a,str);
return 0;
}
/*
./a.out
2^8: 0()
2^8-1: 127)
*/
1000000002⟶010⟶NUL
0111111112⟶12710⟶DEL
让我感觉奇怪的是DEL不是删除右边字符吗,为什么这里变成删除左边的(了?