首页 > 代码库 > Unit高级环境编程 知识积累(二)。

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---fsync4 获取一个操作完成状态。  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 调用失败,错误保存在errno8 提交一个 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_SYNC4 解除映射区。  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_RMID6 将数据放到消息队列中。  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_UNLOCK14 将共享存储连接到进程中。  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:

 

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