Unit高级环境编程 知识积累(二)。

本文仅作个人积累。待功成,重新分类排版。

章14起

1:非阻塞IO:发出open/read/write等IO操作,并使这些操作不会永远阻塞。当不能完成时,会立即出错返回。

  1,非阻塞的两种标志方式:指定标志:O_NONBLOCK。

  2,非阻塞语义:文件状态标志的更改影响同一文件表项的所有用户,但与通过其他文件表项对同一设备的访问无关。(关联于文件表项)

2:记录锁功能(字节范围锁):一个进程正在读/修改文件的某个部分时,使用记录锁可以阻止其他进程修改同一个文件区。

  1,它可以只锁住文件的一段区域。控制上更为精确。

  2,fcnt1记录锁。

int fcnt1(int fd, int cmd, struct flock *flockptr );

struct flock
{
  short l_type;    // F_RDLCK(共享读锁), F_WRLCK(独占写锁), F_UNLCK(解锁一个区域)
  short l_whence;  // 加/解锁区域的起始字节偏移量。可选值:SEEK_SET, SEEK_CUR, SEEK_END
  off_t l_start;   // 加/解锁区域的起始字节偏移量。
  off_t l_len;     // 区域字节长度.等于0时,表示可以添加为最大偏移量,而不是0。
  pid_t l_pid;     // 当前进程ID(cmd == F_GETLK 时)
}

    1)对于记录锁,参数cmd是: F_GETLK(获取锁状态), F_SETLK(设置锁), F_SETLKW(设置锁的阻塞版,W==wait)。

    2)参数flockptr时记录锁的一个结构体。

    3)共享读和独占写的控制模式和读写锁类似。

    4)单个进程在同一区域设置第二把锁时,会覆盖之前的锁。

    5)加读锁时,描述符必须时读打开;加写锁时,必须是写打开。

    6)使用参数cmd 获取/设置锁 的操作都不是原子操作。

  3,锁的隐含继承和释放。

    1)当一个进程终止时,它建立的锁全部释放。当一个描述符关闭时,进程通过描述符设置的锁也会释放。

    2)由fork产生的子进程不继承父进程设置的锁。

    3)执行exec后,新程序可以继承原执行程序的锁(和上条似乎存在矛盾)。

  4,合作进程:一个库的所有函数都以一致的方法处理记录锁,则称使用这些函数访问数据库的进程集为合作进程。

  5,强制性锁:该锁会让内核见车每一个IO函数,验证调用进程是否违背了正在访问的文件上的某一把锁。

    1)打开方式:对特定文件打开其设置组ID位,关闭组执行位(无法理解)。

    2)建议锁的含义?

    3)强制性锁存在缺陷,是可以避开的。

3:轮询:一段时间,调用一次期望的进程/函数。

  1,多任务系统中,尽量避免使用此方法。

4:异步IO(asynchronous IO)。

  1,机制:当描述符准备号可以进程IO时,发送一个信号通知进程。

  2,存在问题。

    1)仅当描述符引用中断设备或网络时,它才能起作用。

    2)这种信号对每个进程都只有一个。

5:IO多路转接(IO multiplexing)。

  1,机制:构造一个包含描述符的列表,然后调用一个函数。当描述符中的一个准备好进程IO操作时,该函数才返回。

  2,相关函数。

<sys/select.h>
1 多路转接的查询函数。
  int select( int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict tvptr );  
  // 1 输入:描述符,描述符条件(读/写/异常),等待时间。
  // 2 输出:描述符总量,已准备好的描述符条件
  // 3 参数tvptr: ==NULL 永远等待, ==0 不等待, !=0 等待具体时间。无const,无法保证不被修改。。
  // 4 fd_set类型参数(写/读/异常条件): 当参数==NULL时,表示不关心此参数。
  // 5 参数maxfdp1:最大文件描述符编号+1.
  // 6 返回值:出错-1,0 表示没准备好的描述符, >0 已准备好的描述符数之和。
  int pselect( int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, const struct timespec *restrict tsptr, const sigset_t *restrict sigmask );
  // 1 提供高精度,并超时值无法改变。
  // 2 可使用 信号屏蔽字。
  int poll( struct pollfd fdarray[], nfds_t nfds, int timeout );
  // 1 将我们感兴趣的描述符写进 pollfd 数组中。
  // 2 参数 nfds:指定数组元素个数。
  // 3 参数timeout:-1 永远等待,0 不等待,>0 等待具体时间。
