《UNIX环境高级编程——APUE》
【附】小知识
1、同步、异步
概念:消息的通知机制
解释:涉及到IO通知机制;
同步,就是发起调用后,被调用者处理消息,必须等处理完才直接返回结果,没处理完之前是不返回的,调用者主动等待结果;
eg. 我去银行办理业务*,* 选择排队等,排到头了就办理。
异步,就是发起调用后,被调用者直接返回,但是并没有返回结果,等处理完消息后,通过状态、通知或者回调函数来通知调用者,调用者被动接收结果。
eg. 我去银行办理业务*,* 取一个小纸条上面有我的号码*,* 等到排到我这一号时由柜台的人通知我轮到我去办理业务。
2、阻塞与非阻塞
概念:程序等待调用结果时的状态
解释:涉及到CPU线程调度;
阻塞:就是调用结果返回之前,该执行线程会被挂起,不释放 CPU 执行权,线程不能做其它事情,只能等待,只有等到调用结果返回了,才能接着往下执行;
eg. 上面的那个例子*,* 不论是排队还是使用号码等待通知*,如果在这个等待的过程中,等待者除了等待消息之外不能做其它的事情,* 那么该机制就是阻塞的。
非阻塞:就是在没有获取调用结果时,不是一直等待,线程可以往下执行,如果是同步的,通过轮询的方式检查有没有调用结果返回,如果是异步的,会通知回调。
eg. 在银行办理这些业务的时候一边打打电话发发短信一边等待*,*这样的状态就是非阻塞的。
3、基本的 进程控制语言
l 用 fork 创建新进程;
l 用 exec 可以初始执行新的程序;
l exit 函数 和 wait 函数处理终止和等待终止。
exit(0) 正常退出
exit(1) 非正常退出
4、进程组、会话、前台进程组、后台进程组、终端控制
进程组与会话:
进程组是一组相关进程的集合,会话是一組相关进程组的集合。进程都有父进程, 父进程也有父进程, 这就形成了一个以init进程为根的家族树。除此以外,进程还有其他层次关系:进程、进程组、会话。进程组和会话在进程之前形成了两级的层次:进程组是一组相关进程的集合,会话是一组相关进程组的集合。
这样说来,一个进程会有如下ID:
l PID:进程的唯一标识。对于多线程的进程而言所有线程调用getpid()函数会返回相同值。
l PGID:进程组ID。每个进程都会有进程组ID,表示该进程所属的进程组。默认情况下新创建的进程会继承父进程的进程组ID。
l SID:会话ID。每个进程也都有会话ID。默认情况下,新创建的进程会继承父进程的会话ID
会话:
由于Linux是多用户多任务的分时系统,所以必须要支持多个用户同时使用一个操作系统。当一个用户登录一次系统就形成一次会话。
一个会话包含多个进程组,但是只能有一个前台进程组。每个会话都有一个会话首领 (leader),即创建会话的进程 sys_setsid() / setsid() 调用能创建一个会话。但必须注意的是,只有当前进程不是进程组组长时,才能创建一个新的会话。调用 setsid 之后,该进程成为会话的 leader 。
一个会话只能有一个控制终端。这通常是登录到其上的终端设备(在终端登录情况下)或者伪终端设备在网络登录情况下。建立与控制终端连接的会话被称为控制进程一个会话中的几个进程组可以分为前台进程组与后台进程组。所以一个会话中,应该包括控制进程(会话首进程),一个前台进程组与任意多个后台进程组。
前台进程 与 后台进程:
用户在 shell 中可以同时执行多个命令。对于耗时很久的命令(如编译大型工程),用户不必傻傻等待命令运行完毕才执行下一个命令。用户在执行命令时,可以在命令的结尾添加 “&” 符号,表示将命令放入后台执行。这样该命令对应的进程组即为后台进程组。
在任意时刻,可能同时存在多个后台进程组,但是不管什么时候都只能有一个前台进程组。只有在前台进程组中进程才能在控制终端读取输入。当用户在终端输入信号生成终端字符(如ctrl+c、ctrl+z、ctr+\等)时,对应的信号只会发送给前台进程组。
shell 中可以存在多个进程组,无论是前台进程组还是后台进程组,它们或多或少存在一定的联系,为了更好地控制这些进程组(或者称为作业),系统引入了会话的概念。会话的意义在于将很多的工作囊括在一个终端,选取其中一个作为前台来直接接收终端的输入及信号,其他的工作则放在后台执行。
终端控制:
会话的领头进程打开一个终端后,该终端就会成为该会话的控制终端(SVR4/linux),与控制终端建立连接的会话领头进程成为控制进程(session leader)。一个会话只能有一个控制终端,产生在控制终端上的输入和信号将发送给会话的前台进程组中的所有进程,终端上的连接断开时(比如网络断开或Modem断开),挂起信号将发送到控制进程(session leader)。
综上:
进程属于一个进程组,进程组属于一个会话,会话可能有也可能没有控制终端。一般而言,当用户在某个终端上登录时,一个新的会话就开始了。
进程组由组中的领头进程标识,领头进程的进程标识符就是进程组的组 标识符。类似的,每个会话也有对应一个领头进程。同一会话中的进程通过该会话的领头进程和一个终端相连,该终端作为这个会话的控制终端。
一个会话只有一个控制终端,而一个控制终端只能控制一个会话。用户通过控制终端,可以向控制终端所控制的会话中的进程发送键盘信号。
同一个会话中只能有一个前台进程组,属于前台进程组的进程可以从控制终端获得输入,而其他进程均是后台进程,可能分属于不同的后台进程组。
当我们打开多个终端窗口时,实际上就创建了多个终端会话。每个会话都有自己的前台工作和后台工作
5、init
在计算机上启动Linux时,内核装入并启动init程序。然后init程序装载硬盘和启动终端程序。登录终端程序时,它启动命令行界面Shell。在计算机上启动Linux之后,init程序监视任何关闭计算机的信号,如不间断电源(UPS)发生的电源故障信号和重新启动命令。init是Linux系统操作中不可缺少的程序之一。所谓的init进程,它是一个由内核启动的用户级进程。内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完成引导进程。所以,init始终是第一个进程(其进程编号始终为1)。
内核会在过去曾使用过init的几个地方查找它,它的正确位置(对Linux系统来说)是/sbin/init。如果内核找不到init,它就会试着运行/bin/sh,如果运行失败,系统的启动也会失败。
6、Unix/Linux 的 System V、BSD、Posix概念
1. System V和BSD
Unix操作系统在操作风格上主要分为System V和BSD(目前一般采用BSD的第4个版本SVR4),前者的代表的操作系统有Solaris操作系统,在Solaris1.X之前,Solaris采用的是BSD风格,2.x之后才投奔System V阵营。后者的代表的操作系统有FreeBSD。
System V它最初由AT&T开发,曾经也被称为AT&T System V,是Unix操作系统众多版本中的一支。在1983年第一次发布,一共发行了4个System V的主要版本,System V Release4,或者称为SVR4,是最成功的版本,该版本有些风格成为一些UNIX共同特性的源头,如下表格的初始化脚本/etc/init.d。用来控制系统的启动和关闭。
BSD(Berkeley Software Distribution,伯克利软件套件)是Unix的衍生系统,1970年代由伯克利加州大学(Uni Versity of California, Berkeley)开创。BSD用来代表由此派生出的各种套件集合。
2. Poxis和System V
System V的概念如上所述。Posix是Portable Operating System Interface(可移植性操作系统接口)的简称,是一个电气与电子工程学会即IEEE开发的一系列标准,目的是为运行在不同操作系统的应用程序提供统一的接口,实现者是不同的操作系统内核。
将这两个名词放在一起讨论的一般是在Linux的进程间通信中,如在信号量编程中,有Posix信号量和System V信号量。它们都可以用于进程或者线程间的同步。然而, Posix信号量是基于内存的,即信号量值是放在共享内存中的,它使与文件系统中的路径名对应的名字来标识。当我们谈论“Posix 信号量”时,所指的是单个计数信号量。在Linux操作系统中,Posix信号量(共享内存、消息队列)可以通过ipcs命令查看。Posix信号量多用于进程间通信。
System v****信号量测试基于内核的,它放在内核里面,要使用System V信号量需要进入内核态,所以在多线程编程中一般不建议使用System V信号量,因为线程相对于进程是轻量级的,从操作系统的调度开销角度看,如果使用System V信号量会使得每次调用都要进入内核态,丧失了线程的轻量优势。当我们讨论“System v信号量”时,所指的是计数信号量集。
第1章-UNIX基础知识
1、UNIX 和Linux的区别:
· UNIX 诞生于 20 世纪 60 年代末,
Windows 诞生于 20 世纪 80 年代中期,
Linux 诞生于 20 世纪 90 年代初
(后来的 Windows 和 Linux 都参考了 UNIX)
· UNIX大多与硬件配套(UNIX操作系统),Linux 可运行在多种硬件平台上。
· UNIX收费(贝尔实验室),Linux 免费开源
2、什么是Linux 系统
Linux系统主要由以下4部分构成:
1. Linux内核
内核主要负责以下四种功能*😗 1. 系统内存管理 内核不断地在交换空间*(swap space)和实际物理内存之间交换虚拟内存中的内容。 2.软件程序管理(进程管理) *3.硬件设备管理 *通过驱动程序实现硬件设备与应用程序之间的通信。在Linux系统中加入驱动程序代码的方式有以下两种😗 *1.*编译进内核的设备驱动代码 2.可插入内核的设备驱动代码 *4.文件系统管理 *ext,ext2,ext3,ext4,minix,nfs,ntfs,XFS等。
2. GNU工具
GNU(GNU is not Unix的缩写),是一套为Unix系统管理员设计的一套类似于Unix的环境。
Linux 系统和 GNU 工具的结合体称为 Linux**系统,也叫做 GNU/Linux**系统。 核心 GNU 工具*(coreutils)包括以下三部分😗 *1.*用以处理文件的工具 2.用于处理文本的工具 3.用于管理进程的工具 还包括 shell,例如 bash shell。
3. 图形化桌面环境
X Window**软件包:直接和 PC 上的显卡和显示器打交道的底层程序,可以产生图形化显示环境。 其中最流行的软件包时 x.org*。* 桌面环境*:KDE**、GNOME、Unity(Ubuntu特有**)*等
4. 应用程序
3、进程控制
进程
进程:程序的执行实例被称为进程(process)。
进程ID: UNIX系统确保每个进程都有一个唯一的数字标识符(是一个非负整数)。
进程控制:有 3 个用于进程控制的主要函数: folk 、exec 和 waitpid。
线程
线程:某一时刻执行的一组机器指令。
线程ID:线程也有自己的ID标识符,只在它所属的进程内起作用。一个线程中的线程ID在另一个进程中没有意义。
信号
信号(signal): 用于通知进程发生了某种情况。
第 2 章- UNIX 标准及实现
没讲啥
第 3 章-文件 I/O
1、引言
文件 I/O函数主要包括:打开文件、读文件、写文件 操作。
用到的函数有:open、 read、 write、 lseed、 close。
2、文件描述符
1) UNIX系统shell把
文件描述符 0 与进程的标准输入关联;
文件描述符 1 与标准输出关联;
文件描述符 2 与标准错误关联;
一个套接字端点表示为一个文件描述符(16.5)。
3、函数 open 和 openat
在 <fcnt1.h>中定义了几个常用的常量,表示对文件的操作:
O_RDONLY 只读打开, 一般定义为 0
O_WRONLY 只写打开, 一般定义为 1
O_RDWR 读、写打开, 一般定义为2
4、函数 creat
#include <fcnt1.h>
int creat(const char *path, mode_t mode);
//返回值:成功,返回为只写打开的文件描述符。出错,返回-1
//path 创建的文件名, mode 权限位
//eg.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PERM 0755
int main(void)
{
static char filename[] = "file.txt"; //会创建一个名为 file的txt文件
int fd;
fd = creat(filename,PERM);
if(fd < 0)
printf("[%s] create fail !!!\n",filename);
else
printf("[%s] open success !\n",filename); //输出 open success!
exit(0);
}
4.1 Linux 系统中采用三位十进制数表示权限,如0755, 0644.
ABCD A- 0, 表示十进制 B-用户 C-组用户 D-其他用户
— -> 0 (no excute , no write ,no read) --x -> 1 excute, (no write, no read) -w- -> 2 write -wx -> 3 write, excute r-- -> 4 read r-x -> 5 read, excute rw- -> 6 read, write , rwx -> 7 read, write , excute
0755-> 即用户具有读/写/执行权限,组用户和 其它用户具有读写权限; 0644->即用户具有读写权限, 组用户和 其它用户具有只读权限;
一般赋予目录0755权限,文件0644权限。
5、函数 close
头文件:
#include<unistd.h>
//功能:关闭一个已经打开的文件
原型
int close(int fd)
//参数说明: fd:是需要关闭的文件描述符
返回值
//成功:返回0;
//失败:返回-1,并设置errno
6、函数 lseek
可以调用 lseek 显示地为一个打开文件设置偏移量。
7、函数 read
从打开文件中读数据。
8、函数write
像打开文件写数据
11、原子操作
1.函数 pread 和 pwrite
这两种扩展允许原子性地定位并执行 I/O
12、函数 dup 和 dup2
这两个函数可用来复制一个现有的文件描述符。
文件描述符:
Linux *中一切皆文件。*Linux 会给每一个文件分配一个编号(ID),这个编号就是一个整数,也就是文件描述符。
13、函数 sync、fsync 和fdatasync
延迟写*(delay write)**:我们向文件写入数据时,内核通常先将数据复制到缓冲区中,然后排入队列,晚些时候再写入磁盘。*
这三个函数是为了保证磁盘上实际文件系统与缓冲区中内容的一致性。
14、函数 fcnt1
它可以改变已经打开文件的属性
15、函数 ioct1
ioct1 函数一直是 I/O 操作的杂物箱。终端I/O是使用 ioct1最多的地方。
16、 /dev.fd
其目录项是名为0、1、2等的文件。
打开文件 /dev/fd/n 等效于复制描述符 n(假定描述符 n 是打开的)。
第 4 章- 文件和目录
1、
2、函数 stat、 fstat、fstatat 和 lstat
功能:返回与此命名文件有关的信息结构。
3、文件类型
· 普通文件(regular file)
· 目录文件(directory file)
· 块特殊文件(block special file)
· 字符特殊文件(character special file)
· FIFO
· 套接字(socket)
· 符号链接(symbolic link)
4、设置用户 ID 和设置组 ID
5、文件访问权限
每个文件有 9 个访问权限位
S_IRUSR 用