第1章 Unix系统编程概述

一 什么是系统编程

1.操作系统的职责

内存空间用来存放程序和数据,所有的程序必须在内存空间中才能运行。用来容纳操作系统的内存空间叫做系统空间,容纳应用程序的内存空间叫做用户空间

二 理解系统编程

内核提供服务以便使系统程序可以直接访问系统资源

1.系统资源

  • 处理器:内核安排一个程序何时开始执行,何时暂停、恢复、终止执行
  • 输入输出(I/O):程序中所有输入输出都必须流经内核
  • 进程管理
  • 内存:内核的职责之一是内存管理,包括分配与回收
  • 设备
  • 计时器
  • 进程间通信
  • 网络

三 从用户角度理解Unix

1.登录

在登录过程中,当用户名和密码通过验证后,系统会启动一个叫做shell的进程,然后把用户交给这个进程,由这个进程处理用户请求。每个用户都有属于自己的shell进程。

2.目录操作

(1)目录树

第1章 Unix系统编程概述

(2)目录操作命令

  • ls:列出目录内容
  • cd:改变当前目录,只输入cd会立即回到用户主目录
  • pwd:显示当前目录全路径
  • mkdir,rmdir:新建,删除目录

3.文件操作

(1)文件操作命令

  • cat,more,less,pg:查看文件内容
  • cp:文件复制
  • rm:文件删除
  • mv:重命名或移动文件

四 从系统角度来看Unix

Unix系统编程中3个重要的方面:通信、协作、网络访问

四 动手实践

1.more的第一个实现版本

/* more01.c - version 0.1 of more01
 * read and print 24 lines then pause for a few special commands
 */
 
#include<stdio.h>
#define PAGELEN 24
#define LINELEN 512
void do_more(FILE*);
int see_more();
int main(int argc,char* argv[])
{
	FILE* fp;
	if(argc==1)
		do_more(stdin);
	else
		while(--argc)
			if((fp=fopen(*++argv,"r"))!=NULL)
			{
				do_more(fp);
				fclose(fp);
			}
			else
				exit(1);
	return 0;
}

void do_more(FILE* fp)
{
	char line[LINELEN];
	int num_of_lines=0;
	int see_more(),replay;
	while(fgets(line,LINELEN,fp))
	{
		if(num_of_lines==PAGELEN)
		{
			replay=see_more();
			if(replay==0)
				break;
			num_of_lines-=replay;
		}
		if(fputs(line,stdout)==EOF)
			exit(1);
		num_of_lines++;
	}
}

int see_more()
{
	int c;
	printf("\033[7m more?\033[m");
	while((c=getchar())!=EOF)
	{
		if(c=='q')
			return 0;           /*不看*/
		if(c==' ')
			return PAGELEN;     /*下一页*/
		if(c=='\n')
			return 1;           /*下一行*/
	}
	return 0;
}

主函数中判断应从文件还是标准输入中获取数据,并打开相应的数据源,然后调用do_more函数,do_more函数将数据显示在显示器上,满一屏后,调用see_more函数接收用户的输入以决定下一步动作。运行结果如下:
第1章 Unix系统编程概述
第1章 Unix系统编程概述

问题:当屏幕上文字上滚时,more?也会随之上滚,这并不是所需要的。而且当按空格键或按q后,如果不按回车键,那程序什么也不会做。

管道是一种通信机制,通常用于进程间的通信(也可通过socket进行网络通信),它表现出来的形式将前面每一个进程的输出(stdout)直接作为下一个进程的输入(stdin)。管道命令使用|作为界定符号。
当运行下列命令时,结果如下

ls /bin |more

第1章 Unix系统编程概述

而运行刚才的more01时,则有如下结果

ls /bin |./more01

第1章 Unix系统编程概述

可见其并未如more那样,这里管道符将more01的标准输入重定向到ls的标准输出,而more01的输入通过getchar()函数读入。重定向后,more01将从同一个数据流中读用户的输入。
解决此问题的方法是,从标准输入中读入要分页的数据,直接从键盘读用户的输入。
/dev/tty处有键盘和显示器的设备描述文件,向这个文件写,相当于显示在用户的屏幕上,读则相当于从键盘获取用户的输入。more命令有两个输入:程序的标准输入是ls的输出,将其分页显示到屏幕上,当more需要用户输入时,可以从/dev/tty得到数据。
改进more01.c为more02.c

/* more02.c - version 0.2 of more01
 * read and print 24 lines then pause for a few special commands
 * feature of version 0.2:reads from /dev/tty for commands
 */
 
#include<stdio.h>
#define PAGELEN 24
#define LINELEN 512
void do_more(FILE*);
int see_more(FILE*);
int main(int argc,char* argv[])
{
	FILE* fp;
	if(argc==1)
		do_more(stdin);
	else
		while(--argc)
			if((fp=fopen(*++argv,"r"))!=NULL)
			{
				do_more(fp);
				fclose(fp);
			}
			else
				exit(1);
	return 0;
}

void do_more(FILE* fp)
{
	char line[LINELEN];
	int num_of_lines=0;
	int see_more(FILE*),replay;
	FILE* fp_tty;
	fp_tty=fopen("/dev/tty","r");
	if(fp_tty==NULL)
		exit(1);
	while(fgets(line,LINELEN,fp))
	{
		if(num_of_lines==PAGELEN)
		{
			replay=see_more(fp_tty);
			if(replay==0)
				break;
			num_of_lines-=replay;
		}
		if(fputs(line,stdout)==EOF)
			exit(1);
		num_of_lines++;
	}
}

int see_more(FILE* cmd)
{
	int c;
	printf("\033[7m more?\033[m");
	while((c=getc(cmd))!=EOF)   /*从文件流中读取下一个字符*/
	{
		if(c=='q')
			return 0;           /*不看*/
		if(c==' ')
			return PAGELEN;     /*下一页*/
		if(c=='\n')
			return 1;           /*下一行*/
	}
	return 0;
}

程序编译运行结果如下:
第1章 Unix系统编程概述
第1章 Unix系统编程概述

more02还需进一步完善,以使得不需要额外的回车,并且输入的字符也不会回显。
用户操作的终端有很多参数,可以调整参数使得用户输入的字符被立即送到程序,而不用等待回车,还可以使输入的字符不回显。