2 数据类型 fd_set 的处理函数/宏(取决于实现成函数,还是宏)。
  int FD_ISSET( int fd, fd_set *fdset );  // 指定位是否已打开。
  void FD_CLR( int fd, fd_set *fdset );   // 清除某一个位。
  void FD_SET( int fd, fd_set *fdset );   // 设置某一个位。
  void FD_ZERO( fd_set *fdset );          // 所有位设置为0。
  // 1 fd_set类型中,一个文件描述符占据一位。通常来说 位上置1 表示条件满足(不确定具体系统实现)。
3 数据类型 pollfd。
  struct pollfd
  {
    int    fd;       // file decriptor to check, or <0 to ignore.
    short  events;   // events of interest on fd.
    short  revents;  // events that occurred on fd.
  }
  // 1 如果使用此结构,需要直到具体的event返回值的含义

  3,系统并不主动告知任何信息。需要我们使用函数取查询文件描述符。

6:BSD派生系统中,异步IO是信号SIGIO 和 SIGURG 的组合。

  1,SIGIO:通用异步IO信号。

  2,SIGURG:通知进程网络连接上的带外数据已经到达。

7:POSIX异步IO:为不同类型的文件进行异步IO提供例一套一致的方法。

  1,相关函数。

1 AIO控制块基本结构(具体系统可在标准上添加)
  struct aiocb
  {
    int              aio_fildes;     // file descriptor
    off_t            aio_offset;     // file offset for IO
    volatile void   *aio_buf;        // buffer for IO
    size_t           aio_nbytes;     // number of bytes to transfer
    int              aio_reqprio;    // priority
    struct sigevent  aio_sigevent;   // signal information
    int              aio_opcode;     // operation for list IO
  }
2 基本读写操作。
  int aio_read( struct aiocb * aiocb );
  int aio_write( struct aiocb * aiocb );
3 强制等待的异步操作直接写入。
  int aio_fsync( int op, struct aiocb *aiocb );   // !!!具体功能未完全确认!!!
  // 1 op参数有两个选择:O_DSYNC---fdatasync, O_SYNC---fsync
4 获取一个操作完成状态。
  int aio_error( const struct aiocb *aiocb );
  // 1 返回值:0 成功,-1失败 errno中保存错误信息,EINPROGRESS 操作等待中,
5 如果成功,获取异步操作返回值。
  ssize_t aio_return( const struct aiocb *aiocb );
  // 1 调用一次后,系统就清除返回值。
6 进程只剩异步操作未完成,可以通过此函数阻塞进程。直到异步操作完成。
  int aio_suspend( const struct aiocb *const list[], int nent, const struct timespec *timeout );
7 取消等待的异步操作。
  int aio_cancel( int fd, struct aiocb *aiocb );   // 仅发出取消命令,并不代表一定取消。
  // 1 返回值:AIO_ALLDONE 所有操作已完成,不需要取消, AIO_CANCELED 成功, AIO_NOTCANCELED 最少有一个操作没被取消, -1 调用失败,错误保存在errno
8 提交一个 AIO控制块的 列表
  int lio_listio( int mode, struct aiocb *restrict const list[ restrict ], int nent, struct sigevent *restrict sigev );
  // 1 参数mode:LIO_WAIT, LIO_NOWAIT

  2,异步IO操作必须显式的指定偏移量。异步IO接口并不影响由操作系统维护的文件偏移量。

    1)使用追加模式时,aio_offset字段会被系统忽略。

8:多种读写函数。

  1,相关函数。

1 读/写 多个缓冲区
  ssize_t readv( int fd, const struct iovec *iov, int iovcnt );
  ssize_t writev( int fd, const struct iovec *iov, int iovcnt );
  // 1 参数iovcnt:读取缓冲区的个数。
2 按照需求 读/写 N个字节的数据
  ssize_t readn( int fd, void *buf, size_t nbytes );
  ssize_t writen( int fd, void *buf, size_t nbytes );

9:应当用尽量少的系统调用次数。

10:储存映射IO。

  1,相关函数。

1 将给定文件映射到储存区域。
  void *nmap( void *addr, size_t len, int prot, int flag, int fd, off_t off );
  // 1 参数addr:指定映射区域的起始地址。设置为0得到最大的可移植性。
  // 2 参数prot:储存区的权限(读/写/执行)
  // 3 参数flag:MAP_FIXED,MAP_SHARED,MAP_PRIVATE.
2 更改映射权限。
  int mprotect( void *addr, size_t len, int prot );
3 将储存区数据写入被映射文件。
  int msync( void *addr, size_t len, int flags );
  // 1 参数flags:MS_ASYNC, MS_SYNC
