1.5输入和输出
1. 文件描述符
文件描述符通常是一个小的非负整数,内核用以标识一个特定进程正在访问的文件。当内核打开一个现有文件或创建一个新文件时,都返回一个文件描述符。在读写文件时,可以使用文件描述符。
2. 标准输入、标准输出和标准错误
每当运行一个新程序时,shell都会为其打开3个文件描述符,即标准输入、标准输出和标准错误,这三个描述符都链接到终端。
3. 不带缓冲的I/O
函数open、read、write、lseek、close提供了不带缓冲的I/O,这些函数都使用文件描述符。
Page7 实例
#include "apue.h"
#define BUFFERSIZE 4096
int main(void)
{
int n;
char buf[BUFFERSIZE];
while((n = read(STDIN_FILENO, buf, BUFFERSIZE)) > 0)
if(write(STDOUT_FILENO, buf, n) != n)
err_sys("write error");
if (n < 0)
err_sys("read error");
exit(0);
}
说明:
- STDIN_FILENO和STDOUT_FILENO定义在unistd.h中,指定了标准输入和标准输出的文件描述符。
- read函数返回读取的字节数,此值用作要写的字节数。当到达输入文件末尾时,read返回0,程序停止。
-
调用方法:
- ./a.out > data
- ./a.out < infile > outfile
4. 标准I/O
标准I/O为不带缓冲的I/O函数提供了带缓冲的接口。
Page8 实例
#include "apue.h"
int main(void)
{
int c;
while((c = getc(stdin)) != EOF)
if(putc(c, stdout) == EOF) // putc() returns EOF when error ocurrs
err_sys("output error");
if(ferror(stdin))
err_sys("input error");
exit(0);
}
1.6 程序和进程
1. 程序
程序是一个存储在磁盘上某个目录中的可执行文件,内核使用exec函数将程序度如内存,并执行程序。
2. 进程和进程ID
程序的执行实例被称为进程,每一个进程都有一个唯一的数字标识符,称为进程ID。进程ID是一个非负整数。
Page9 实例
#include "apue.h"
int main(void)
{
printf("hello world from process ID %ld\n", (long)getpid());
exit(0);
}
说明:使用getpid函数获取进程ID。
3. 进程控制
进程控制的函数主要有fork、exec、和waitpid。
Page9 实例
#include "apue.h"
#include <sys/wait.h>
int main(void)
{
char buf[MAXLINE];
pid_t pid;
int status;
printf("%% ");
while(fgets(buf, MAXLINE, stdin) != NULL)
{
if(buf[strlen(buf) - 1] == '\n')
buf[strlen (buf) - 1] = 0;
if((pid = fork()) < 0)
{
err_sys("fork error");
}
else if (pid == 0) /* child */
{
execlp(buf, buf, (char *)0);
err_ret("couldn't execute: %s", buf);
exit(127);
}
/* parent */
if((pid = waitpid(pid, &status, 0)) < 0)
err_sys("waitpid error");
printf("%% ");
}
exit(0);
}
说明:
- fgets返回的每一行都以换行符结束,后随一个null字节。由于execlp函数要求的参数是以null结束的,所以要将换行符替换为null。
- fork创建了一个新进程。新进程调用进程的一个副本,称调用进程为父进程,新创建的进程为子进程。fork对父进程返回新的子进程的进程ID,对子进程则返回0.因为fork创建了一个新进程,所以它被调用一次(由父进程),但返回两次(分别在父进程和在子进程中)。
- 在子进程中,调用execlp执行从标准输入读入的命令。这就用新的程序文件替换了子进程原先执行的程序文件。fork和跟随其后的exec两者的组合就是某些操作系统所称的产生(spawn)一个新进程。在UNIX系统中,者两部分分离成两个独立的函数。
- 子进程调用execlp执行新程序文件,而父进程希望等待子进程终止,这是通过调用waitpid实现的,其参数指定要等待的进程(即pid参数是子进程ID)。waitpid函数返回子进程的终止状态(status变量)。
- 该程序的最主要限制是不能向所执行的命令传递参数。
4. 线程和线程ID
通常,一个进程只有一个控制线程(thread)--某一时刻执行的一组及其指令。如果有多个控制线程分别作用与进程的不同部分,可以更容易解决问题,同时可以充分利用多处理器系统的并行能力。
一个进程内的所有线程共享同一地址空间、文件描述符、栈以及与进程相关的属性。因为它们能访问同一存储区,所以各线程在访问贡献数据时需要采取同步措施避免不一致性。
与进程相同,线程也用ID标识。但是,线程ID旨在它所属的进程内起作用。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。