用于记录linux平台下c语言的学习记录
关键字
volatile
定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确的说就是,编译器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
作用
就是防止编译器对代码进行优化
使用volatile变量的例子
- 并行设备的硬件寄存器(如:状态寄存器)
- 一个中断服务子进程中会访问到的非自动变量(Non-automatic variables)
- 多线程应用中被几个任务共享的变量
string.h
strcmp
C 库函数 int strcmp(const char _str1, const char _str2)
把 str1 所指向的字符串和 str2 所指向的字符串进行比较。
1
2
3
| #include <string.h>
int strcmp(const char *str1, const char *str2)
|
参数
- str1 – 要进行比较的第一个字符串。
- str2 – 要进行比较的第二个字符串。
返回值
- 如果返回值小于 0,则表示 str1 小于 str2。
- 如果返回值大于 0,则表示 str1 大于 str2。
- 如果返回值等于 0,则表示 str1 等于 str2。
实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| #include <stdio.h>
#include <string.h>
int main ()
{
char str1[15];
char str2[15];
int ret;
strcpy(str1, "abcdef");
strcpy(str2, "ABCDEF");
ret = strcmp(str1, str2);
if(ret < 0)
{
printf("str1 小于 str2");
}
else if(ret > 0)
{
printf("str1 大于 str2");
}
else
{
printf("str1 等于 str2");
}
return(0);
}
|
strtol函数用法
C 库函数 long int strtol(const char *str, char **endptr, int base)
把参数 str 所指向的字符串根据给定的 base 转换为一个长整数(类型为 long int 型),base 必须介于 2 和 36(包含)之间,或者是特殊值 0。
1
| long int strtol(const char *str, char **endptr, int base)
|
参数
- str – 要转换为长整数的字符串。
- endptr – 对类型为 char* 的对象的引用,其值由函数设置为 str 中数值后的下一个字符。
- base – 基数,必须介于 2 和 36(包含)之间,或者是特殊值 0。
返回值
该函数返回转换后的长整数,如果没有执行有效的转换,则返回一个零值。
实例
下面的实例演示了 strtol() 函数的用法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| #include <stdio.h>
#include <stdlib.h>
int main()
{
char str[30] = "2030300 This is test";
char *ptr;
long ret;
ret = strtol(str, &ptr, 10);
printf("数字(无符号长整数)是 %ld\n", ret);
printf("字符串部分是 |%s|", ptr);
return(0);
}
|
运行结果
1
2
| 数字(无符号长整数)是 2030300
字符串部分是 | This is test|
|
strstr
在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 ‘\0’。
1
| char *strstr(const char *haystack, const char *needle);
|
参数
- haystack – 要被检索的 C 字符串。
- needle – 在 haystack 字符串内要搜索的小字符串。
返回值
该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。
示例
下面的实例演示了 strstr() 函数的用法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| #include <stdio.h>
#include <string.h>
int main()
{
const char haystack[20] = "RUNOOB";
const char needle[10] = "NOOB";
char *ret;
ret = strstr(haystack, needle);
printf("子字符串是: %s\n", ret);
return(0);
}
|
让我们编译并运行上面的程序,这将产生以下结果:
sscanf
sscanf 读取格式化的字符串中的数据。
1
2
3
4
5
| #include <stdio.h>
int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
|
ffs
ffs()函数用于查找一个整数中的第一个置位值(也就是bit为1的位)。
1
2
| #include<strings.h>
int ffs(int i);
|
这个函数几乎没有使用的,很少见到。
socket编程errno大全
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
| 0:Success
1:Operation notpermitted
2:No such fileor directory
3:No suchprocess
4:Interruptedsystem call
5:Input/outputerror
6:No such deviceor address
7:Argument listtoo long
8:Exec formaterror
9:Bad filedescriptor
10:No childprocesses
11:Resourcetemporarily unavailable
12:Cannot allocatememory
13:Permissiondenied
14:Bad address
15:Block devicerequired
16:Device orresource busy
17:File exists
18:Invalidcross-device link
19:No suchdevice
20:Not adirectory
21:Is adirectory
22:Invalidargument
23:Too many openfiles in system
24:Too many openfiles
25:Inappropriateioctl for device
26:Text filebusy
27:File toolarge
28:No space lefton device
29:Illegal seek
30:Read-onlyfile system
31:Too manylinks
32:Broken pipe
33:Numericalargument out of domain
34:Numericalresult out of range
35:Resourcedeadlock avoided
36:File name toolong
37:No locksavailable
38:Function notimplemented
39:Directory notempty
40:Too manylevels of symbolic links
41:Unknown error41
42:No message ofdesired type
43:Identifierremoved
44:Channelnumber out of range
45:Level 2 notsynchronized
46:Level 3halted
47:Level 3 reset
48:Link numberout of range
49:Protocoldriver not attached
50:No CSIstructure available
51:Level 2halted
52:Invalidexchange
53:Invalidrequest descriptor
54:Exchange full
55:No anode
56:Invalidrequest code
57:Invalid slot
58:Unknown error58
59:Bad font fileformat
60:Device not astream
61:No dataavailable
62:Timer expired
63:Out ofstreams resources
64:Machine isnot on the network
65:Package notinstalled
66:Object isremote
67:Link has beensevered
68:Advertiseerror
69:Srmount error
70:Communicationerror on send
71:Protocolerror
72:Multihopattempted
73:RFS specificerror
74:Bad message
75:Value toolarge for defined data type
76:Name notunique on network
77:Filedescriptor in bad state
78:Remoteaddress changed
79:Can notaccess a needed shared library
80:Accessing acorrupted shared library
81:.lib sectionin a.out corrupted
82:Attempting tolink in too many shared libraries
83:Cannot exec ashared library directly
84:Invalid orincomplete multibyte or wide character
85:Interruptedsystem call should be restarted
86:Streams pipeerror
87:Too manyusers
88:Socketoperation on non-socket
89:Destinationaddress required
90:Message toolong
91:Protocolwrong type for socket
92:Protocol notavailable
93:Protocol notsupported
94:Socket typenot supported
95:Operation notsupported
96:Protocolfamily not supported
97:Addressfamily not supported by protocol
98:Addressalready in use
99:Cannot assignrequested address
100:Network isdown
101:Network isunreachable
102:Networkdropped connection on reset
103:Softwarecaused connection abort
104:Connectionreset by peer
105:No bufferspace available
106:Transportendpoint is already connected
107:Transportendpoint is not connected
108:Cannot sendafter transport endpoint shutdown
109:Too manyreferences: cannot splice
110:Connectiontimed out
111:Connectionrefused
112:Host is down
113:No route tohost
114:Operationalready in progress
115:Operationnow in progress
116:Stale NFSfile handle
117:Structureneeds cleaning
118:Not a XENIXnamed type file
119:No XENIXsemaphores available
120:Is a namedtype file
121:Remote I/Oerror
122:Disk quotaexceeded
123:No mediumfound
124:Wrong mediumtype
125:Operationcanceled
126:Required keynot available
127:Key hasexpired
128:Key has beenrevoked
129:Key wasrejected by service
130:Owner died
131:State notrecoverable
132:Unknownerror 132
133:Unknownerror 133
134:Unknownerror 134
135:Unknownerror 135
136:Unknownerror 136
137:Unknownerror 137
138:Unknownerror 138
139:Unknownerror 139
140:Unknownerror 140
141:Unknownerror 141
142:Unknownerror 142
143:Unknownerror 143
144:Unknownerror 144
145:Unknownerror 145
146:Unknownerror 146
147:Unknownerror 147
148:Unknownerror 148
149:Unknownerror 149
|
printf
输出控制符
控制符 | 说明 |
---|
%d | 按十进制整型数据的实际长度输出。 |
%ld | 输出长整型数据。 |
%md | m 为指定的输出字段的宽度。如果数据的位数小于 m,则左端补以空格,若大于 m,则按实际位数输出。 |
%u | 输出无符号整型(unsigned)。输出无符号整型时也可以用 %d,这时是将无符号转换成有符号数,然后输出。但编程的时候最好不要这么写,因为这样要进行一次转换,使 CPU 多做一次无用功。 |
%c | 用来输出一个字符。 |
%f | 用来输出实数,包括单精度和双精度,以小数形式输出。不指定字段宽度,由系统自动指定,整数部分全部输出,小数部分输出 6 位,超过 6 位的四舍五入。 |
%.mf | 输出实数时小数点后保留 m 位,注意 m 前面有个点。 |
%o | 以八进制整数形式输出,这个就用得很少了,了解一下就行了。 |
%s | 用来输出字符串。用 %s 输出字符串同前面直接输出字符串是一样的。但是此时要先定义字符数组或字符指针存储或指向字符串,这个稍后再讲。 |
%x(或 %X 或 %#x 或 %#X) | 以十六进制形式输出整数,这个很重要。 |
如何输出 %d、\ 和双引号
printf 中有输出控制符%d
,转义字符前面有反斜杠\
,还有双引号。那么大家有没有想过这样一个问题:怎样将这三个符号通过 printf 输出到屏幕上呢?
要输出%d
只需在前面再加上一个%
,要输出\
只需在前面再加上一个\
,要输出双引号也只需在前面加上一个\
即可。程序如下:
1
2
3
4
5
6
7
| # include <stdio.h>
int main(void){
printf("%%d\n");
printf("\\\n");
printf("\"\"\n");
return 0;
}
|
attribute 机制详解
GNU C 的一大特色就是attribute 机制。attribute 可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。
其位置约束为: 放于声明的尾部";" 之前
内存分配
malloc
C 库函数 void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。
1
2
3
| #include <stdlib.h>
void *malloc(size_t size)
|
参数
返回值
该函数返回一个指针 ,指向已分配大小的内存。如果请求失败,则返回 NULL。
实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| #include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char *str;
/* 最初的内存分配 */
str = (char *) malloc(15);
strcpy(str, "runoob");
printf("String = %s, Address = %u\n", str, str);
/* 重新分配内存 */
str = (char *) realloc(str, 25);
strcat(str, ".com");
printf("String = %s, Address = %u\n", str, str);
free(str);
return(0);
}
|
free
C 库函数 void free(void *ptr) 释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。
1
2
3
| #include <stdlib.h>
void free(void *ptr)
|
参数
- ptr – 指针指向一个要释放内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果传递的参数是一个空指针,则不会执行任何动作。
calloc
1
| void *calloc(size_t n, size_t size);
|
calloc会自动初始化分配的空间,置为0。malloc需要自行初始化。
realloc
1
| void *realloc(void *ptr, size_t new_Size)
|
对动态内存进行扩容。ptr为原空间地址,new_Size为新增空间大小。
alloca
1
2
| #include
void *alloca(size_t size);
|
函数说明:alloca() 用来配置 size 个字节的内存空间,然而和 malloc、colloc 不同的是,
alloca() 是从 栈空间(stack) 中分配内存的,因此在函数返回时会自动释放此空间。
返回值:内存分配成功则返回首地址,失败则返回 NULL
popen
1
2
| #include<stdio.h>
FILE * popen( const char * command,const char * type);
|
popen()会调用fork()产生子进程,然后从子进程中调用/bin/sh -c来执行参数command的指令。参数type可使用“r”代表读取,“w”代表写入。依照此type值,popen()会建立管道连到子进程的标准输出设备或标准输入设备,然后返回一个文件指针。随后进程便可利用此文件指针来读取子进程的输出设备或是写入到子进程的标准输入设备中。此外,所有使用文件指针(FILE*)操作的函数也都可以使用,除了fclose()以外。
- 如果 type 为 r,那么调用进程读进 command 的标准输出。
- 如果 type 为 w,那么调用进程写到 command 的标准输入。
返回值
若成功则返回文件指针,否则返回NULL,错误原因存于errno中。
错误值
EINVAL参数type不合法。
示例
1
2
3
4
5
6
7
8
9
10
11
| #include<stdio.h>
main()
{
FILE *fp;
char buffer[80];
fp = popen("cat /etc/passwd", "r");
fgets(buffer, sizeof(buffer), fp);
printf("%s", buffer);
pclose(fp);
}
|
pclose
获取popen执行的命令,在pclose中获取到的main函数的返回值,返回的数字为256的倍数,使用WEXITSTATUS(retValue) 可以获取真实值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| int write_value_to_json(char *key, char *value, char *type)
{
FILE *fp = NULL;
char buf[128] = {0};
char cmd[128] = {0};
snprintf(cmd, "djsonutil get /tmp/config_nat_set %s %s", key, type);
fp = popen(key, "r");
if(fp != NULL) {
fread(buf, sizeof(buf), 1, fp);
int ret = pclose(fp);
if(WEXITSTATUS(ret) == 0) {
strncpy(value, buf, strlen(buf));
} else {
retrun -1;
}
}
return -1;
}
|
C 库宏
offsetof()
C 库宏 offsetof(type, member-designator) 会生成一个类型为 size_t 的整型常量,它是一个结构成员相对于结构开头的字节偏移量。成员是由 member-designator 给定的,结构的名称是在 type 中给定的。
1
| offsetof(type, member-designator)
|
参数
- type – 这是一个 class 类型,其中,member-designator 是一个有效的成员指示器。
- member-designator – 这是一个 class 类型的成员指示器。
返回值
该宏返回类型为 size_t 的值,表示 type 中成员的偏移量。
实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| #include <stddef.h>
#include <stdio.h>
struct address {
char name[50];
char street[50];
int phone;
};
int main()
{
printf("address 结构中的 name 偏移 = %d 字节。\n",
offsetof(struct address, name));
printf("address 结构中的 street 偏移 = %d 字节。\n",
offsetof(struct address, street));
printf("address 结构中的 phone 偏移 = %d 字节。\n",
offsetof(struct address, phone));
return(0);
}
|
执行结果
1
2
3
| address 结构中的 name 偏移 = 0 字节。
address 结构中的 street 偏移 = 50 字节。
address 结构中的 phone 偏移 = 100 字节。
|
typeof
typeof关键字是C语言中的一个新扩展,这个特性在linux内核中应用非常广泛。
typeof()关键字常见用法一共有以下几种。
- 不用知道函数返回什么类型,可以使用typeof()定义一个用于接收该函数返回值的变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| #include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct apple{
int weight;
int color;
};
struct apple *get_apple_info()
{
struct apple *a1;
a1 = malloc(sizeof(struct apple));
if(a1 == NULL)
{
printf("malloc error.\n");
return;
}
a1->weight = 2;
a1->color = 1;
return a1;
}
int main(int argc, char *argv[])
{
typeof(get_apple_info()) r1;//定义一个变量r1,用于接收函数get_apple_info()返回的值,由于该函数返回的类型是:struct apple *,所以变量r1也是该类型。注意,函数不会执行。
r1 = get_apple_info();
printf("apple weight:%d\n", r1->weight);
printf("apple color:%d\n", r1->color);
return 0;
}
|
- 在宏定义中动态获取相关结构体成员的类型
如下代码,定义一个和变量x相同类型的临时变量_max1,定义一个和变量y相同类型的临时变量_max2,再判断两者类型是否一致,不一致给出一个警告,最后比较两者。
1
2
3
4
5
| #define max(x, y) ({ \
typeof(x) _max1 = (x); \
typeof(y) _max2 = (y); \
(void) (&_max1 == &_max2); \//如果调用者传参时,两者类型不一致,在编译时就会发出警告。
_max1 > _max2 ? _max1 : _max2; })
|
- 如下代码,传入的a和b不是同一类型。
1
2
3
4
5
6
7
8
9
10
| int main(int argc, char *argv[])
{
int a=3;
float b = 4.0;
int r = max(a, b);
printf("r:%d\n", r);
return 0;
}
|
- 此时编译就会出现警告
1
2
3
| [[email protected] c_base]# gcc test.c
test.c: 在函数‘main’中:
test.c:43: 警告:比较不相关的指针时缺少类型转换
|
- 也可直接取得已知类型
如下代码,定义了一个int类型的指针p,像这种用法主没什么太大的意义了。
1
2
3
4
| int a = 2;
typeof (int *) p;
p = &a;
printf("%d\n", *p);
|
- 其它用法
1
2
3
4
5
6
7
8
9
| //其它用法1
char *p1;
typeof (*p1) ch = 'a'; //ch为char类型,不是char *类型。
printf("%d, %c\n", sizeof(ch), ch);//1, a
//其它用法2
char *p2;
typeof (p2) p = "hello world";//此时的p才是char *类型,由于在64位机器上,所以指针大小为8字节
printf("%d, %s\n", sizeof(p), p);//8, hello world
|
select函数
select函数使用
函数定义
1
2
3
4
5
6
7
8
9
| #include <sys/select.h>
void FD_CLR(fd, fd_set *fdset);
void FD_COPY(fd_set *fdset_orig, fd_set *fdset_copy);
int FD_ISSET(fd, fd_set *fdset);
void FD_SET(fd, fd_set *fdset);
void FD_ZERO(fd_set *fdset);
int select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *exceptfds, struct timeval *timeout););
|
函数说明
select()用来等待文件描述词状态的改变。参数nfds代表最大的文件描述词加1,参数readfds、writefds和exceptfds 成为描述词组,是用来回传该描述词的读、写或例外的状况。底下的宏提供了处理这三种描述词组的方式:
1
2
3
4
5
| void FD_CLR(fd, fd_set *fdset); // 用来清除描述词组set中相关fd的位
void FD_COPY(fd_set *fdset_orig, fd_set *fdset_copy);
int FD_ISSET(fd, fd_set *fdset); // 用来测试描述词组set中相关fd的位是否位真
void FD_SET(fd, fd_set *fdset); // 用来设置描述词组set中相关fd的位
void FD_ZERO(fd_set *fdset); // 用来清除描述词组set的全部位
|
在使用FD_ISSET测试fd_set数据类型中的描述符后,描述符集内任何与未就绪描述符对应的位返回时均被清0,因此,每次重新调用select函数时,都需要将描述符集内所关心的位置为1描述符必须被初始化,因为作为自动变量分配的一个描述符如果没有被初始化,那么发生的后果不可预期
readset、writeset、exceptset三个参数中,若对其中任何参数条件不感兴趣,则可将其设为NULL
timeout: 告知内核等待指定描述符中的任何一个就绪的时间限制
timeval结构:
1
2
3
4
| struct timeval{
long tv_sec; // 秒
long tv_usec; // 微秒
}
|
timeout参数的三种可能:
- 设为空指针:永远等待下去,仅在有描述符就绪时才返回
- 正常设置timeout,在不超过timeout设置的时间内,在有描述符就绪时返回
- 将timeout.tv_sec和timeout.tv_usec都设为0:检查描述符后立即返回(轮询)
前两种情形的等待通常会被进程在等待期间捕获的信号中断,并从信号处理函数返回,因此在考虑可移植性的情况下,若我们在捕获信号,那必须做好select返回EINTR错误的准备
描述符的就绪条件:
可读条件:
- 该套接字接收缓冲区中的数据字节数大于等于套接字接收缓冲区低水位标记的当前大小
- 该连接的读半部关闭(即接收了FIN的TCP连接)
- 该套接字是一个监听套接字且已完成的连接数不为0
- 该套接字上有一个套接字错误待处理
可写条件:
- 该套接字发送缓冲区中的可用空间字节数大于等于套接字发送缓冲区低水位标记的当前大小
- 该连接的写半部关闭
- 使用非阻塞式connect的套接字已建立连接,或者connect已经以失败告终
- 该套接字上有一个套接字错误待处理
异常条件:
a.该套接字存在带外数据或者仍处于带外标记
**接受缓冲区低水位标记(读):**让select返回套接字接收缓冲区所需数据量
**发送缓冲区低水位标记(写):**让select返回套接字发送缓冲区可用空间
返回值
- 负值:select错误
- 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件
如果参数timeout设为NULL则表示select()没有timeout。
错误代码
执行成功则返回文件描述词状态已改变的个数,如果返回0代表在描述词状态改变前已超过timeout时间,当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds,exceptfds和timeout的值变成不可预测。
- EBADF 文件描述词为无效的或该文件已关闭
- EINTR 此调用被信号所中断
- EINVAL 参数n为负值。
- ENOMEM 核心内存不足
getopt
1
2
3
4
5
6
| #include <unistd.h>
extern char *optarg; //选项的参数指针
extern int optind, //下一次调用getopt的时,从optind存储的位置处重新开始检查选项。
extern int opterr, //当opterr=0时,getopt不向stderr输出错误信息。
extern int optopt; //当命令行选项字符不包括在optstring中或者选项缺少必要的参数时,该选项存储在optopt中,getopt返回'?’、
int getopt(int argc, char * const argv[], const char *optstring);
|
1.单个字符,表示选项,
2.单个字符后接一个冒号:表示该选项后必须跟一个参数。参数紧跟在选项后或者以空格隔开。该参数的指针赋给optarg。
3 单个字符后跟两个冒号,表示该选项后必须跟一个参数。参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给optarg。(这个特性是GNU的扩张)。
getopt处理以’-’开头的命令行参数,如optstring=“ab:c::d::",命令行为getopt.exe -a -b host -ckeke -d haha
在这个命令行参数中,-a和-h就是选项元素,去掉’-’,a,b,c就是选项。host是b的参数,keke是c的参数。但haha并不是d的参数,因为它们中间有空格隔开。
还要注意的是默认情况下getopt会重新排列命令行参数的顺序,所以到最后所有不包含选项的命令行参数都排到最后。
如getopt.exe -a ima -b host -ckeke -d haha, 都最后命令行参数的顺序是: -a -b host -ckeke -d ima haha
如果optstring中的字符串以’+‘加号开头或者环境变量POSIXLY_CORRE被设置。那么一遇到不包含选项的命令行参数,getopt就会停止,返回-1。
daemon
run in the background
1
2
| #include <unistd.h>
int daemon(int nochdir, int noclose);
|
The daemon() function is for programs wishing to detach themselves from the controlling terminal and run in the background as system daemons.
If nochdir is zero, daemon() changes the calling process’s current working directory to the root directory (”/"); otherwise, the current working directory is left unchanged.
If noclose is zero, daemon() redirects standard input, standard output and standard error to /dev/null; otherwise, no changes are made to these file descriptors.
return value:
(This function forks, and if the fork(2) succeeds, the parent calls _exit(2), so that further errors are seen by the child only.) On success daemon() returns zero. If an error occurs, daemon() returns -1 and sets errno to any of the errors specified for the fork(2) and setsid(2).
errno
当linux中的 api函数发生异常时,一般会将errno变量(需include errno.h)赋一个整数值,不同的值表示不同的含义,可以通过查看该值推测出错的原因,在实际编程中用这一招解决了不少原本看来莫名其妙的问题。但是errno是一个数字,代表的具体含义还要到errno.h中去阅读宏定义,而每次查阅是一件很繁琐的事情。下面的几种方法可以方便的得到错误信息
perror
perror ()
用来将上一个函数发生错误的原因输出到标准错误(stderr),参数s 所指的字符串会先打印出,后面再加上错误原因 字符串。此错误原因依照全局变量 errno
的值来决定要输出的字符串。
1
2
3
| #include <errno.h>
#include <stdio.h>
void perror(const char *s)
|
strerror
将错误代码转换为字符串错误信息,可以将该字符串和其它的信息组合输出到用户界面
1
2
| #include <errno.h>
char *strerror(int errno)
|
errno 的部分值
以下来自linux 2.4.20-18的内核代码中的/usr/include/asm/errno.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
| #ifndef _I386_ERRNO_H
#define _I386_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 / Arg 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 NFS 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 */
#endif
|
herror
在使用 gethostbyname() 的时候,你不能用perror() 打印错误信息(因为 errno 没有使用),你应该调用 herror()。
1
2
| #include <netdb.h>
void herror( const char *s );
|
适用于以下函数
1
2
3
4
5
6
7
| gethostbyaddr()
gethostbyaddr_r()
gethostbyname()
gethostbyname2()
gethostbyname_r()
res_query()
res_search()
|
会出现的错误信息
- HOST_NOT_FOUND
Authoritative answer: Unknown host.
- NETDB_INTERNAL
You specified an invalid address family when calling gethostbyname2().
- NO_DATA
Valid name, no data record of the requested type. The name is known to the name server, but has no IP address associated with it—this isn’t a temporary error. Another type of request to the name server using this domain name will result in an answer (e.g. a mail-forwarder may be registered for this domain).
- NO_RECOVERY
Unknown server error. An unexpected server failure was encountered. This is a nonrecoverable network error.
- TRY_AGAIN
Nonauthoritative answer: Host name lookup failure. This is usually a temporary error and means that the local server didn’t receive a response from an authoritative server. A retry at some later time may succeed.
ctype
isalpha
isalpha() 函数用来检测一个字符是否是字母。
要检测的字符。它可以是一个有效的字符(被转换为 int 类型),也可以是 EOF(表示无效的字符)。
1
2
| #include <ctype.h>
int isalpha(int c);
|
返回值
返回值为非零(真)表示c是字母,返回值为零(假)表示c不是字母。
islower
islower() 函数用来检测一个字符是否是小写字母。
1
2
| #include <ctype.h>
int islower (int c);
|
在默认情况下,小写字母包括:
a b c d e f g h i j k l m n o p q r s t u v w x y z
标准 ASCII 编码共包含了 128 个字符,不同的字符属于不同的分类,我们在 <ctype.h> 头文件中给出了详细的列表。
参数
- c – 要检测的字符。它可以是一个有效的字符(被转换为 int 类型),也可以是 EOF(表示无效的字符)。
返回值
返回值
为非零(真)表示c是小写字母,返回值为零(假)表示c不是小写字母。
toupper
toupper()将小写字母转换为大写字母
1
2
| #include <ctype.h>
int toupper (int c);
|
只有当参数 c 是一个小写字母,并且存在对应的大写字母时,这种转换才会发生。
在默认情况下,小写字母包括:
a b c d e f g h i j k l m n o p q r s t u v w x y z
大写字母包括:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
参数
- c – 要转换的字符。它可以是一个有效的字符(被转换为 int 类型),也可以是 EOF(表示无效的字符)。
返回值
如果转换成功,那么返回与 c 对应的大写字母;如果转换失败,那么直接返回 c(值未变)。
注意,返回值为 int 类型,你可能需要隐式或者显式地将它转换为 char 类型。
settimer
在linux c编程中。setitimer是一个比較经常使用的函数。可用来实现延时和定时的功能,网上有各种零零散散的使用方法说明,都仅仅提到了个别使用方法,今天抽出时间实践整理了一份比較具体的:
函数定义
1
2
3
| #include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
|
当中which參数表示类型。可选的值有:
ITIMER_REAL:以系统真实的时间来计算,它送出SIGALRM信号。
ITIMER_VIRTUAL:以该进程在用户态下花费的时间来计算,它送出SIGVTALRM信号。
ITIMER_PROF:以该进程在用户态下和内核态下所费的时间来计算。它送出SIGPROF信号。
紧接着的new_value和old_value均为itimerval结构体,先看一下itimerval结构体定义:
1
2
3
4
5
6
7
8
9
| struct itimerval {
struct timeval it_interval; /* next value */
struct timeval it_value; /* current value */
};
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
|
itimeval又是由两个timeval结构体组成,timeval包括tv_sec和tv_usec两部分。当中tv_se为秒。tv_usec为微秒(即1/1000000秒)
当中的new_value參数用来对计时器进行设置,it_interval为计时间隔,it_value为延时时长,以下样例中表示的是在setitimer方法调用成功后,延时1微秒便触发一次SIGALRM信号,以后每隔200毫秒触发一次SIGALRM信号。
settimer工作机制是,先对it_value倒计时,当it_value为零时触发信号。然后重置为it_interval。继续对it_value倒计时。一直这样循环下去。
基于此机制。setitimer既能够用来延时运行,也可定时运行。
假如it_value为0是不会触发信号的,所以要能触发信号,it_value得大于0;假设it_interval为零,仅仅会延时。不会定时(也就是说仅仅会触发一次信号)。
old_value參数,通经常使用不上。设置为NULL,它是用来存储上一次setitimer调用时设置的new_value值。
下面是一个简单的使用样例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| #include <stdio.h>
#include <signal.h>
#include <sys/time.h>
void signalHandler(int signo)
{
switch (signo){
case SIGALRM:
printf("Caught the SIGALRM signal!\n");
break;
}
}
int main(int argc, char *argv[])
{
signal(SIGALRM, signalHandler);
struct itimerval new_value, old_value;
new_value.it_value.tv_sec = 0;
new_value.it_value.tv_usec = 1;
new_value.it_interval.tv_sec = 0;
new_value.it_interval.tv_usec = 200000;
setitimer(ITIMER_REAL, &new_value, &old_value);
for(;;);
return 0;
}
|
预置宏
可变参数宏__VA_ARGS__
使用方法
1
| #define my_print2(fmt,...) printf(fmt,##__VA_ARGS__)
|
Q/A
linux 多线程与信号
接收到一个信号时,主进程会停止运行,直到信号处理完成后再继续。
信号处理顺序:最后一个信号处理完再处理上一个,直到全部处理完成后再运行主进程