4 解除映射区。
  int munmap( void *addr, size_t len );  // 调用此函数,不会使储存区数据写入文件

  2,机制:将一个磁盘文件映射到储存空间中的一段缓冲区上。

11:进程通信基础(interProcess Communication, IPC)

12:管道。

  1,缺陷。

    1)部分系统支持全双工(不确定linux)

    2)管道只能在具有公共祖先的两个进程之间使用。

  2,相关函数。

1 创建管道。
  int pipe( int fd[2] );
  // 1 fd[0]为读 fd[1]为写。fd[1]的输出是fd[0]的输入。
2 创建一个管道,fork一个子进程,关闭未使用的管道端,执行一个shell命令,等待命令终止。
  // !!!需要进一步了解者两个函数原理和使用机制!!!
  FILE *popen( cosnt char *cmdstring, const char *type );  // fork --> exec and cmd --> return a ptr of IO.
  int pclose( FILE *fp );
  // 适用于简单的过滤器程序

  3,协同进程:一个过滤程序既产生某个过滤程序的输入,又读取该过滤程序的输出。

13:FIFO:命名管道。

  1,作用:能够使 不相关的进程 进行通信。

  2,相关函数。

#include <sys/stat.h>
1 创建FIFO
  int mkfifo( const char *path, mode_t mode );
  int mkfifoat( int fd, const char *path, mode_t mode );
  // 1 一般情况下,都是阻塞到读写开始为止。但设置非阻塞时,会立即返回-1,errno设置ENXIO。

  3,一些注意点。

    1)FIFO路径名存在于文件系统中。

  4,一些用途。

    1)shell命令使用FIFO将数据从一条管道传送到另外一条。无需创建中间文件。

    2)C/S模式中,FIFO用作汇聚点,在客户进程和服务器进程之间传递数据。

14:XSI IPC

  1,相关函数。

1 通过路径名+项目ID产生一个KEY。
  key_t ftok( const char *path, int id );
  // 1 参数path必须引用现有文件,参数id只使用低8位。
2 ipc_perm 权限和所有者.
  struct ipc_perm
  {
    uid_t   uid;    // owner‘s effective user id.
    gid_t   gid;    // owner‘s effective group id.
    uid_t   cuid;   // creator‘s effective user id.
    gid_t   cgid;   // creator‘s effective group id.
    mode_t  mode;   // access modes
  }  // 此为最小结构。具体实现 可添加成员
3 消息队列的信息结构 msqid_ds。
  struct msqid_ds
  {
    struct ipc_perm      msg_perm;   // see section.
    msgqnum_t            msg_qnum;   // # of messages on queue.
    msglen_t             msg_qbytes; // max # of bytes on queue.
    pid_t                msg_lspid;  // pid of last msgsnd()
    pid_t                msg_lrpid;  // pid of last msgrcv()
    time_t               msg_stime;  // last-msgsnd() time
    time_t               msg_rtime;  // last-msgrcv() time
    time_t               msg_ctime;  // last-change time
  }
4 打开/创建 一个队列。
  int msgget( key_t key, int flag );
5 对 队列 执行多种操作。
  int msgctl( int msqid, int cmd, struct msqid_ds *buf );
  // 1 参数cmd:IPC_STAT, IPC_SET, IPC_RMID
6 将数据放到消息队列中。
  int msgsnd( int msqid, cosnt void *ptr, size_t nbytes, int flag );
  // 1 参数ptr:指向mymesg结构。
7 从队列取消息。
  ssize_t megrcv( int msqid, void *ptr, size_t nbytes, long type, int flag );
  // 1 参数type:>0时,以非 先进先出 的次序 获取消息。
8 获取一个信号量。
  int semget( key_t key, int nsems, int flag );
9 多种信号量操作。
  int semctl( int semid, int semnum, int cmd, .../* union semun arg */ );
  // 1 参数cmd:IPC_STAT,IPC_SET,IPC_RMID,GETVAL...
10 自动执行信号量集合上的操作数组。
  int semop( int semid, struct sembuf semoparray[], size_t nops );   // 具有原子性,或者执行所有,或者全部不执行。
  struct sembuf
  {
    unsigned short    sum_num;   // member # in set (0, 1, ..., nsems-1)
    short             sem_op;    // operation(negative, 0, or pasitive )
    short             sem_flg;   // IPC_NOWAIT, SEM_UNDO
  }
