当前位置:网站首页>UNIX environment advanced programming - the first chapter

UNIX environment advanced programming - the first chapter

2022-08-06 07:51:20heart flower

1.UNIX体系结构:

严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境.我们通常将这种软件称为内核,因为它相对较小,而且位于环境的核心.图1-1显示了UNIX系统的体系结构.
在这里插入图片描述

内核的接口被称为系统调用(system call).公用函数库构建在系统调用接口之上,应用程序既可以使用公用函数库,也可以使用系统调用.shelltime a special application,为运行其他应用程序提供了一个接口.
从广义上说,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特性.Other software mentioned here includes system utilities(system utility),应用程序,shell以及公用函数库等.
例如,Linux时GNU操作系统使用的内核.一些人将这种操作系统称为GNU/Linux操作系统,但是,It is more common to simply call itLinux.Although this expression is not strictly correct,但鉴于“操作系统”double meaning of the word,It's still understandable.

2.登录

(1)登录名:
用户在登录UNIX系统时,先键入登录名,然后键入口令.系统在其口令文件(通常是/etc/passwd文件)中查看登录名.The login entry in the password file is7个以冒号分隔的字段组成,依次是:登录名,加密口令,数字用户ID(205),数字组ID(105),注释字段,起始目录(/home/sar)以及shell程序(/bin/ksh).
例:sar: x:205:105:Stephen Rago:/home/sar:/bin/ksh
目前,All systems have moved encrypted passwords to another file.第6章将说明这种文件以及访问它们的函数.
(2)shell:
用户登陆后,系统通常先显示一些系统信息,然后用户向shell程序键入命令.(当用户登录时,某些系统启动一个视窗管理程序,但最终总会有一个shell程序运行在一个视窗中).shell是一个命令行解释器,它读取用户输入,然后执行命令.shell的用户输入通常来自于终端,sometimes from a file(称为shell脚本),图1-2总结了UNIX系统中常见的shell.
在这里插入图片描述

The system learns from the last field of the corresponding user login item in the password file which one should be executed for the login usershell.
自V7以来,由由Steve Bourne在贝尔实验室开发的Bourne shell得到了广泛应用,almost every existingUNIX系统都提供Bourne shell,Its control flow structure is similar toAlgol 68.
C shell是由Bill Joy在伯克利开发的,所有BSD版本都提供这种shell.另外,AT&T的System V/386 R3.2和System V R4(SVR4)也提供了C shell(The next chapter will discuss these different versions ofUNIXSystem to do more).
C shell是在第6版shell而非Bourne shell的基础上构造的,Its control flow is similar toC语言,它支持Bourne shell没有的一些特色功能,例如作业控制,历史机制以及命令行编辑等.
Korn shell是在Bourne shell的后继者,它首先在SVR4中提供.
Korn shell是由贝尔实验室的David Korn开发的,在大多数UNIXsystem running on,但在SVR4之前,Usually it needs to be purchased separately,So there are no other twoshell流行.它与Bourne shell向上兼容,and hasC shellSome widely used features,Including job control and command line editing, etc..
Bourne-again shell是GNU shell,所有Linux系统都提供这种shell.它的设计遵循POSIX标准,also retains theBourne shell的兼容性.它支持C shell和Korn shellSpecial features of both.
TENEX C shell是C shell的加强版本.它从TENEXThe operating system borrows a lot of special,例如命令完备.
POSIX 1003.2标准对shell进行了标准化.这项规范基于Korn shell和Bourne shell的特性.

3.文件和目录:

(1)文件系统
Unix文件系统是目录和文件的一种层次结构,The starting point of everything is called the root(root)的目录,这个目录的名称是一个字符"/".
目录是一个包含目录项的文件.在逻辑上,可以认为每个目录项都包含一个文件名,Also contains information describing the properties of the file.文件属性是指文件类型,文件大小,文件所有者,File permissions and the last modification time of the file, etc..stat和fstat函数返回包含所有文件属性的一个信息结构.第4Chapter will detail the various attributes of the file.
The logical attempt of directory entries is different from how they are actually stored on disk.UNIXMost implementations of filesystems do not store attributes in directory entries,This is because when a file has multiple hard links,很难保持多个属性副本之间的同步.这一点将在第4Chapter discusses hard links more clearly.
(2)文件名
Each name in the directory is the file name.只有斜线/and the null character cannot appear in filenames.Slashes are used to separate the individual filenames that make up the path,The null character is used to terminate a pathname.尽管如此,It is good practice to use only a subset of commonly used typographic characters for name characters.
Two filenames are automatically created when a new directory is created:.(称为点),.(point).点指向当前目录,点点指向父目录.在最高层次的根目录中,点点与点相同.
(3)路径名
A sequence of one or more filenames separated by slashes forms the pathname,以斜线开头的路径名称称为绝对路径名,Otherwise known as relative path names.相对路径名指向相对于当前目录的文件.文件系统根的名字(/)是一个特殊的绝对路径名,it does not contain the filename.
(4)工作目录
每个进程都有一个工作目录,Sometimes it is the current working directory.All relative pathnames are interpreted starting from the working directory.进程可以用chdir函数更改其工作目录.
例如,相对路径名doc/memo/joe指的是当前工作目录中的doc目录中的memo目录中的文件joe.As can be seen from the path name,doc和memoshould be a directory,but can't distinguishjoe是文件还是目录.

4.输入输出

(1)文件描述符
The file descriptor is usually a small non-negative integer,内核用以标识一个特定进程正在访问的文件.当内核打开一个现有文件或创建一个新文件时,它都返回一个文件描述符.在读,写文件时,Can use the file number of seconds.
(2)标准输入,标准输出和标准错误
按惯例,每当运行一个新程序时,所有的shell都为其打开3个文件描述符,即标准输入,标准输出以及标准错误.如果不做特殊处理,例如就像简单的命令ls,则这3个描述符都链接向终端.大多数shell都提供一种方法,make any or all of this3every descriptor can be redirected to a file,例如:
ls > file.list
执行ls命令,其标准输出重新定向到名为file.list的文件.
(3)不带缓冲的IO
函数open,read,write,lseek以及close提供了不带缓冲的IO.These functions are made the file descriptor.
实例:程序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(0);
}

①头文件<unistd.h>(包含与apue.h)And two constantsSTDIN_FILENO和STDOUT_FILENO是POSIX标准的一部分.头文件<unistd.h>包含了很多UNIX系统服务的函数原型,例如程序1-4中的read和write.
②两个常量STDIN_FILENO和STDOUT_FILENO定义在<unistd.h>头文件中,它们指定了标准输入和标准输出的文件描述符.在POSIX标准中,它们的值分别是0和1.
③3.9将详细讨论BUFFSIZE常量,show how its various values ​​will affect the efficiency of the program.This program can always copy eitherUNIX普通文件.
④read函数返回读取的字节数,此值用作要写的字节数.当到达输入文件的尾端时,read返回0,程序停止执行.If any one is wrong,read返回-1.
⑤If the program is compiled to the standard namea.out文件,以下列方式执行:

./a.out > data

then the standard input when the terminal,Standard output is redirected to a filedata,标准错误也是终端.If this output file does not exist,则shell会创建它.The program copies the lines typed by the user to standard output,type end-of-file(Ctrl+D)时,This copy will be terminated.
If you execute the program in the following way:

./a.out < infile > outfile

会将名为infileCopy the contents of the file to a file namedoutfile的文件中.
(4)标准IO
标准IO函数为那些不带缓冲的IOfunction provides a buffered interface.使用标准IO函数无需担心如何选取最佳的缓冲区大小,如代码1-4的BUFFSIZEthe size of the constant.使用标准IO函数还简化了对输入行的处理.例如,fgets函数读取一个完整的行,而read函数读取指定字节数.
我们最熟悉的标准IO函数是printf.在调用printf的程序中,总是包含<stdio.h>,This header file includes all standardIO函数的原型

实例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(0);
}

