UNIX环境高级编程学习笔记——第一章UNIX基础知识

第一章 UNIX基础知识

1、文件和目录

1.1 文件系统

UNIX文件系统是目录和文件组成的一种层次结构,目录的起点称为根(root),其名字是一个字符/

1.2 文件属性

文件属性是指文件类型(是普通文件还是目录)、文件大小、文件所有者、文件权限以及文件最后的修改时间等。Statfstat函数返回包含所有文件属性的一个信息结构。

struct stat {
	unsigned long	st_dev;		/* Device.  */
	unsigned long	st_ino;		/* File serial number.  */
	unsigned int	st_mode;	/* File mode.  */
	unsigned int	st_nlink;	/* Link count.  */
	unsigned int	st_uid;		/* User ID of the file's owner.  */
	unsigned int	st_gid;		/* Group ID of the file's group. */
	unsigned long	st_rdev;	/* Device number, if device.  */
	unsigned long	__pad1;
	long		        	st_size;	/* Size of file, in bytes.  */
	int		        	st_blksize;	/* Optimal block size for I/O.  */
	int		        	__pad2;
	long		        	st_blocks;	/* Number 512-byte blocks allocated. */
	long		        	st_atime;	/* Time of last access.  */
	unsigned long    	st_atime_nsec;
	long			st_mtime;	/* Time of last modification.  */
	unsigned long	st_mtime_nsec;
	long			st_ctime;	/* Time of last status change.  */
	unsigned long	st_ctime_nsec;
	unsigned int	__unused4;
	unsigned int	__unused5;
};

1.3 .和..

创建新目录时会自动创建两个文件名:.(称为点)..(称为点点),点指当前目录,点点指父目录,在最高层次的根目录中,点和点点相同。

1.4 路径名

以斜线开头的路径名称为绝对路径名,否则称为相对路径名。

1.5显示文件名:

自己实现ls命令

//myls.c
#include "apue.h"
#include <dirent.h>

int main( int argc,char *argv[] )
{
    DIR *dp;
    struct dirent *dirp;
    
    if ( argc != 2 )
	//printf("usage: myls directory_name");
 	err_quit("usage: myls directory_name");

    if ( (dp = opendir(argv[1])) == NULL )
	printf("can't open %s",argv[1]);

    while ( (dirp = readdir(dp)) != NULL )
	printf("%s\n",dirp->d_name);
    closedir(dp);
    exit(0);
}

shell中运行gcc myls.c -o myls

运行结果:

UNIX环境高级编程学习笔记——第一章UNIX基础知识_第1张图片


myls没有把...过滤掉,而系统的ls...过滤掉了,ls命令在打印目录项前一般按照字母顺序将名字排序。

1.6工作目录

每个进程都有一个工作目录,有时也称其为当前工作目录,所有相对路径名都从工作目录开始解释的,chdir函数更改其工作目录。

2、输入和输出

2.1 文件描述符

文件描述符通常是一个小的非负整数,内核用它标识一个特定进程正在访问的文件。当内核打开一个已有文件或者创建一个新文件时,它返回一个文件描述符。

2.2 open等函数

函数openreadwritelseek以及close提供了不用缓冲的I/O

3、程序和进程

3.1 进程ID

      程序是存放在磁盘上、处于某个目录中的一个可执行文件;程序的执行实例被称为进程。Unix系统确保每个进程都有一个唯一的数字标识符,被称为进程ID,进程ID总是一个非负整数。

//mygetpid.c
//获得进程ID号
#include "apue.h"
int main(void)
{
    printf("hello world form process ID %d\n",getpid());
    exit(0);
}

运行结果:


3.2 进程控制

有三个用于进程控制的主要函数:forkexecwaitpid

自行简化实现一个简单的shell程序

#include "apue.h"
#include <sys/wait.h>

int main(void)
{
    char buf[MAXLINE];//form "apue.h"
    pid_t pid;
    int status;

    printf("%% ");//print '%'
    while ( fgets(buf,MAXLINE,stdin) != NULL )
    {
	if ( buf[strlen(buf) - 1] == '\n')
	    buf[strlen(buf) - 1] = 0;
        if ( (pid = fork()) < 0)
	{
	     printf("fork error");
	    //err_sys("fork error");
	}
	else if (pid == 0)
	{//child
	    execlp(buf,buf,(char *)0);
	    printf("couldn't execute:%s",buf);
	    //err_ret("couldn't execute:%s,buf");
	    exit(127);
	}
	//parent
	if ((pid = waitpid(pid,&status,0)) < 0)
   	    printf("waitpid error");//err_sys("waitpid error");
	printf("%% ");//print '%'
    }
    exit(0);
}
运行结果:

