记录学习UNIX高级环境编程的过程。
萌新路过,大神勿扰!!!
之前学习一直认为要深究到底,其实深究到底并没有什么错,但要掌握方法。
在一开始就出现了类似“upue.h”这个头文件,这个头文件其实是作者自己定义的,当然可以直接用,但是如果非要全部搞懂,这其实是没必要也是不现实的,在看过相关的教学视频,比如李慧琴的系统编程,大概就明白如何学习这本书。man手册其实是关键,通过man手册学习比直接看书能够学到更多的知识。
在三册书中,第三册其实要比第一册更好懂,第一册需要扎实的网络基础,必须和一本网络教程书共同学习,比如《计算机网络:自顶而下》。
首先就是贯穿全书的apue.h,可以自己创建这样一个头文件,并且通过不断地学习,将使用到的头文件,创建的函数不断地添加进去,从而得到自己的apue.h函数。
通过学习,在程序中尽量使用自己定义的函数是一种很好的习惯。
;
UNIX基础知识
第一部分:文件和目录
shell部分在《linux shell脚本编程》中已经说得很明白了,直接看1-3中的“列出一个目录中的所有文件”这个程序。
例子中使用的函数,分别为err_quit、err_sys、printf、opendir、closedir、readdir
opendir函数
opendir -- open a directory,打开一个目录
头文件:
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
返回值是一个叫DIR的指针,暂时不管是什么。
;
readdir函数
readdir -- read a directory
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
;
closedir函数
closedir - close a directory
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
;
err_quit函数
作用是命令行没有文件或者多于一个的时候,使用此函数进行输出,暂时考虑用一条printf实现。
err_sys函数
这个函数用于输出错误信息,类似于printf函数,和printf函数是相同的效果。
https://blog.csdn.net/qq_33706673/article/details/84729447中有关于printf如何实现的方法。
#include <stdio.h>
#include <dirent.h>
void err_sys(const char *str, ...)
{
va_list var;
char c = 0;
unsigned int ui = 0;
int i = 0;
float f = 0;
double d = 0;
char *s = NULL;
va_start(var, str);
while ('\0' != *str)
{
if ('%' != *str)
{
printf("%c", *str);
str++;
continue;
}
else
{
switch (*(++str))
{
case 'c':
c = (char)va_arg(var, int);
printf("%c", c);
break;
case 'u':
ui = (unsigned int)va_arg(var, int);
printf("%u", ui);
break;
case 'd':
i = va_arg(var, int);
printf("%i", i);
break;
case 'f':
f = (float)va_arg(var, double);
printf("%f", f);
break;
case 'l':
if ('f' == *(str + 1))
{
d = va_arg(var, double);
printf("%lf", d);
str++;
}
break;
case 's':
s = (char *)va_arg(var, char *);
printf("%s", s);
break;
default:
printf("%c", *str);
break;
}
str++;
}
}
va_end(var);
}
apue.h文件(第一阶段)
/*头文件包含*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <dirent.h>
#include <sys/types.h>
/*函数声明*/
void err_quit(); /*文件打开失败*/
void err_sys(const char *str, ...); /*输出报错信息*/
/*函数原型*/
void err_quit()
{
printf("Usage: ls directory_name");
}
void err_sys(const char *str, ...)
{
va_list var;
char c = 0;
unsigned int ui = 0;
int i = 0;
float f = 0;
double d = 0;
char *s = NULL;
va_start(var, str);
while ('\0' != *str)
{
if ('%' != *str)
{
printf("%c", *str);
str++;
continue;
}
else
{
switch (*(++str))
{
case 'c':
c = (char)va_arg(var, int);
printf("%c", c);
break;
case 'u':
ui = (unsigned int)va_arg(var, int);
printf("%u", ui);
break;
case 'd':
i = va_arg(var, int);
printf("%i", i);
break;
case 'f':
f = (float)va_arg(var, double);
printf("%f", f);
break;
case 'l':
if ('f' == *(str + 1))
{
d = va_arg(var, double);
printf("%lf", d);
str++;
}
break;
case 's':
s = (char *)va_arg(var, char *);
printf("%s", s);
break;
default:
printf("%c", *str);
break;
}
str++;
}
}
va_end(var);
}
程序分析
1、程序功能是从命令行中读取一个文件目录然后再陈列其中的文件
if (argc != 2)
err_sys("Usage: ls directory_name");
2、dirent.h头文件用于使用opendir和readdir函数
打开命令行中的第一个文件夹,鉴于opendir返回值是dp,且还要判断是否成功打开,否则报错。
打开:
DIR *dp;
opendir(argv[1]) /*打开*/
dp = opendir(argv[1]) /*返回值保存在一个DIR指针中*/
(dp = open[argv[1]] == NULL) /*判断是否打开*/
/*综上*/
if ((dp = opendir(argv[1])) == NULL)
err_sys("Can't open %s", argv[1]);
读:opendir返回一个struct dirent
struct dirent *dirp; /*保存readdir的返回值*/
readdir(dp); /*读刚才打开的文件*/
dirp = readdir(dp); /*保存readdir的返回值*/
(dirp = readdir(dp)) != NULL; /*判断dirp的值从而判断是否读*/
/*总*/
while ((dirp = readdir(dp)) != NULL)
{
printf("%s\n", dirp->d_name);
}
完整的list.c程序,用于列出目录中的名字:
#include "apue.h"
int main(int argc, char *argv[])
{
DIR *dp;
struct dirent *dirp;
if (argc != 2)
err_sys("Usage: ls directory_name");
if ((dp = opendir(argv[1])) == NULL)
err_sys("Can't open %s", argv[1]);
while ((dirp = readdir(dp)) != NULL)
{
printf("%s\n", dirp->d_name);
}
closedir(dp);
exit(0);
}
程序输出:
第二部分:输入和输出
文件描述符是一个小的非负整数,内核打开或创建一个新文件都会返回一个文件描述符。
读、写文件可以运用这个文件描述符。
运行一个程序的时候,shell会打开三个文件描述符,分别是标准输入、标准输出和标准错误。
标准I/O函数为那些不带缓冲的I/O提供了一个带缓冲的接口,使用标准I/O无需担心如何选取最佳的缓冲区大小。
函数
open,read,write,lseek、close提供了不带缓冲的I/O
标准输入和标准输出的文件描述符:STDIN_FILENO、STDOUT_FILENO
;
read函数,用man 2 read可查询相关信息:
read - read from a file descriptor
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
--- ssize_t -> signed integer data types
;
write函数,用man 2 write查询
write - write to a file descriptor
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
--- ssize_t -> signed integer data types
;
fgetc, fgets, getc, getchar, ungetc - input of characters and strings
#include <stdio.h>
int getc(FILE *stream);
;
fputc, fputs, putc, putchar, puts - output of characters and strings
#include <stdio.h>
int fputc(int c, FILE *stream);
;
clearerr, feof, ferror, fileno - check and reset stream status
#include <stdio.h>
int ferror(FILE *stream);
;
1.4
#include "apue.h"
#define BUFFSIZE 4096
int main(void)
{
int n;
char buf[BUFFSIZE];
while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
if (write(STDOUT_FILENO, buf, n) != n)
err_sys("write error");
if(n < 0)
err_sys("read error");
exit(EXIT_FAILURE);
}
1.5
#include "apue.h"
int main(void)
{
int c;
while ((c = getc(stdin)) != EOF)
if ((putc(c, stdout)) == EOF)
err_sys("output error");
if(ferror(stdin))
err_sys("input error");
exit(EXIT_FAILURE);
}
第三部分:程序和进程
1、程序
程序是存储在磁盘上某个目录的可执行文件,内核使用exec函数(共7个),将程序读入内存并执行程序
2、进程和进程ID
程序的执行实例称为进程,UNIX的每一个进程有唯一的数字标识符,称为进程ID。
打印进程,使用getpid函数。
getpid, getppid - get process identification
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
#include "apue.h"
int main(void)
{
printf("hello world from process ID %ld\n",(long)getpid);
exit(EXIT_FAILURE);
}
3、进程控制
三个用于进程的函数:fork,exec,waitpod
时间值
UNIX系统用过两种不同的时间值。
1、日历时间
2、进程时间
--- 也称为CPU时间,用以度量进程使用的*处理器资源
--- 系统基本数据类型clock_t保存这种类型
UNIX系统为一个进程维护了3个进程时间值:时钟时间、用户CPU时间、系统CPU时间。
时钟时间又称为墙上时钟时间,它是度量进程运行的时间总量,其值与系统中同时运行的进程数有关。
用户CPU时间是指执行用户指令所用的时间量。系统CPU时间是为该进程执行内核程序所经历的时间。
要取得任一进程的时钟时间、用户时间和系统时间只要执行命令time(1),参数是度量其执行时间的命令。
;