11 内核为每个共享储存段维护一个结构。
  struct shmid_ds
  {
    struct ipc_perm  shm_perm;   // see section
    size_t           shm_segsz;  // size of segment in bytes
    pid_t            shm_lpid;   // pid of last shmop
    pid_t            shm_cpid;   // pid of creator
    shmatt_t         shm_nattch; // number of current attaches
    time_t           shm_atime;  // last-attach time
    time_t           shm_dtime;  // last-detach time
    time_t           shm_ctime;  // last-change time
    ...
  }
12 获得一个共享储存标示符。
  int shmget( key_t key, size_t size, int flag );
  // 1 参数size:字节为单位。通常为系统页长的整倍数。
13 对共享存储 执行多种操作。
  int shmctl( int shmid, int cmd, struct shmid_ds *buf );
  // 1 参数cmd:IPC_STAT,IPC_SET,IPC_RMID,SHM_LOCK,SHM_UNLOCK
14 将共享存储连接到进程中。
  void shmat( int shmid, cosnt void *addr, int flag );
15 进程和共享存储的分离操作(不删除共享存储)。
  int shmdt( const void *addr );

  2,有三种:消息队列,信号量,共享储存器。

  3,使用 非负整数 的标识符。数据类型为: key_t <sys/types.h>

  4,有多种方法使 客户进程 和服务器进程 在同一IPC结构上汇聚。

    1)服务器进程指定键IPC_PROVATE创建新IPC结构,并将标识符存放在某处,让客户进程取用。

    2)在公共头文件中定义一个 两个进程 都认可的键。

    3)两个进程 认可一个路径名和项目ID使用ftok函数将两个值变成一个KEY。

  5,基本问题。

    1)IPC结构在系统范围内起作用,没有引用计数。

    2)IPC结构在文件系统中没有名字。

    3)IPC不能使用文件描述符,所以不能对它们使用多路转接IO函数。

  6,消息队列:消息的链接表。

  7,信号量:一个计数器。用于为多个进程提供对共享数据对象的访问。

    1)信号量通常在内核中实现。且减1操作为原子操作。

    2)进程终止时,内核会自动检测信号量,并进行调整。

    3)互斥量比信号量快很多。但如果可能尽量使用信号量。因为信号量的心痛支持度较高,而且复杂性更低。

  8,共享存储:最快的IPC。

    1)信号量可用于同步共享储存访问。

    2)

15:POSIX 信号量。

  1,相关函数。

1 创建信号量 / 使用现有信号量。
  sem_t sem_open( const char *name, int oflag, mode_t mode, unsinged int value );
2 释放信号量相关资源。
  int sem_close( sem_t *sem );
3 销毁一个命名信号量。
  int sem_unlink( const char *name );  // 当最后一个引用关闭时 才销毁。
4 实现信号量减一操作。
  int sem_trywait( sem_t *sem );
  int sem_wait( sem_t *sem );
  int sem_timedwait( sem_t *sem, const struct timespec *restrict tsptr );
  // 1 资源为0时,发生阻塞。>0,减一
5 信号量+1
  int sem_post( sem_t *sem );
6 创建/销毁 一个未命名信号量。
  int sem_init( sem_t *sem, int pshared, unsigned int value );
  int sem_destroy( sem_t *sem );
7 检索信号量值。
  int sem_getvalue( sem_t *restrict sem, int *restrict valp );
  // 1 除非使用额外的同步机制来避免竞争,否则此函数只能用于调试

  2,相对XSI优点。

    1)更高性能。

    2)使用更简单:没有信号量集,部分操作统一化。

    3)信号量删除表现的更完美:直到最后一次使用后才释放。

  3,拥有两种性是:命名的和未命名的。差异:创建和销毁的形式上。

  4,为增加移植性,信号量命名应该有一定规则:

    1)第一个字符为 斜杠 / 。

    2)名字不包含其他斜杠以避免实现定义的行为。

    3)信号量名最大长度由实现定义。

  5,P470 客户进程-服务器进程属性。

16:

17:

时间: 2024-10-16 05:37:36

Unit高级环境编程 知识积累(二)。的相关文章

Unit高级环境编程 知识积累。

本文仅作个人积累.待功成,重新分类排版. 1:shell 为命令行解释器.分为:交互式Shell和Shell脚本. 2:文件名建议字符集:字母+数字+ ./-/_   尽量不要使用其他符号.因为特殊符号在很多功能中已经被占用. 3:

Linux环境编程之进程(二):程序的存储空间布局