函数getc一次读取一个字符,然后函数putc将此字符写到标准输出.读到输入的最后一个字节时,getc返回常量EOF(该常量定义在<stdio.h>).标准IO常量stdin和stdout也在头文件<stdio.h>中定义,They represent standard input and standard output, respectively.

5.程序和进程

(1)程序
A program is an executable file stored in a directory on disk.内核使用exec函数,将程序读入内存,并执行程序.
(2)进程和进程ID
程序的执行实例被称为进程.This term is used on almost every page of this book.Some operating systems use tasks to represent programs that are being executed.
UNIXThe system ensures that each process has a unique numerical identifier,称为进程ID.进程ID总是一个非负整数.
实例1-6:

#include "apue.h"

int
main(void)
{
    
	printf("hello world from process ID %d\n",(long)getpid());
	exit(0);
}

如果将该程序编译成a.out,然后执行,then the resulting output may be different each time
此程序运行时,它调用函数getpid得到其进程ID.我们将会在后面看到,getpid返回一个pid_t数据类型.我们不知道它的大小,Just know that is a standard will ensure it can be stored in a long integer.因为我们必须在printf函数中指定需要打印的每一个变量的大小,So we have to cast its value to the largest data type it can possibly use.虽然大多数进程IDcan be represented by integer,但用长整型可以提高可移植性.
(3)进程控制
有3个用于进程控制的主要函数:fork,exec和waitpid.(exec函数有7medium variant,but are often referred to collectively asexec函数).
实例1-7:

#include "apue.h"
#include <sys/wait.h>
int
main(void)
{
    
	char buf[MAXLINE]; //MAXLINE定义与apue.h
	pid_t pid;
	int status;
	pritnf("%%");
	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){
    
			execlp(buf,buf,(char *)0);
			err_ret("couldn't execute: %s\n",buf);
			exit(127);	
		}
		if((pid=waitpid(pid,&status,0))<0)
			err_sys("waitpid error");
		printf("%%");
	}
	exit(0);
}

在这个30行的程序中,有很多功能需要考虑
①用标准IO函数fgets从标准输入一次读取一行.当type end-of-file作为行的第一个字符时,fgets返回一个null指针,于是循环停止,进程也就终止.
②因为fgets返回的每一行都以换行符终止,后随一个null字节,So use the standardC函数strlenCalculate the length of the string,然后用一个null字节替换换行符.这样做是因为execlp函数要求的参数是以nullend rather than end with a newline.
③调用fork创建一个新进程.forkA process that returns a new child process to the parent processID(一个非负整数),对子进程则返回0.
④在子进程中,调用execlpConsistent commands read from standard input.This replaces the program file originally executed by the child process with the new program file.fork和跟随其后的exec两者的组合就是某些操作系统所称的产生(spawn)一个新进程.
⑤子进程调用execlp执行新程序文件,而父进程希望等待子进程终止,这是通过调用waitpid实现的,其参数指定要等待的进程.waitpid函数返回子进程的终止状态.
(4)线程和线程ID
通常,一个进程只有一个控制线程(thread),That is, a set of machine instructions executed at a certain time.对于某些问题,If there are multiple threads of control acting on different parts of it,then it's much easier to solve.另外,Multiple threads of control can also take full advantage of the parallelism of a multiprocessor system.
一个进程内的所有线程共享同一地址空间,file seconds,栈以及与进程相关的属性.因为它们能访问同一存储区,所以各线程在访问共享数据时需要采取同步措施以避免不一致性.
与进程相同,线程也用ID标识.但是,线程ID只在它所属的进程内起作用.一个进程中的线程ID在另一个进程中没有意义.When a particular thread is processed in an incoming city,We can use this threadID引用它.

6.出错处理

