一、系统调用与库函数
(一)系统调用
常见系统调用
(二)库函数
普通的库函数调用由函数库或用户自己提供,因此库函数是可以替换的。例如,对于存储空间分配函数malloc,如果不习惯它的操作方式,我们完全可以定义自己的malloc函数。
系统调用通常提供最小接口,而库函数通常提供较复杂功能
系统调用运行在内核空间,而库函数运行在用户空间
因为系统调用属于内核,和库函数不属于内核。因此,如果当用户态进程调用一个系统调用时,CPU需要将其切换到内核态,并执行一个内核函数。
内核调用都返回一个整数值,而库函数并非一定如此
在内核中,整数或0表示系统调用成功结束,而负数表示一个出错条件。而出错时,内核不会将其设置在errno,而是由库函数从系统调用返回后对其进行设置或使用。
POSIX 标准针对库函数而不是系统调用
判断一个系统是否与POSIX需要看它是否提供一组合适的应用程序接口,而不管其对应的函数是如何实现的。因此从移值性来讲,使用库函数的移植性较系统调用更好。
系统调用运行时间属于系统时间,库函数运行时间属于用户时间
调用系统调用开销相对库函数来说更大
二、使用C库函数控制LED
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define LED1_BRIGHTNESS "/sys/class/leds/led1/brightness"
#define LED2_BRIGHTNESS "/sys/class/leds/led2/brightness"
#define LED3_BRIGHTNESS "/sys/class/leds/led3/brightness"
int main()
{
FILE *fd1, *fd2, *fd3;
fd1 = fopen(LED1_BRIGHTNESS, "w");
if(fd1 < 0) {
printf("Fail to Open %s devicen", LED1_BRIGHTNESS);
exit(1);
}
fd2 = fopen(LED2_BRIGHTNESS, "w");
if(fd2 < 0) {
printf("Fail to Open %s devicen", LED2_BRIGHTNESS);
exit(1);
}
fd3 = fopen(LED3_BRIGHTNESS, "w");
if(fd3 < 0) {
printf("Fail to Open %s devicen", LED3_BRIGHTNESS);
exit(1);
}
while(1) {
fwrite("1",3,1,fd1);
fflush(fd1);
sleep(1);
fwrite("0",1,1,fd1);
fflush(fd1);
fwrite("1",3,1,fd2);
fflush(fd2);
sleep(1);
fwrite("0",1,1,fd2);
fflush(fd2);
fwrite("1",3,1,fd3);
fflush(fd3);
sleep(1);
fwrite("0",1,1,fd3);
fflush(fd3);
}
fclose(fd1);
fclose(fd2);
fclose(fd3);
return 0;
}
三、总结
本次实验使用C库函数实现了对LED的控制,通过一个简单的示例来感受系统调用与库函数的区别。但是代码中还有需要注意的地方。
代码中调用fwrite函数写入内容时,它可能只是把内容保存到了C库的缓冲区,并没有执行真正的系统调用write函数把内容写入到设备文件,这种情况下LED灯的状态是不会被改变的,代码中在fwrite函数后调用了fflush要求立刻把缓冲区的内容写入到文件,确保 执行了相应的操作。在实验时可以尝试把代码中的fflush都注释掉,这种情况下有极大的几率是无法正常改变LED灯状态的。
如果不考虑操作的时间开销,其实控制硬件更推荐的做法是,每次控制LED灯都使用fopen—fwrite—fclose的流程,这样就不需要考虑flseek、fflush的问题了,但最推荐的还是直接通过系统调用来控制硬件的方式。