每个运行中的程序称为进程,它有一些与之关联的文件描述符。这是一些小值整数,你可以通过它们访问打开的文件或设备。有多少文件描述符可用取决于系统的配置情况。当一个程序开始运行时,它一般会有3个已经打开的文件描述符:
0:标准输入;
1:标准输出;
2:标准错误输出;
你可以通过系统调用open函数把其他文件描述符与文件和设备相关联,稍后讲解。其实使用自动打开的文件描述符就已经可以通过write系统调用来创建一些简单的程序了。
1.write系统调用:
系统调用write的作用是把缓冲区buf的前nbytes字节写入与文件描述符fildes关联的文件中。它返回实际写入的字节数。如果文件描述符有错误或者底层的设备驱动程序对数据块长度比较敏感,该返回值可能会小于nbytes。如果这个函数返回0,就表示为写入任何数据;如果它返回的是-1,就表示在write调用中出现了错误,错误代码保存在全局变量errno里。
下面是write系统调用的原型:
#include<unistd.h>
size_t write(int fildes, const void *buf, size_t nbytes);
有了这些知识,你就可以编写第一个程序simple_write.c了:
#include<unistd.h>
#include<stdlib.h>
int main()
{
if((write(1,"Here is some data\n",18)) !=18)
{
write(2, "A write error has occurred on file descriptor\n",46);
}
exit(0);
}
这个程序只在标准输出上显示一条消息。当程序退出运行时,所有已经打开的文件描述符都会自动关闭,所以你不需要明确的关闭它们。
编译程序:
gcc -o simple_write simple_write.c
生成:simple_write可执行文件;
运行程序:./simple_write
程序输出:Here is some data
2.read系统调用:
系统调用read的作用是:从与文件描述符fildes相关联的文件里读入nbytes个字节的数据,并把它们放到数据区buf中。它返回实际读入的字节数,这可能会小于请求的字节数。如果read调用返回0,就表示未读入任何数据,已到达了文件尾。同样,如果返回-1,就表示read调用出现了错误。
下面是read系统调用的原型:
#include<unistd.h>
size_t read(int fildes, const void *buf, size_t nbytes);
下面这个程序simple_read.c把标准输入的前128个字节复制到标准输出。如果输入少于128个字节,就把它们全体复制过去。
#include<unistd.h>
#include<stdlib.h>
int main()
{
char buffer[128];
int nread;
nread = read(0, buffer, 128);
if(nread==-1)
{
write(2, "A read error has occurred\n", 26);
}
if((write(1, buffer, nread)) != nread)
{
write(2, "A write error has occurred\n", 27);
}
exit(0);
}
编译程序:gcc -o simple_read simple_read.c
生成simple_read可执行文件;
运行程序:echo hello there | ./simple_read
程序输出:hello there
运行程序时,你使用echo通过管道为程序提供输入。
执行程序:./simple_read < simple_read.c
输出:
#include<unistd.h>
#include<stdlib.h>
int main()
{
char buffer[128];
int nread;
nread = read(0, buffer, 128);
if(nreadandroid@android-caihaitao:~/linux_test$
请注意,下一个shell提示符出现在输出数据最后一行的尾部,因为在这个例子中,128个字节的数据并没有构成一个完成的行。