当UNIX系统函数出错时,通常会返回一个负值,而且整形变量errno通常被设置为具有特定信息的值.例如,open函数如果成功执行则返回一个非负文件描述符,如出错则返回-1.在open出错时,有大约15中不同的errno值(文件不存在,权限问题等).And some functions use another convention for errors instead of return values.例如,Most functions that return pointers to objects,On error will return anull指针.
文件<errno.h>中定义了errno以及可以赋予它的各种常量.这些常量都以字符E开头.
POSIX和ISO C将errno定义为一个符号,it expands to a modifiable integer lvalue.它可以是一个包含出错编号的整数,也可以是一个返回出错编号指针的函数.
对于errnoTwo rules to be aware of.第一条规则:如果没有出错,其值不会被例程清除.因此,仅当函数的返回值指明出错时,才检验其值.第二条规则是:任何函数都不会将errno值设置为0,而且在<errno.h>中定义的所有常量都不为0.
C标准定义了两个函数,它们用于打印出错信息.

#include<string.h>
char * strerror(int errnum);

strerror函数将errnum(通常就是errno值)映射为一个出错消息字符串,并且返回此字符串的指针.
perror函数基于errno的当前值,generate an error message on standard error,然后返回.

#include<stdio.h>
void perror(const char *msg);

它首先输出由msg指向的字符串,然后是一个冒号,一个空格,接着是对应于erro值的出错消息,最后是一个换行符.
实例1-8:

#include "apue.h"
#include <errno.h>

int main(int argc,char *argv[])
{
    
	fprintf(stderr,"EACESS:%s\n",strerror(EACCESS));
	errno=ENOENT;
	perror(argv[0]);
	exit(0);
}

我们将程序名(argv[0],Its value is the executable file name,That is the name of this file)作为参数传递给perror,这是一个标准的UNIX惯例.
出错恢复:
可将在<errno.h>Defined in a variety of error is divided into two categories:lethal and non-lethal.对于致命性错误,无法执行恢复动作.The most you can do is print an error message on the user's screen or write an error message to a log file,然后退出.对于非致命性的错误,有时可以较妥善地进行处理.
与资源相关的非致命性出错包括:EAGAIN,ENFILE,ENOBUFS,ENOLCK,ENOSPC,EWOULDBLOCK,有时ENOMEM也是非致命性出错.当EBUSY指明共享资源正在使用时,也可将它作为非致命性出错处理.当EINTRWhen interrupting a slow system call,可将它作为非致命性出错处理.

7.用户标识

(1)用户ID
The user in the login entry of the password fileID(user ID)是一个数值,它向系统标识各个不同的用户.系统管理员在确定一个用户的登录名的同时,确定其用户ID.用户不能更改其用户ID.通常每个用户有一个唯一的用户ID.The following describes how the kernel uses the userIDto verify that the user has permission to perform certain operations.
用户ID为0of users are follow users(root)或超级用户(superuser).在口令文件中,There is usually a login item,Its login isroot,We call such user privileges superuser privileges.我们将在第4章中看到,If a process has superuser privileges,then most file permission checks are not in progress.Some operating system features are only available to superusers.
(2)组ID
The password file login entry page includes the user's groupID,它是一个数值.组IDAlso assigned by the system administrator when specifying the user login name.一般来说,There are multiple login entries with the same group in the password fileID.Groups are used to group groups into projects or departments.这种机制允许同组的各个成员之间共享资源.
group file maps group names to numeric groupsID.Group files are usually/etc/group
users using numeric valuesIDand groups of valuesIDSetting permissions is historically formed.For every file on the disk,The file system stores the user of the file ownerID和组ID.To store these two values ​​just4个字节.If you use the fullASCIILogin and group names,requires more disk space.另外,during inspection of permissions,Comparing strings is more time consuming than comparing integers.
但是对于用户而言,It is more convenient to use names than numbers,So the password file contains the login and userID之间的映射关系,The group file contains the group name and groupID之间的映射关系.
实例1-9:

#include "apue.h"
int
main(void)
{
    
	printf("uid=%d,gid=%d\n",getuid(),getgid());
	exit(0);
}

该程序调用getuid和getgidTwo functions to get usersid和组id.
(3)附属组ID
In addition to specifying a group for a login in the password fileID外,大多数UNIXThe system version also allows a user to belong to other groups.This function is from4.2BSD开始的,It allows a user to belong to as many as16other groups.登录时,读文件/etc/group,Find the top16A record items can get the user's affiliate groupID.POSIXIt is required that the system should support at least8个附属组,In practice most systems support at least16个附属组.