UNIX环境高级编程学习笔记——第一章UNIX基础知识_第2张图片


说明:

>>fgets返回的每一行都以换行符终止,后随一个null字节,故用标准C函数strlen计算字符串长度,然后用以个null字节替换换行符,因为execlp函数要求参数以null而不是以换行符结束;

>>调用fork()创建一个新进程。新进程是调用进程的复制品,我们称调用进程为父进程,新创建的进程为子进程。fork向父进程返回新子进程的进程ID,对子进程则返回0。因为fork创建一个新进程,所以说它被调用一次(由父进程),但是返回两次。

>>在子进程中,调用execlp以执行从标准输入读入的命令。

>>子进程调用execlp执行新程序文件,而父进程希望等待子进程终止,这一要求由调用waitpid实现,其参数指定要等待的进程。

4、出错处理

      UNIX函数出错时,常常返回一个负值,而且整形变量errno通常呗设置含有附加信息的一个值。文件<errno.h>中定义了符号errno以及可以赋予它的各种常量,这些常量都是以E开头。


5、用户标识

5.1用户ID

      口令文件登录项中的用户ID是个数值,它向系统标识各个不同的用户。用户ID0的用户为根用户或者超级用户,通过getuid()获得。

//mygetpid.c
#include "apue.h"

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

运行结果:


5.2ID

      口令文件登录项也包括用户的组ID,它是一个数值,通过getgid()获得。

//mygetuid.c
#include "apue.h"

int main(void)
{
    printf("uid = %d,gid = %d\n",getuid(),getgid());
    exit(0);
}
运行结果:


6、信号

      信号是通知进程已发生某种情况的一种技术。终端键盘上有两种产生信号的方法,分别是中断键(DeleteCtrl+c)和退出键(Ctrl+\)


7、时间值

      长期以来,UNIX系统一直使用两种不同的时间值

7.1日历时间

      该值是自19701100:00:00以来国际标准时间(UTC)所经过的秒数积累计值。基本数据类型是time_t

7.2进程时间

      这也被称为CPU时间,用以度量进程使用的中央处理机资源。基本数据类型是clock_t

8、系统调用和库函数

      各种版本的UNIX实现都提供定义明确、数量有限、可直接进入内核的入口点,这些入口点被称为系统调用。虽然有些函数可能会调用一个或多个内核的系统调用,但是它们不是内核的入口点。例如,printf函数会调用write系统调用以输出一个字符串,但是函数strcpyatoi并不会使用任何系统调用。

==============================================================================

备注:关于#include "apue.h"头文件,是《UNIX环境高级编程》的作者自定义的头文件,如若要使用到它,需要做一翻工作。我参考的是:http://www.cnblogs.com/feiling/archive/2012/02/15/2353286.html

最近在读 Richard Stevens 的大作《UNIX环境高级编程》,相信很多初读此书的人都会与我一样遇到这个问题,编译书中的程序实例时会出现问题,提示 “错误:apue.h:没有那个文件或目录”。

apue.h 是作者自定义的一个头文件,并不是Unix/Linux系统自带的,此头文件包括了Unix程序所需的常用头文件及作者Richard自己写的出错处理函数。所以在默认情况下,gcc在编译时是读不到这个头文件的。

先在这个网站 http://www.apuebook.com/src.tar.gz 下载tar.gz格式的源码包,然后解压至某个目录,比如说/home/godsoul/下,然后进入目录apue.2e,把文件 Make.defines.linux 中的 WKDIR=/home/xxx/apue.2e 修改为 WKDIR=/home/godsoul/apue.2e ,然后再进入apue.2e目录下的std目录,打开linux.mk,将里面的nawk全部替换为awk,如果是用的vi/vim编辑器,可以使用这个 命令  :1.$s/nawk/awk/g (注意前面有冒号)
然后在此目录下运行make命令,即回到 /home/godsoul/apue.2e 目录在终端中输入 “./make” (不含引号)

然后把 /home/godsoul/apue.2e/inlcude 目录下的 apue.h 文件和位于 /home/godsoul/apue.2e/lib 目录下的 error.c 文件都复制到 /usr/include 目录下,apue.2e/lib/libapue.a 到/usr/lib/和 /usr/lib64下。注意复制这文件你需要有root权限。之所以要这样做,是因为gcc在链接头文件时会到 /usr/include 这个目录下寻找需要的头文件,若找不到则报错。

最终还要编辑一下复制过来的 apue.h 文件
在最后一行 #endif 前面添加一行 #include “error.c”

然后进入apue.2e/std 目录,编辑linux.mk。修改里面所有的nawk为awk。

这样就不会报错了。

====================================================================== ========















你可能感兴趣的:(编程,unix)