DPDK(三):入门2---DPDK部分代码解释
一、access函数
1.函数功能:
检查调用进程是否可以对指定的文件执行某种操作。
2.函数原型:
1)函数头文件
#include <stdio.h>
#include <unistd.h>
int access(const char * pathname, int mode)
3)形参
pathname:需要检测的文件路劲名
mode:需要测试的操作模式。
4)函数返回值说明
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EINVAL: 模式值无效
EACCES: 文件或路径名中包含的目录不可访问
ELOOP : 解释路径名过程中存在太多的符号连接
ENAMETOOLONG:路径名太长
ENOENT:路径名中的目录不存在或是无效的符号连接
ENOTDIR: 路径名中当作目录的组件并非目录
EROFS: 文件系统只读
EFAULT: 路径名指向可访问的空间外
EIO:输入输出错误
ENOMEM: 不能获取足够的内核内存
ETXTBSY:对程序写入出错
5)mode说明
R_OK 测试读许可权
W_OK 测试写许可权
X_OK 测试执行许可权
F_OK 测试文件是否存在
3.函数实例
access("test.txt", F_OK)==0) printf("File exist\n");
二、__attribute__ 机制详解
__attribute__ 可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
_attribute__ 语法格式为:__attribute__ ((attribute-list)),其位置约束为:放于声明的尾部“ ;” 之前。
1、对结构体(struct )或共用体(union )进行属性设置。
大致有六个参数值可以被设定,即:aligned(字节对齐), packed(取消字节对齐即一字节对齐), transparent_union(弱类型的联合体), unused(变量或函数可能不使用,避免编译告警), deprecated (不提倡使用的函数或变量,编译器必须生成警告)和 may_alias 。
(1)aligned
struct m
{
char a;
int b;
short c;
}__attribute__((aligned(4))) mm;
(2)packed
typedef struct {
double x;
double y;
} __attribute__((packed)) position_t;
(3)transparent_union
typedef union {
int i ;
float f;
}U __attribute__((transparent_union));
void foo (U u)
{
static int s;
s += u.i;
printf("s = %d\n",s);
}
void caller(void){
foo(1);
foo(1.0f);
}
int main(void){
caller();
}
(4)unused
__attribute__((unused)) static void a(void)
{
printf("a\n");
}
}
2、函数属性(Function Attribute)
GNU CC编译选项添加 –Wall打开所有告警,函数属性可以帮助开发者把一些特性添加到函数声明中,从而可以使编译器在错误检查方面的功能更强大。
下面介绍几个常见的函数属性:
(1)__attribute__ format,该__attribute__属性可以给被声明的函数加上类似printf或者scanf的特征,它可以使编译器检查函数声明和函数实际调用参数之间的格式化字符串是否匹配。
具体使用格式如下:
format (archetype, string-index, first-to-check)
__attribute__((format(printf,m,n)))
__attribute__((format(scanf,m,n)))
其中参数m与n的含义为:
m:第几个参数为格式化字符串(format string);
n:从函数的第几个参数开始按上述规则进行检查,即参数“…”里的第一个参数在函数参数总数排在第几。
举例:有参数不匹配的情况就会报编译告警。
1:
2:extern void myprint(const char *format,...)
_
__attribute__((format(printf,1,2)));
3:
4:void test()
5:{
6: myprint("i=%d\n",6);
7: myprint("i=%s\n",6);
8: myprint("i=%s\n","abc");
9: myprint("%s,%d,%d\n",1,2);
10:}
运行$gcc –Wall –c attribute.c attribute后,输出结果为:
attribute.c: In function `test':
attribute.c:7: warning: format argument is not a pointer (arg 2)
attribute.c:9: warning: format argument is not a pointer (arg 2)
attribute.c:9: warning: too few arguments for format
(2)__attribute__ noreturn
该属性通知编译器函数从不返回值,当遇到类似函数需要返回值而却不可能运行到返回值处就已经退出来的情况,该属性可以避免出现错误信息。C库函数中的abort()和exit()的声明格式就采用了这种格式。
例如:extern void myexit() __attribute__((noreturn));之后,编译不会再出现警告信息。
(3)__attribute__ const
该属性只能用于带有数值类型参数的函数上。当重复调用带有数值参数的函数时,由于返回值是相同的,所以此时编译器可以进行优化处理,除第一次需要运算外, 其它只需要返回第一次的结果就可以了,进而可以提高效率。
extern int square(int n) __attribute__ ((const));
...
for (i = 0; i < 100; i++ ) {
total += square (5) + i;
}
通过添加__attribute__((const))声明,编译器只调用了函数一次,以后只是直接得到了相同的一个返回值。
const参数不能用在带有指针类型参数的函数中,因为该属性不但影响函数的参数值,同样也影响到了参数指向的数据,它可能会对代码本身产生严重甚至是不可恢复的严重后果。
(4)__attribute__ constructor/destructor
若函数被设定为constructor属性,则该函数会在 main()函数执行之前被自动的执行。类似的,若函数被设定为destructor属性,则该函数会在main()函数执行之后或者exit()被调用后被自动的执行。拥有此类属性的函数经常隐式的用在程序的初始化数据方面。
__attribute__((constructor)) void before_main() {
printf("--- %s\n", __func__);
}
}
__attribute__((destructor)) void after_main() {
printf("--- %s\n", __func__);
}
}
int main(int argc, char **argv) {
printf("--- %s\n", __func__);
exit(0);
printf("--- %s, exit ?\n", __func__);
return 0;
}
}
执行结果:
--- before_main
--- main
--- after_main
三、gcc的__builtin_函数介绍
1、__builtin_prefetch() 说明
原型:void __builtin_prefetch (const void *addr, ...)
__builtin_prefetch() 是 gcc 的一个内置函数。这个函数为流操作实现预抓取机制。使用这个函数通常可以减少缓存缺失和停顿,从而提高性能。但该函数也需要 CPU 的支持。
其中参数 addr 是个内存指针,它指向要预取的数据,我们人工需要判定这些数据是很快能访问到的,或者说是它们就在最近的内存中 --- 一般来说,对于链表而言,各个节点在内存中基本上是紧挨着的,所以我们容易预取链表节点里的指针项。
该函数还有两个可选参数,rw 和 locality 。
rw 是个编译时的常数,或 1 或 0 。1 时表示写(w),0 时表示读(r) 。
locality 必须是编译时的常数,也称为“时间局部性”(temporal locality) 。时间局部性是指,如果程序中某一条指令一旦执行,则不久之后该指令可能再被执行;如果某数据被访问,则不久之后该数据会被再次访问。该值的范围在 0 - 3 之间。为 0 时表示,它没有时间局部性,也就是说,要访问的数据或地址被访问之后的不长的时间里不会再被访问;为 3 时表示,被访问的数据或地址具有高 时间局部性,也就是说,在被访问不久之后非常有可能再次访问;对于值 1 和 2,则分别表示具有低 时间局部性 和中等 时间局部性。该值默认为 3 。
2、__builtin_constant_p (exp)说明
判断exp是否在编译时就可以确定其为常量,如果exp为常量,该函数返回1,否则返回0。
3、__builtin_expect 说明
这个指令是GCC (version >= 2.96)提供给程序员使用的,作用是允许程序员将最有可能执行的分支告诉编译器。这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。
这个指令的写法为:__builtin_expect(EXP, N)。
意思是:EXP==N的概率很大。
一般的使用方法是将__builtin_expect指令封装为likely和unlikely宏。这两个宏的写法如下.
#define likely(x) __builtin_expect(!!(x), 1) //x很可能为真
#define unlikely(x) __builtin_expect(!!(x), 0) //x很可能为假
首先要明确:
if(likely(value)) //等价于 if(value)
if(unlikely(value)) //也等价于 if(value)
进一步了解原理可以反汇编看一下函数的跳转顺序,参考文档:https://blog.csdn.net/grublinux/article/details/37543489
4、__builtin_ffs(x):返回x中最后一个为1的位是从后向前的第几位,如__builtin_ffs(0x789)=1, __builtin_ffs(0x78c)=3。
5、__builtin_popcount(x):x中1的个数。
6、__builtin_parity(x):x中1的奇偶性。
7、__builtin_return_address(n):当前函数的第n级调用者的地址,用的最多的就是__builtin_return_address(0),即获得当前函数的调用者的地址。
8、__builtin_types_compatible_p(type1, type2):判断type1和type2是否是相同的数据类型,相同返回1,否则返回0。该函数不区分const/volatile这样的修饰符,即int和const int被认为是相同的类型。
四、
五、asm volatile
六、pipe
#include<unistd.h>
int pipe(int filedes[2]);
返回值:成功,返回0,否则返回-1。参数数组包含pipe使用的两个文件的描述符。fd[0]:读管道,fd[1]:写管道。
必须在fork()前调用pipe(),否则子进程不会继承文件描述符。两个进程不共享祖先进程,就不能使用pipe。但是可以使用命名管道,下面介绍。
当管道进行写入操作的时候,如果写入的数据小于128K则是非原子的,如果大于128K字节,缓冲区的数据将被连续地写入管道,直到全部数据写完为止,如果没有进程读取数据,则将一直阻塞。
2、命名管道FIFO
与管道不同的是:每个FIFO都有一个路径与之关联,从而允许无亲缘关系的进程访问。
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
这里pathname是路径名,mode是sys/stat.h里面定义的创建文件的权限.
fifo时需要注意:
fifo管道是先调用mkfifo创建,然后再用open打开得到fd来使用.
在打开fifo时要注意,它是半双工的的,一般不能使用O_RDWR打开,而只能用只读或只写打开.
fifo可以用在非亲缘关系的进程间,而它的真正用途是在服务器和客户端之间. 由于它是半双工的所以,如果要进行客户端和服务器双方的通信的话,
每个方向都必须建立两个管道,一个用于读,一个用于写。
3、socketpair
#include <sys/types.h>
#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sv[2]);
参数1(domain):表示协议族,在Linux下只能为AF_LOCAL或者AF_UNIX。(自从Linux 2.6.27后也支持SOCK_NONBLOCK和SOCK_CLOEXEC)
参数2(type):表示协议,可以是SOCK_STREAM或者SOCK_DGRAM。SOCK_STREAM是基于TCP的,而SOCK_DGRAM是基于UDP的
参数3(protocol):表示类型,只能为0
参数4(sv[2]):套节字柄对,该两个句柄作用相同,均能进行读写双向操作
返回结果: 0为创建成功,-1为创建失败,并且errno来表明特定的错误号,具体错误号如下所述:
使用注意:
该函数只能用于UNIX域(LINUX)下。
只能用于有亲缘关系的进程(或线程)间通信。
所创建的套节字对作用是一样的,均能够可读可写(而管道PIPE只能进行单向读或写)。
在读的时候,管道内必须有内容,否则将会阻塞;简而言之,该函数是阻塞的。