8.信号

信号(signal)用于通知进程发生了某种情况.例如,若某一进程执行除法操作,其除数为0,则将名为SIGFPE(浮点异常)的信号发送给该进程.进程有以下3中处理信号的方式.
(1)忽略信号,Some signals indicate hardware anomalies,例如,除以0or accessing an unexpected location in the process address space, etc.,Because the consequences of these anomalies are uncertain,So this approach is not recommended
(2)按系统默认方式处理.对于除数为0,The system default is to kill the process
(3)提供一个函数,信号发生时调用该函数,This is called catching the signal.By providing self-written functions,we can know when the signal is generated,and handle it as expected.
In many cases, can produce a signal.There are two ways to generate a signal on the terminal keyboard,Known as the break key respectively,通常是Delete键或(Crtl+C)and escape key(通常是Crtl+),They are used to interrupt the currently running process.Another way to generate a signal is to callkill函数.Call this function in one process to send a signal to another process.
Of course there are some limitations:when sending a signal to a process,We must be the owner of that process or be the superuser.

实例1-10:

#include<apue.h>
#include<sys/wait.h>
static void sig_int(int);
int
main(void)
{
    
	char buf[MAXLINE];
	pid_t pid;
	int status;
	if (signal(SIGINT,sig_int)==SIG_ERR)
		err_sys("signal error");
	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){
    
			execlp(buf,buf,(char *)0);
			err_ret("couldn't execute:%s",buf);
			exit(127);
		}
		if ((pid=waitpid(pid,&status,0))<0)
			err_sys("waitpid error");
		printf("%%");
	}
	exit(0);
}
void sig_int(int signo)
{
    
	printf("interrnpt\n%%");
}

9.时间值

历史上,UNIXThe system has used two different time values.
(1)日历时间
The value is automatic UTC1970年1月1日00:00:00The cumulative number of seconds elapsed since this particular time.These time values ​​can be used to record the last modification time of the file, etc..
System basic data typestime_tfor saving this time value.
(2)进程时间.
Process time is also known asCPU时间,Central processing resources used to measure process usage.Process time in clock ticks.taken every second50,60,100The clock ticks.
System basic data typesclock_tsave this time value.
当度量一个进程的执行时间时,UNIXThe system is maintained for a process3process time value:时钟时间,用户CPU时间,系统CPU时间.
clock time aka wall clock time,It is the total amount of time the process has been running,Its value is related to the number of processes running at the same time on the system.
用户CPUTime is the amount of time used perform user instructions.系统CPUtime is the time elapsed to execute the kernel program for the process.例如,Whenever a process executes a system service,如read或write,Within the kernel executes the service time is included in the process of the systemCPU时间.
To get the clock time of any process,用户时间,System time is easy,只要执行命令time(1),Its argument is the command whose execution time is to be measured
timeThe output format of the command and theshell有关,The reason is that someshell并不运行/usr/bin/time,Instead, use a built-in function to measure how long the command took to run.

10.系统调用和库函数

All operating systems provide entry points for various services,由此程序向内核请求服务.各种版本的UNIX实现都提供良好定义,数量有限,直接进入内核的入口点,这些入口点被称为系统调用.
UNIX所使用的技术是为每个系统调用在标准CIn the library to set up a function has the same name.standard for user processesCcall sequence to call these functions,然后,The function calls the corresponding kernel service with the technology required by the useful system.例如,function can combine one or moreC参数送入通用寄存器,然后执行某个产生软中断进入内核的机器指令.From an application point of view,System calls can be thought of asC函数.
CThe common library functions,Although these functions may call one or more kernel system calls,但是它们并不是内核的入口点.例如,printf函数会调用writesystem call to output a string,但函数strcpy和atoidoes not apply to any kernel system calls.
从实现者的角度来看,There is a fundamental difference between system calls and library functions,但从用户角度来看,其区别并不重要.

原网站

版权声明
本文为[heart flower]所创,转载请带上原文链接,感谢
https://chowdera.com/2022/218/202208060723403420.html

随机推荐