引言: 一个写好的程序一般要存放在存储器中,那么程序中的代码.数据等各部分,是如何有规律的存放在存储器中的呢? (一) 一个存储的程序可分为五部分:正文段.初始化数据段.非初始化数据段.栈.堆.其典型的存储安排如下图: 正文段:这是由CPU执行的机器指令的部分.通常,正文段是可共享的,所以即使是频繁执行的程序在存储器中也只需要一个副本,另外正文段常常是只读的,以防止程序由于意外而修改其自身的指令. 初始化数据段:通常称为数据段,它包含了程序中需要明确地赋初值的变量. 非初始化数据段:通常称为bs

Linux环境编程之同步(二):条件变量

MainActivity如下: package cn.testprogressdialog; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; import android.os.

《unix高级环境编程》终端 I/O——终端 IO 基本概述

终端基本概念 终端 IO 是一种字符型设备,终端特殊设备文件一般有以下几种: 串行端口终端:是使用计算机串行端口连接的设备,计算机把每个串行端口都看作是一个字符设备.串行端口所对应的设备名称 /dev/ttySn(n表示从0开始的整数): 伪终端:是成对的逻辑终端设备,例如 /dev/ptyp3 和/ dev/ttyp3(在设备文件系统中分别是 /dev/pty/m3 和/ dev/pty/s3 ),它们与实际物理设备并不直接相关: 控制终端:是当前进程的控制终端的设备特殊文件 /dev/tty

Linux环境编程之信号(二):不可靠信号、中断的系统调用、可重入函数

(一)不可靠信号 对前面说的信号,是不可靠的,不可靠指的是信号可能会丢失:一个信号发生了,但进程却可能一直不知道这一点.另外,进程对信号的控制能力有限,只能捕捉信号或忽略它.有时用户希望通知内核阻塞一个信号:不要忽略它,在其发生时记住它,然后在进程做好准备时再通知它.这种阻塞信号的能力并不具备. 之前的版本中村咋一个问题:在进程每次接到信号对其进行处理时,随即将该信号动作复位为默认值.另一个问题是,在进程不希望某种信号发生时,它不能关闭该信号.进程能做的一切就是忽略该信号. (二)中断的系统调用

高级程序员需知的并发编程知识(二)

说明 本篇是继上一篇并发编程未讨论完的内容的续篇.上一篇传送门: Java并发编程一万字总结(吐血整理) 活跃性问题 在上一篇我们讨论并发编程带来的风险的时候,说到其中 一个风险就是活跃性问题.活跃性问题其实就是我们的程序在某些场景或条件下执行不下去了.在这个话题下我们会去了解什么是死锁.活锁以及饥饿,该如何避免这些情况的发生. 死锁 我们一般使用加锁来保证线程安全,但是过度地使用加锁,可能导致死锁发生. 哲学家进餐问题 "哲学家进餐"问题能很好地描述死锁的场景.5个哲学家去吃火锅,坐

Unix高级环境编程学习笔记(二):文件和目录

1 stat,fstat,lstat函数 #include<sys/stat.h> int stat (const char* restrict pathname, struct stat *restrict buf); int fstat (int filedes, struct stat * buf); int lstat (const char* restrict pathname, struct stat *restrict buf); 成功返回0,失败返回-1 stat返回pathn

UNIX高级环境编程(15)进程和内存分配 &lt; 故宫角楼 &gt;

故宫角楼是很多摄影爱好者常去的地方,夕阳余辉下的故宫角楼平静而安详. ? 首先,了解一下进程的基本概念,进程在内存中布局和内容. 此外,还需要知道运行时是如何为动态数据结构(如链表和二叉树)分配额外内存的. 一 进程 1 进程和程序 进程:是一个可执行程序的实例. 程序:包含一系列信息的文件,这些信息描述了如何在运行时创建一个进程.包含如下信息: 二进制格式标识:如最常见的ELF格式. 机器语言指令:对程序算法进行编码. 程序入口地址:标识程序开始执行时的起始指令位置. 数据:程序文件包含的变量

UNIX高级环境编程(2)FIle I/O - 原子操作、共享文件描述符和I/O控制函数

引言: 本篇通过对open函数的讨论,引入原子操作,多进程通信(共享文件描述符)和内核相关的数据结构. 还会讨论集中常见的文件IO控制函数,包括: dup和dup2 sync,fsync和fdatasync fcntl ioctl /dev/fd ? 一.文件共享 这里所说的文件共享主要指的是进程间共享打开的文件. 这一节主要讨论文件在进程间共享的理论基础和数据结构,不涉及具体的技术实现,不同的系统可能会有不同的实现. 每一个打开的文件,涉及内核中的三种数据结构,这三种数据结构也是文件在进程间共