【unix】unix环境高级编程

2023-05-16

文章目录

    • 1.UNIX基础知识
      • 1.基本知识
      • 2.文件和目录
      • 3.输入和输出
      • 4.程序和进程
      • 5.出错处理
      • 6.用户标识
      • 7.信号
      • 8.时间
      • 9.系统调用和库函数
    • 标准化和实现
      • 1.标准化
        • ⑴ISO C
        • ⑵POSIX
        • ⑶Single UNIX Specification(SUS)
      • 2.主要实现
      • 3.限制(▲)
        • ⑴ISO C函数
        • ⑵POSIX限制
        • ⑶三个运行时限制函数
          • ①sysconf
          • ②pathconf
          • ③fpathconf
        • ⑷不确定的运行方式限制
      • 4.选项
      • 5.功能测试宏
      • 6.基本系统数据类型
    • 文件IO
      • 1.基本知识
      • 2.open/openat/close
      • 3.creat
      • 4.lseek
      • 5.read/write
      • 6.进程文件共享
      • 9.dup和dup2
      • 10.sync fsync fdatasync
      • 10.fcntl
      • 11.ioctl
    • 文件和目录
      • 1.获取文件信息
      • 2.文件类型
      • 3.权限
        • ⑴权限位
        • ⑵进程用户ID和设置组ID
        • ⑶新文件和目录的所有权
        • ⑷access/faccessat
        • ⑸unmask
        • ⑹chmod/fchmod/fchmodat
        • ⑺粘着位
        • ⑺chown/fchown/fchownat/lchown
      • 4.文件长度和截断
      • 3.文件系统
        • ⑴文件系统结构
        • ⑵硬链接
        • ⑶符号链接(软链接)
        • ⑷文件的时间
        • ⑸函数
      • 4.目录
        • ⑴知识点
        • ⑵函数
      • 5.设备特殊文件
    • 标准I/O库
      • 1.基础
      • 4.打开流/关闭流
      • 5.读和写流
      • 7.二进制IO
      • 8.定位流
      • 10.fileno
      • 11.创建临时文件
      • 12.内存流
      • 13.扩展
    • 系统数据文件
      • 1.口令文件
      • 2.组文件/附属组ID
      • 3.其它数据文件
      • 5.其它
    • 进程环境
      • 1.基本知识
      • 2.C程序的存储空间布局
      • 3.共享库
      • 4.存储空间分配
        • ①ISO C标准下的
        • ②进阶的
      • 5.环境变量
    • 进程控制
      • 1.基本知识
      • 2.fork
      • 3.wait
      • 4.exec
      • 5.用户ID和组ID
      • 6.解释器文件
      • 7.进程会计
      • 8.进程时间
    • 进程关系
      • 1.终端登录
      • 2.网络登录
      • 3.进程组
      • 4.会话
      • 5.控制终端
      • 6.作业控制
      • 7.shell执行程序
      • 8.孤儿进程组
      • 9.守护进程
    • 信号
      • 1.信号基础
      • 2.函数signal
      • 3.中断的系统调用
      • 4.可重入函数
      • 5.SIGCHLD
      • 6.信号集
      • 5.作业控制信号
      • 6.信号名和编号
      • 7.主要函数(待整理)
        • signal
        • kill/raise
        • alarm/pause
        • sigprocmask
        • sigpending
        • sigaction
        • sigsetjmp/siglongjmp
        • sigsuspend
        • abort
        • system
        • sleep/nanosleep/clock_nanosleep
        • sigqueue
    • 线程
      • 1.基础概念
      • 2.线程同步
        • ⑴互斥量
        • ⑵读写锁
        • ⑶条件变量
        • ⑷自旋锁
        • ⑸屏障
    • 线程控制
      • 1.线程限制
      • 2.线程属性
      • 6.线程同步属性
        • ⑴互斥量属性
        • ⑵读写锁属性
        • ⑶条件变量属性
        • ⑷屏障属性
      • 7.重入
      • 8.线程私有数据
    • 高级IO
      • 1.非阻塞IO
      • 2.字节范围锁
      • 3.多路转接
        • ⑴轮训
        • ⑵IO多路转接
      • 4.异步IO
      • 5.readv/writev
      • 6.readn/writen
      • 7.存储映射IO
    • 进程通讯
      • 1.概述
      • 2.管道
      • 3.FIFO
      • 4.XSI
        • 标识符和键
        • 消息队列
        • 信号量
        • 共享存储

1.UNIX基础知识

1.基本知识

  • 程序调用内核的入口叫做系统调用
  • GNU是一个类Unix操作系统,典型的内核就是Linux
  • 用户登录信息存储于/etc/passwd
  • Bourne-again shell就是bash
  • 根目录的起点是"/"

2.文件和目录

  • 根目录的名称是"/"
  • 斜线和空格不能出现在文件名中
  • 登录时的工作目录从/etc/passwd中获取

3.输入和输出

  • 内核(kernel)利用文件描述符(file descriptor)来访问文件,不同进程文件描述符
    相同,那么可能是同一个文件,也有可能不是
  • 每当运行一个新程序时,所有shell都为其打开3个文件描述符,它们三个都是链接向终端
    • 标准输入
    • 标准输出
    • 标准错误
  • 不断缓冲的IO open read write lseek close
  • 标准IO 使用标准IO无需担心如何选择最佳缓冲区的大小。比如printf

4.程序和进程

  • 程序是存储在磁盘某个目录上的可执行文件
  • 进程控制的主要函数
    • fork创建一个新进程
    • exec 执行
    • waitpid 父进程等待子进程终止,通过waitpid实现
  • 线程ID只在它所属的进程内起作用

5.出错处理

UNIX系统函数出错时,通常会返回一个负值

错误分为致命性错误和非致命性错误

6.用户标识

  • 用户ID 用户不能更改,0为根用户
  • 组ID 数值,组文件时/etc/group
  • 附属组ID

7.信号

用于通知进程发生了某种状况,进程可以对信号做三种处理

  • 忽略信号
  • 默认方式处理
  • 信号处理函数

信号产生的方式多种多样,比如Ctrl+C,kill函数等等

8.时间

时间分为两种,日历时间(就是时间戳)和进程时间

进程时间又分为:

  • 时钟时间,进程运行时间总量
  • 用户CPU时间,执行用户指令所用的时间量
  • 系统CPU时间,执行内核程序所经历的时间

9.系统调用和库函数

操作系统提供多种服务的入口点,由此程序向内核请求服务

UNIX所使用的技术是为每个系统调用在标准C库中设置一个具有同样名字的函数,用户进程调用这些函数,然后函数用系统所要求的技术调用对应的内核服务

系统调用和库函数的区别

  • 从用户角度来看,它们区别并不重要
  • 我们可以替换库函数,但是系统调用通常不能替换
  • 应用程序既可调用系统调用也可以调用库函数,很多库函数还会调用系统调用
  • 系统调用一般是最小接口,库函数提供比较复杂的功能
  • fork exec wait通常由用户应用程序直接调用

标准化和实现

1.标准化

⑴ISO C

意图是提供C程序的可移植性,ISO C头文件依赖于操作系统所配置的C编译器版本

在这里插入图片描述

⑵POSIX

它定义了符合POSIX标准的操作系统必须提供的各种服务,它也包含了ISO C标准库函数,它的全名叫可移植的操作系统接口

ISO C标准如果和POSIX.1之间冲突,POSIX.1服从ISO C

POSIX标准分为必须部分和可选部分,必须不含如下:
在这里插入图片描述

⑶Single UNIX Specification(SUS)

POSIX接口的超集,POSIX相当于它的基本规范,只有遵循SUS标准的操作系统才能称作UNIX

2.主要实现

ISO C,POSIX,SUS只是接口的规范,主要的实现如下

  • BSD 伯克利分校开发的
  • Linux 类似于UNIX的操作系统
  • Mac OS X 内核也是一个UNIX系统
  • Solaris Sun公司开发的UNIX系统

这四种只有Mac OS X和 Solaris 10是UNIX系统,但是他们都提供UNIX编程环境,都在不同程度上符合POSIX标准

3.限制(▲)

  • 编译时限制,在头文件中定义

  • 运行时限制,要求进程调用一个函数获得限制值

    • 与文件和目录无关的运行时限制(sysconf函数)
    • 与文件或目录有关的运行时限制(pathconf和fpathconf函数)

如果一个特定的运行时限制啊在一个给定的系统中不改变,可以静态的定义在一个头文件中,如果没有定义在头文件中,应用程序就必须调用3个conf函数中的一个

⑴ISO C函数

所有编译时限制都列在头文件limits.h中

⑵POSIX限制

POSIX对ISO C进行了扩充

⑶三个运行时限制函数

若成功,返回相应值,出错返回-1

①sysconf
②pathconf

参数是路径名

③fpathconf

参数时文件描述符

⑷不确定的运行方式限制

  • 路径名
  • 最大打开文件数

4.选项

如果我们要编写一些可移植的应用程序,而这些程序与所有得到支持的选项有关,那么就需要一种可移植到方法以决定一种实现是否支持一个给定的选项

  • 编译时选项定义在<unistd.h>中。
  • 与文件或目录无关的选项用sysconf函数确定。
  • 与文件或目录有关的选项通过调用pathconf或fpathconf函数来发现

在这里插入图片描述* 如果符号常量的定义值为-1,那么该平台不支持相应的选项。

  • 如果符号常量的定义值大于0,那么该平台支持相应的选项。
  • 如果符号常量的定义值为0,则必须调用sysconf、pathconf或fpathconf以确定相应的选项是否受到支持

5.功能测试宏

头文件有POSIX.1和XSI的符号,也有具体实现自己的定义,为了防止和实现冲突,我们可以通过定义功能测试宏的方式来使用POSIX.1或者XSI的定义

6.基本系统数据类型

基本系统数据类型定义在头文件sys/types.h中,也有很多定义在其它数据类型,他们绝大多数都是_t结尾

在这里插入图片描述

文件IO

1.基本知识

  • UNIX系统的而大多数文件IO只需要用到open,read,write,lseek,close函数,这些都是不带缓冲的IO,并非标准IO
  • 文件描述符
    • 0是进程标准输入
    • 1是进程标准输出
    • 2是进程标准错误
  • 大多数现代文件系统文件名最大长度255

2.open/openat/close

若成功,返回文件描述符,出错返回-1

  • open
  • openat 相对于open,它可以使用相对路径打开目录中的文件

返回的文件描述符一定是最小的未用描述符数值

  • close 关闭文件

3.creat

创建一个新文件,成功返回一个文件描述符,出错返回-1

可以被open函数替代,而且open函数可以以读写的方式打开文件,这样在创建临时文件时更方便。creat只能以只写的方式打开

4.lseek

读写操作都是从当前文件偏移量开始,打开文件除非是O_APPEND,否则是0

我们可以使用lseek给一个打开文件设置偏移量

管道,FIFO,网络套接字无法设置偏移量,我们可以用lseek来检查是否可以设置,不能设置的返回-1

设置偏移量大于文件当前长度,下次写时会形成全为0的空洞,空洞不占用磁盘存储区

5.read/write

  • read 返回值,读到的字节数,若已经到文件尾,返回0,若出错,返回-1

  • write 返回值,成功返回已写的字节数,若出错,返回-1,写操作从文件当前偏移量开始

6.进程文件共享

在Linux中,如果两个进程各自打开了同一文件:
在这里插入图片描述
不同的进程的vi节点是共享的,所以多个进程操作同一个文件可能会有问题。

解决方式是将定位和写合并成一个原子操作。UNIX通过O_APPEND标志来设置原子操作。

  • pread 相当于lseek后调用read,但是有一定的区别
  • pwrite 相当于lseek后调用write,同样有区别

9.dup和dup2

  • dup 返回的文件描述符是当前可用文件描述符的最小值
  • dup2 可以指定新的文件描述符

复制一个现有的文件描述符,成功返回新的文件描述符,失败返回-1

两个描述符共享相同的内部结构,共享所有的锁定,读写位置和各项权限或flags等等。例如:对一个文件描述符进行了lseek操作,另一个文件描述符的读写位置也会随之改变。不过,文件描述符之间并不共享close-on-exec flags,也就是一个关闭对另一个没有影响

10.sync fsync fdatasync

UNIX系统在内核中设有缓冲区,大多数磁盘IO都通过缓冲区进行,当写数据时,先写缓冲区,然后进入队列,最后进入磁盘

  • sync 将缓冲区写入队列后返回
  • fsync 只对特定文件描述符起作用,磁盘操作结束返回,确保修改过的块立即写到磁盘
  • fdatasync 它只影响文件数据部分,而fsync还会同步更新文件属性

10.fcntl

  • 复制一个已有的描述符
  • 获取/设置文件描述符标志
  • 获取/设置文件状态标志
  • 获取/设置异步I/O所有权
  • 获取/设置记录锁

11.ioctl

I/O操作杂物箱,很多IO操作都能用ioctl表示,主要用于终端I/O

文件和目录

1.获取文件信息

重点是stat函数,它通过文件名filename获取文件信息,并保存在buf所指的结构体stat中

int stat(const char *file_name, struct stat *buf);

下面是buf的结构体:

struct stat {
  dev_t     st_dev;    //文件的设备编号
  ino_t     st_ino;    //节点
  mode_t    st_mode;   //文件的类型和存取的权限
  nlink_t    st_nlink;   //连到该文件的硬连接数目,刚建立的文件值为1
  uid_t     st_uid;    //用户ID
  gid_t     st_gid;    //组ID
  dev_t     st_rdev;   //(设备类型)若此文件为设备文件,则为其设备编号
  off_t     st_size;   //文件字节数(文件大小)
  unsigned long st_blksize;  //块大小(文件系统的I/O 缓冲区大小)
  unsigned long st_blocks;  //块数
  time_t    st_atime;   //最后一次访问时间
  time_t    st_mtime;   //最后一次修改时间
  time_t    st_ctime;   //最后一次改变时间(指属性)
};

2.文件类型

  • 普通文件 文本或者二进制
  • 目录文件
  • 块特殊文件 对设备(如磁盘)带缓冲的访问,块特殊文件包含被编号的块,每一块都可以独立地读取或者写入。而且可以定位于任何块,并且开始读出或写入。这些对于字符特殊文件是不可能的
  • 字符特殊文件 对设备不带缓冲的访问,访问长度可变,系统所有设备要么是字符特殊文件,要么是块特殊文件
  • FIFO管道 用于进程通讯
  • 套接字
  • socket
  • 符号连接 这种类型的文件指向另一个文件

3.权限

⑴权限位

用户(读,写,执行)
组(读,写,执行)
其他(读,写,执行)

  • 打开一个文件,需要对上级目录有执行权限,对文件本身有该有的权限
    • 目录的执行权限位常被称为搜索位
    • 目录的读权限和执行权限是由区别的
    • 在目录中创建一个新文件,需要对目录有写权限和执行权限
    • 在目录中删除文件,需简要对目录有写权限和执行权限,对文件不需要权限

⑵进程用户ID和设置组ID

与一个进程关联的ID至少有6个:

  • 实际
    • 实际用户ID
    • 实际组ID
  • 权限
    • 有效用户ID,通常为实际用户ID
    • 有效组ID,通常为实际组ID
    • 附属组ID
  • exec函数保存
    • 保存的设置用户ID
    • 保存的设置组ID

实际用户是用来标识进程是谁,是谁在执行进程,一般是登录用户;而有效用户ID则标识这该进程的访问权限在大多数情况下,实际ID和有效ID一般情况下是一样的,但是在有些情况就是不一样的了,具体可见:

Linux特殊权限位

⑶新文件和目录的所有权

当我们创建新文件时:

  • 新文件用户ID为进程有效有效用户ID
  • 组ID POSIX允许有下面两种方式任意一种设置,Linux默认是目录组ID,目录组ID没有则是进程组ID
    • 进程有效组ID
    • 所在目录组ID

⑷access/faccessat

用实际用户ID和实际组ID进行访问权限测试

⑸unmask

当我们登录系统之后创建一个文件总是有一个默认权限的,那么这个权限是怎么来的呢?这就是umask干的事情。umask设置了用户创建文件的默认权限,它与chmod的效果刚好相反,umask设置的是权限“补码”

⑹chmod/fchmod/fchmodat

修改文件权限,进程的有效用户ID必须等于文件所有者ID,或者该进程必须具有超级用户权限

⑺粘着位

  • 针对目录使用
  • 如果设置了粘着位,只有对该目录具有写权限的用户,并且满足下列条件之一,才能删除或重命名该目录下的文件
    • 拥有此文件
    • 拥有此目录
    • 超级用户
  • 主要是对/tmp和/var/tmp使用

⑺chown/fchown/fchownat/lchown

主要用于更改文件的用户ID和组ID

4.文件长度和截断

st_size表示以字节为单位的文件长度,只针对普通文件,目录文件和符号链接有意义

EOF end-of-file表示文件结束

st_blksize 文件的块大小,文件系统给文件分配空间时的最小单位,例如512字节,这个不固定
st_blocks 文件包含的块数,文件全部内容应该根据st_size读取,而非块大小和块数相乘

文件中的空洞:
lseek函数显示地为一个打开文件设置偏移量,文件偏移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将加长该文件,并在文件中构成一个空洞,这一点是允许的。位于文件中但没有写过的字节都被读为0,空洞是否占用硬盘空间是由文件系统(file system)决定的

3.文件系统

⑴文件系统结构

磁盘可以分成一个或多个分区,每个分区可以包含一个文件系统
在这里插入图片描述

  • i节点就是inode,指向磁盘上该文件存储区,i节点不包含文件名
  • 目录项中包括文件名和i节点

⑵硬链接

多个文件名指向同一索引节点(Inode)就称为硬链接

硬连接的作用是允许一个文件拥有多个有效路径名,这样用户就可以建立硬连接到重要文件,以防止“误删”的功能。其原因如上所述,因为对应该目录的索引节点有一个以上的连接。只删除一个连接并不影响索引节点本身和其它的连接,只有当最后一个连接被删除后,文件的数据块及目录的连接才会被释放。也就是说,文件真正删除的条件是与之相关的所有硬连接文件均被删除。

硬链接的限制:

  • 连接和文件需要位于同一文件系统
  • 只有超级用户才能创建指向目录的硬链接

⑶符号链接(软链接)

软链接文件有类似于Windows的快捷方式。它实际上是一个特殊的文件。在符号连接中,文件实际上是一个文本文件,其中包含的有另一文件的位置信息。

假设符号链接没了,不会影响到原始数据。假设原始数据没了,那我这个符号链接就变成了一张空头支票,也就是悬空的符号链接

软连接可以跨文件系统,可以对目录使用

⑷文件的时间

  • 最后访问时间,read
  • 最后修改时间,write
  • i节点状态的最后更改时间,比如chmod,chown,更改链接数等等。
    系统并不维护对i节点的访问时间,所以access和stat不会影响着三个时间

⑸函数

  • link/linkat 创建硬链接
  • symlink/symlinkat创建符号链接
  • unlink/unlinkat(解除硬链接或者软链接)
  • remove
    • 对于文件,remove功能与unlink相同
    • 对于目录,remove功能与rmdir相同
  • readlink/readlinkat·读取符号链接
  • rename/renameat 重命名
  • futimens,utimensat utimes 一个文件访问和修改时间

4.目录

⑴知识点

  • 有适合权限的任一用户都能读该目录,只有内核才能写目录
  • 一个目录的写权限位和执行权限位决定了该目录中能否创建新文件以及删除文件,它们不表示能否写目录本身

⑵函数

  • mkdir/mkdirat 创建目录,rmdir 删除一个空目录,空目录是只包含.和…的目录
  • chdir/fchdir 更改当前工作目录
  • getcwd获取当前工作目录的绝对路径名

5.设备特殊文件

每个文件系统都构建在存储设备上,位置由主,次设备号确定

主设备号标识设备驱动程序

次设备号标识特定的子设备

比如同一个磁盘驱动器上各文件系统具有相同的主设备号,但是次设备号不同

标准I/O库

1.基础

  • 不仅是UNIX,其它很多操作系统都实现了标准I/O库
  • I/O函数都是围绕文件描述符的,而标准I/O库时围绕着流进行的
  • 一个进程预定义了标准输入,标准输出,标准错误
  • 流的定向决定了读写字符的是单字节还是多字节,当一个流最初被创建时,它并没有定向
    • freopen 清除流的定向
    • fwide 设置流的定向,它并不改变已定向流的定向
  • 缓冲
    • 全缓冲,除非进行flush,否则缓冲满进行实际IO将内容写到硬盘,除了打开至终端设备的流和标准错误以外的一般是全缓冲的
    • 行缓冲,填满缓冲区或者遇到换行符进行IO,打开至终端设备的流一般是行缓冲的
    • 不带缓冲,一般情况标准错误是不带缓冲的
    • setbuf/setvbuf 更改缓冲类型,一定要在流已被打开后调用
    • 按照一定的格式输入和输出叫格式化输入和输出
      • 格式化输入主要是scanf等函数
      • 格式化输出主要是printf等函数

4.打开流/关闭流

fopen/freopen/fdopen 打开流,若成功,返回文件指针,出错返回NULL

fclose 关闭流,若成功,返回0,若出错,返回EOF

5.读和写流

  • 每次一个字符读写,如果流式带缓冲的,则标准I/O函数处理所有缓冲

    • 输入函数 getc fgetc getchar
    • 输出函数 putc fputc putchar
  • 每次一行读写

    • 输入一行fgets/gets,gets(已弃用)从标准输入读,fgets从指定的流读
    • 输出一行fputs/puts,puts(已弃用)写到标准输出,fputs写到指定的流
  • 直接IO,fread/fwrite

7.二进制IO

如果进行二进制I/O操作,我们更愿意一次读或写一个完整的结构,因为有了下面两个函数

  • fread 读取
  • fwrite 写入

二进制IO在跨系统时要有互认的规范模式,也就是网络协议交换二级制数据的某些技术

8.定位流

  • ftell fseek 它们都嘉定文件的位置可以存放在一个长整形中
  • ftello fseeko Single UNIX Specification引入
  • fgetpos fsetpos IOSC引入,需要一直到非UNIX系统的应该使用它们

10.fileno

标准I/O库最终都调用I/O例程,每个标准I/O流都有一个与其相关的文件描述符,我们可以使用fileno方法来获取描述符

11.创建临时文件

  • tmpnam 返回指向唯一路径名的指针
  • tmpfile 返回文件指针

12.内存流

内存流只访问主存,不访问磁盘上的文件,所以性能更高

13.扩展

标准IO效率不高,每当使用fgets的时候,需要复制两次数据,一次是内核和标准IO缓冲区,第二次是标准IO缓冲区到用户程序的行缓冲区,后来有了fio,sfio,ASI等使性能大大的提升

系统数据文件

1.口令文件

加密口令存放位置/etc/passwd,里面大多数有7个字段,字段之间用冒号分割
在这里插入图片描述

root:*:0:0:System Administrator:/var/root:/bin/sh

我们可以看到,加密口令字段用*来代替

为了防止用户暴力破解加密口令,很多系统将加密口令存放到一个叫阴影口令/etc/shadow的文件中,里面存放了用户登录名和加密口令

阴影口令文件不是一般用户可以读取的

2.组文件/附属组ID

组文件就是维护组用户信息的地方

在很早以前,一个用户只能属于一个组,后来有了附属组ID的概念,一个用户不仅可以属于口令文件中组ID对应的组,还可以属于至多16个另外的组,这时候对文件访问权限检查时,文件组ID不仅比较进程有效组ID,还会和附属组ID比较

使用附属组ID的优点是不需要经常更改组

3.其它数据文件

在这里插入图片描述
一般情况下,每个数据文件都包含三个函数:

  • get 读
  • set 写
  • end 关闭

5.其它

  • 登录账户信息
    • utmp 记录登录到系统的各个用户
    • wtmp 登录和注销事件记录
  • 系统标识
    • uname 返回主机和操作系统有关信息

进程环境

1.基本知识

  • C程序总是从main函数启动
  • 终止有多种方式,比如exit,调用abort等等,分为正常终止和异常终止
  • 当执行一个程序时,调用EXEC的可将命令行参数传递给该新程序
  • 跨函数跳转setjmp和longjmp
  • 每个进程都有一组资源显示,可以使用getrlimit和setrlimit查询和修改

2.C程序的存储空间布局

  • 正文段,CPU执行的机器指令部分,一般是只读可共享的
  • 初始化数据段,C程序中函数之外的赋值声明
  • 未初始化数据段,一般初始化为0或空指针

需要存放在磁盘程序文件中的段只有正文段和初始化数据段

3.共享库

共享库使得可执行文件中不再需要包含共用的库函数,而只需在所有进程都可引用的存储区保存这种库例程的一份副本,程序第一次执行或者第一次调用某个库函数时,用动态链接方法将程序与共享库函数相链接

4.存储空间分配

①ISO C标准下的

  • malloc 分配指定字节数的存储区,此存储区初始值不确定
  • calloc 为指定数量的对象分配存储空间,每一位都初始化为0
  • realloc 修改存储区长度
  • free 释放

②进阶的

  • alloca 在栈上分配,自动释放

5.环境变量

这里修改不会影响父进程,只会影响自己和子进程

  • getenv 取其环境变量值
  • putenv 以前有,就会删除以前的
  • setenv 根据rewrite来判断是否删除以前的
  • unsetenv 删除

进程控制

1.基本知识

  • 每个进程都有一个非负整形表示其唯一进程ID,进程ID终止后过段时间可以复用,但是一般UNIX系统会稍微等等再复用
  • ID为0的是系统进程,它是内核进程
  • ID是1的是init进程,位于/etc/init或/sbin/init,负责在自举过后启动一个UNIX系统,它通常读取与系统有关的初始化文件/etc/rc*或/etc/inittab以及/etc/init.d,它是一个不会终止的普通用户进程
  • strlen计算不包含终止null字节,sizeof会包含终止null字节
  • 一个已经终止,但是其父进程还没有对它做善后处理是僵尸进程,要注意这和进程收养没有任何关系
  • 父进程在子进程之前终止,子进程的父进程就会变成ID为1的init进程,init进程收养的进程如果终止,init就会调用一个wait函数来取的终止状态,防止出现僵尸进程
  • system函数能够执行函数参数中的命令
  • 可以通过getlogin函数获取用户的登录名
  • 进程可以通过nice函数调整优先级,进程只能影响自己的NICE值
  • 进程需要熟练掌握的函数
    • fork
    • exec
    • _exit
    • wait
    • waitpid

2.fork

创建的新进程为子进程,fork函数被调用后返回两次,父进程返回新建子进程ID,子进程返回ID0,这个0可不是上面所说的系统进程,子进程获得父进程数据空间,堆,栈的副本,但是他们并不共享这些存储空间部分,他们共享正文段

这里有引入了一个写时复制的概念,也就是说,fork子进程先和父进程共享存储空间,写的时候再真正做副本,有的时候父进程fork时也会将缓冲区内容复制到子进程

后来有了clone函数,可以控制共享范围

fork的一个特征是父进程的所有打开文件描述符都被赋值到子进程中,父进程和子进程每个相同的打开描述符共享一个文件表项

父进程和子进程共享同一个文件偏移量

fork的常见用法:

  • 父进程接受网络请求,子进程处理网络请求

vfork函数用于创建一个新进程,而新进程的目的是exec一个新程序,它可以保证子进程先运行

3.wait

当一个进程终止时,内核会向父进程发送信号,这种信号的系统默认动作是忽略它

当父进程调用wait时:

  • wait 阻塞到有一个子进程终止。没有子进程的会出错
  • waitpid 可以指定进程号,也可以选择不阻塞
  • waitid 可以指定进程组号的所有子进程
  • wait3/wait4 可以返回由终止进程机器所有子进程使用的资源概况

4.exec

在这里插入图片描述
在执行exec函数时,如果filename包含/,则视其为路径名,否则就按PATH环境变量处理,在它所指定的各目录中搜寻可执行文件,PATH变量用:分割

大部分UNIX实现中,只有execve是内核系统的调用,另外6个只是库函数,他们最终都是调用execve函数

5.用户ID和组ID

在设计应用时,一般试图使用最小特权模型
在这里插入图片描述

6.解释器文件

shell脚本就是一个解释器文件

exec函数执行的是改解释器文件中第一行pathname的文件

7.进程会计

/var/account/paccount文件中会记录每个结束进程的信息,在进程结束时会按终止顺序会进行记录

acct函数用启动和禁用进程会计,会计记录写到指定的文件中:

  • 在mac系统中为 /var/account/acct
  • 在linux系统中为 /var/account/pacct

会计进程对应的是进程,而不是程序,如果一个进程执行三个程序,只会生成一个会计记录

8.进程时间

time函数可以获得它自己以及终止子进程的:

  • 墙上时钟时间
  • 用户CPU时间
  • 系统CPU时间

进程关系

1.终端登录

UNIX目前已经支持多种身份的方式,比如PAM,允许管理人员来灵活的配置登录权限,这里只说传统的终端登录方式:

  • 在/etc/ttys或者/etc/inittab中,每个终端设备都有一行
  • 系统自举时,内核创建进程ID为1的init进程
  • init读取文件/etc/ttys或者/etc/inittab,对每一个终端设备调用一次fork
  • fork出来的子进程执行getty程序
  • 输出login之类的信息等待用户键入,之后调用登录程序,然后输入密码
  • 登录陈宫,将工作目录更改为该用户的起始目录等

2.网络登录

系统使用伪终端的软件驱动程序,使他同一个软件既能处理终端登录,又能处理网络登录

在系统中,有一个inetd/xinetd进程来等待大多数网络连接,它等待TCP/IP连接请求,当一个连接请求到达时,它执行一次fork,然后生成的子进程来exec适当的程序

我们以telnet为例:
在这里插入图片描述在上图的过程之后,telnetd进程打开一个伪终端设备,然后分成两个进程,一个处理网络连接,一个执行login程序,它们通过伪终端相连

在这里插入图片描述

3.进程组

通常,多个进程组合起来可以形成一个一起干活的进程组,每个进程组有个组长进程,组长进程的进程ID等于进程组ID

进程组的目的是为了完成一个任务,同时管理多个进程的启动终止等,进程有且只有一个进程组

只要某个进程组有一个进程存在,该进程组就存在,和组长进程无关,进程也可以转移到另外的进程组

4.会话

会话是一个或多个进程组的集合,当一个用户登录一次系统就形成一次会话,一个会话可包含多个进程组,但只有一个前台进程组,进程组组长进程不能创建新的会话

5.控制终端

  • 一个会话可能有一个控制终端,也最多有一个控制终端
  • 一个会话有一个控制终端,则它又一个前台进程组,其它进程为后台进程组
  • 通常,登录时自动建立控制终端

6.作业控制

  • Shell分前后台来控制的不是进程而是作业(Job)或者进程组(Process Group)。一个前台作业可以由多个进程组成,一个后台也可以由多个进程组成,Shell可以运行一个前台作业和任意多个后台作业,这称为作业控制。
  • 作业与进程组的区别:如果作业中的某个进程又创建了子进程,则子进程不属于作业。一旦作业运行结束,Shell就把自己提到前台,如果原来的前台进程还存在(如果这个子进程还没终止),它自动变为后台进程组。
  • 一个作业通常是一个进程管道
  • 挂起,退出,中断只影响前台进程
  • 后台作业不能读取标准输入和标准输出

7.shell执行程序

Shell调用本质其实是由shell(该系统的shell类型为bash)调用fork()函数创建一个进程,由该创建的子进程去调用一种exec函数去执行另一个程序。当子进程调用一种exec函数时,该子进程的执行的程序完全替换为新进程,因为调用exec函数并不创建新进程,所有前后进程的进程ID并不改变

8.孤儿进程组

该进程组的每个成员的父进程要么是该组的成员,要么在其它会话中
在这里插入图片描述
进程组1不是孤儿进程组,进程组2是孤儿进程组

9.守护进程

守护进程没有控制终端,大部分守护进程都是以超级用户特权运行

常见的守护进程

  • rpcbind 将远程过程调用程序号映射为网络端口号
  • cron 定时任务

处理出错消息:
syslog来处理出错的消息

守护进程的惯例:

  • 锁文件存放在/var/run下面
  • 配置文件在/etc/xxx.conf,而且修改配置文件对守护进程不一定生效
  • 守护进程可以在/etc/inittab配置重新启动守护进程

信号

1.信号基础

  • 信号是软件中断
  • 信号都有名字,以SIG开头
  • Linux3.2.0支持31种信号
  • 信号在signal.h中被编号成正整数常量
  • 不存在编号为0的信号
  • 信号处理方式:
    • 忽略,SIGKILL和SIGSTOP不能忽略
    • 捕获,然后使用函数处理
    • 执行系统默认动作,大部分系统默认动作是终止
  • UNIX系统信号
    在这里插入图片描述

2.函数signal

设置如何处理信号

在不同的实现有不同的语义,推荐使用sigaction函数

  • 当执行一个程序时,所有信号的状态都是系统默认或忽略
  • 进程fork时,子进程进程父进程的信号处理方式

3.中断的系统调用

早期UNIX系统如果一个进程在执行一个低速系统调用而阻塞期间收到一个信号,则系调用不再继续执行,返回出错,这个就唤醒了一个阻塞的系统调用

低速系统调用是指可能会使进程永远阻塞的一类系统调用,比如读一个数据不存在管道等等

注意:与磁盘IO相关的系统调用,只会暂时阻塞调用者,除非发生硬件错误,一般会很快返回

后来在Liunx中,当信号处理程序时signal函数时,被中断的系统调用会重启动,在sigaction中,是可选的

4.可重入函数

当一个进程正在执行malloc分配存储空间,而此时捕获信号又执行malloc,那么很可能对进程造成破坏。

在unix中,有很多可以保证信号调用安全的函数叫做可重入函数

5.SIGCHLD

SIGCHLD 是 BSD的SIGCHLD信号,POSIX.1采用它

子进程状态改变后产生此信号,父进程需要调用一个wait函数检测发生了什么

SIGCHLD在目前的UNIX实现中是可以忽略的

6.信号集

首先来看看什么是信号屏蔽字:

每个进程都有一个信号屏蔽字,它规定了当前要阻塞递送到该进程的信号集

信号集,是为了批量管理信号,在linux中,它的类型是sigset_t,大小是64bits,因为目前linux流行版本一共有64个信号(不同版本信号格式不同),我们一个bit来表示一种信号

信号集第一位就是1号信号(SIGHUP),第二位就是2号信号(SIGINT)….以此类推(我们假设位号是从1开始,而不是从0开始)。处理信号集的函数

  • sigemptyset 初始化信号集,全都设置为0
  • sigfillset 初始化信号集,全都设置为1
  • sigaddset 将一位设置为1
  • sigdelset 关闭一位
  • sigismember 测试一位

5.作业控制信号

  • SIGCHLD 子进程已停止或终止
  • SIGCONT 如果进程停止,则继续运行★
  • SIGSTOP 停止信号
  • SIGTSTP 交互式停止信号
  • SIGTTIN 后台进程组成员读控制终端
  • SIGTTOU 后台进程组成员写控制终端

除了SIGCHLD以外,交互式SHELL通常会处理这些信号的所有工作,比如挂起或恢复作业

6.信号名和编号

linux中提供一个数组,下标是信号编号,值是信号名

7.主要函数(待整理)

signal

signal函数由ISO C定义,是系统信号机制最简单的接口,但是signal的语义和实现有关,最好使用sigaction函数代替signal函数

kill/raise

  • kill将信号发送给进程或进程组,kill的参数有多种,分别向指定进程等等发送信号
  • raise允许进程向自身发送信号

进程将信号发送给其它进程需要权限,如果被发送的信号是SIGCONT,则进程可将它发送给属于同一会话的任意其它进程

alarm/pause

  • alarm的作用是设置定时器,每个进程只能设置一个
  • pause使进程挂起直到捕获到一个信号

sigprocmask

检测和更改进程的信号屏蔽字

sigpending

返回一个信号集,对于调用进程而言,其中的信号是阻塞不能递送的,因为也一定是未决的

sigaction

signal的替代品

sigsetjmp/siglongjmp

sigsuspend

abort

使程序异常终止

system

sleep/nanosleep/clock_nanosleep

sigqueue

线程

1.基础概念

  • 就算是单核系统,多线程程序也也可以改善相应时间和吞吐量
  • 线程也有ID,但只在所属进程上下文里面才有意义
  • 线程ID使用pthread_t数据类型表示,因而比较线程ID相等不能仅仅按整数比较,应该调用pthread_equal函数
  • 线程可以通过pthread_self函数获得自身的线程ID
  • 可以调用pthread_create方法创建线程,创建线程的时候可以指定线程属性
  • 新创建的线程会继承调用线程的浮点环境和信号屏蔽字,但是挂起信号集不继承
  • 如果进程中的任一线程使用了exit, _Exit或者_exit,那么整个进程就终止了
  • 在不终止进程的情况下线程可以有三种方式退出
    • 正常结束
    • 被同一进程其他线程取消
    • 调用pthread_exit
  • pthread_join()函数,以阻塞的方式等待thread指定的线程结束。当函数返回时,被等待线程的资源被收回
  • 一个线程可以调用pthread_cancel请求终止同一进程中的另一个线程,注意是请求!!!
  • 线程同进程一样,也可以有自己的退出钩子函数,而且可以有多个
  • 线程和进程的比较
    在这里插入图片描述
    • unix线程的pthread有两种状态,joinable状态和unjoinable状态
      • joinable状态,线程退出不会释放资源,直到调用pthread_join
      • unjoinable状态,退出就释放资源
      • unjoinable状态可以创建时指定,也可以调用pthread_detach函数讲状态改变成unjoinable状态,省去了给线程释放资源的麻烦

2.线程同步

⑴互斥量

互斥量具有一些属性,通过修改这些属性可以控制锁的一些行为,缺省的互斥量属性如下

  • pshared 是否多个进程可以共享,默认是同一进程中的线程共享的
  • type 它的具体属性如下:
    在这里插入图片描述
  • protocol 线程的优先级和调度相关的属性
  • prioceiling 优先级上限属性
  • robustness 定义了互斥锁持有者挂了怎么办

⑵读写锁

读写锁也叫共享互斥锁,适合读远大于写的情况

读写锁多个线程可以同时占有读模式的锁,只有一个线程可以占用写模式的锁。有读的就不能写,有写的就不能读

读写锁也有带有超时模式的加锁函数

⑶条件变量

条件变量和互斥量一起使用时,允许线程以无竞争的方式等待特定条件发生

⑷自旋锁

自旋锁等待锁的线程不是休眠,而是一个劲的忙等(自旋),自旋锁是一种偏底层的锁,通常用于实现其它类型的锁

自旋锁可以减少线程重新调度,但是占用CPU,所以只适合自旋一小段时间

⑸屏障

是用户协调多个线程并行工作的同步机制

屏障相当于一个线,当所有合作线程到达这个线时,在继续执行

线程控制

1.线程限制

UNIX环境线程的限制:
在这里插入图片描述
UNIX不同实现对线程的限制,不确定不代表没有!
在这里插入图片描述
我们可以通过sysconf函数来查询线程操作的一些限制

2.线程属性

6.线程同步属性

⑴互斥量属性

⑵读写锁属性

⑶条件变量属性

⑷屏障属性

7.重入

如果一个函数对多个线程是可重入的,就说这个函数是线程安全的。

8.线程私有数据

高级IO

1.非阻塞IO

使

2.字节范围锁

当一个进程正在读或修改文件某个部分时,字节范围锁会组织其他进程修改同一文件区,字节记录锁有很多实现的方法:

  • fcntl
  • lockf
  • flock

共享读锁和独占性写锁:

  • 多个读可以用一把共享读锁
  • 写锁只能加一把
  • 读写锁不能同时来

3.多路转接

⑴轮训

就是挨个文件轮着问,浪费CPU时间,在多任务系统中应当避免

⑵IO多路转接

构造一个需要监听的描述符列表,然后执行一个函数,直到列表有一个函数准备好了函数再返回

可以实现I/O多路复用的函数:

  • select
  • pselect
  • poll

4.异步IO

描述符准备好的时候通知进程, 异步IO编写成本很高

另外我们要知道,同步IO只是相对于程序是同步的

5.readv/writev

读写多个非连续的缓冲区

6.readn/writen

7.存储映射IO

能将一个磁盘文件映射到存储空间的一个缓冲区上,此时读取缓冲区就相当于读取文件,存入缓冲区就相当于存入文件。

mmap是负责存储映射的函数

进程通讯

1.概述

IPC是各种进程通讯方式的统称

不同的UNIX实现的IPC并不完全相同

在这里插入图片描述

2.管道

所有的UNIX实现都支持管道,半双工管道是最常用的IPC形式

管道的局限性:

  • 大部分系统管道只支持半双工的,那么为了可移植性,我们只能认为它是半双工的
  • 管道只能在具有公共祖先的两个进程之间使用,通常两个进程是fork关系

基于管道的局限性

  • FIFO 没有第二种局限性
  • UNIX域套接字两种局限性都没有

我们为shell命令创建一个进程,前一条命令标准输出是后一个的标准输入

管道是通过pipe函数创建的,下面是一个标准的管道
在这里插入图片描述

  • 标准IO库提供了popen和pclose来建立管道

协同进程

几个过滤程序通常在shell管道中线性连接,当一个过滤程序即产生某个过滤程序的输入,又读取该过滤程序的输出时,它就变成了协同进程

在这里插入图片描述

3.FIFO

未命名管道只能在两个相关的进程之间使用,通过FIFO,不相关的进程也能交换数据,FIFO被叫做命名管道

创建FIFO

  • mkfifo
  • mkfifoat

应用程序可以通过mknod和mknodat函数来创建FIFO

FIFO的用途

  • FIFO将数据从一条管道传送到另一条是,无需创建中间临时文件
  • 客户进程-服务器进程应用程序中,FIFO用作聚合点,在客户进程和服务器进程两者之间传递数据

4.XSI

  • 消息队列
  • 信号量
  • 共享存储器

XSI IPC是基于System V的IPC函数的,由于XSI IPC不使用文件系统命名空间,而是构造了自己的命名空间,因而长期受批评

标识符和键

每个内核的IPC结构都有一个非负整数的标识符加以应用,标识符是IPC对象的内部名,每个IPC对象都与一个键关联,这个键就是它的外部名,创建IPC时都应指定一个键

消息队列

消息队列是消息的链接表,存储在内核中,由消息队列标识符标识

  • msgget 创建一个新队列或打开一个现有队列
  • msgsnd将新消息添加到队列尾端
  • msgrcvy用于从队列中取消息

信号量

它是一个计数器,用于为多个进程提供对共享对象的访问

共享存储

允许两个或多个进程共享一个给定的存储区,这是最快的一种IPC

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【unix】unix环境高级编程 的相关文章

  • 寻找奇数出现II

    给定一个整型数组arr xff0c 其中有两个数出现了奇数次 xff0c 其他的数都出现了偶数次 xff0c 找到这两个数 要求时间复杂度为O N xff0c 额外空间复杂度为O 1 给定一个整形数组arr 及它的大小n xff0c 请返回
  • 用JQuery去实现单个表格中的td数据修改

    需要学习资料的 43 微信公众号 学习资源后台找我 本人比较忙 我看到了会在后台帮你 xff0c 谢谢关注啦 JS代码如下 xff1a document ready function var tds 61 34 td 34 tds clic
  • 二维数组中的查找

    题目描述 在一个二维数组中 xff0c 每一行都按照从左到右递增的顺序排序 xff0c 每一列都按照从上到下递增的顺序排序 请完成一个函数 xff0c 输入这样的一个二维数组和一个整数 xff0c 判断数组中是否含有该整数 思路 xff1a
  • 替换空格

    题目描述 请实现一个函数 xff0c 将一个字符串中的空格替换成 20 例如 xff0c 当字符串为We Are Happy 则经过替换之后的字符串为We 20Are 20Happy 思路 xff1a s表示空白字符也就是空格 xff0c
  • 从尾到头打印链表

    题目描述 输入一个链表 xff0c 从尾到头打印链表每个节点的值 输入描述 输入为链表的表头 输出描述 输出为需要打印的 新链表 的表头 思路 xff1a 创建一个栈stack将链表依次倒入然后再依次倒出就可以了 xff1b 代码如下 xf
  • 用两个栈实现队列

    题目描述 用两个栈来实现一个队列 xff0c 完成队列的Push和Pop操作 队列中的元素为int类型 思路 xff1a 用两个栈stack1和stack2去实现队列 xff0c 先将所有数据倒入stack1中 xff0c 这叫入队 xff
  • 百度2014校园招聘笔试题武汉站三道算法设计题

    百度2014校园招聘笔试题武汉站三道算法设计题 1 给定任意一个整整数 求比这个数大且最小的不重复数 就是相邻两位不同 xff0c 例如1231 如1101就是重复数 解 xff1a 思路 xff1a 每次将给定的值加上1 xff0c 然后
  • 旋转数组的最小数字

    题目描述 把一个数组最开始的若干个元素搬到数组的末尾 xff0c 我们称之为数组的旋转 输入一个递增排序的数组的一个旋转 xff0c 输出旋转数组的最小元素 例如数组 3 4 5 1 2 为 1 2 3 4 5 的一个旋转 xff0c 该数
  • 斐波那契数列

    题目描述 大家都知道斐波那契数列 xff0c 现在要求输入一个整数n xff0c 请你输出斐波那契数列的第n项 n lt 61 39 思路 xff1a 用非递归的方法 xff0c 即遍历的方法去实现斐波拉切数列 代码如下 xff1a pub
  • 跳台阶

    题目描述 一只青蛙一次可以跳上1级台阶 xff0c 也可以跳上2级 求该青蛙跳上一个n级的台阶总共有多少种跳法 思路 xff1a 对于本题 前提只有 一次 1阶或者2阶的跳法 a 如果两种跳法 xff0c 1阶或者2阶 xff0c 那么假定
  • 变态跳台阶

    题目描述 一只青蛙一次可以跳上1级台阶 xff0c 也可以跳上2级 它也可以跳上n级 求该青蛙跳上一个n级的台阶总共有多少种跳法 思路 xff1a 关于本题 xff0c 前提是n个台阶会有一次n阶的跳法 分析如下 f 1 61 1 f 2
  • 矩形覆盖

    题目描述 我们可以用2 1的小矩形横着或者竖着去覆盖更大的矩形 请问用n个2 1的小矩形无重叠地覆盖一个2 n的大矩形 xff0c 总共有多少种方法 xff1f 思路 xff1a 依旧是斐波那契数列 2 n的大矩形 xff0c 和n个2 1
  • 数值的整数次方

    题目描述 给定一个double类型的浮点数base和int类型的整数exponent 求base的exponent次方 思路 xff1a 判断该整数的负数和正数情况 代码如下 xff1a public class Solution publ
  • 调整数组顺序使奇数位于偶数前面

    题目描述 输入一个整数数组 xff0c 实现一个函数来调整该数组中数字的顺序 xff0c 使得所有的奇数位于数组的前半部分 xff0c 所有的偶数位于位于数组的后半部分 xff0c 并保证奇数和奇数 xff0c 偶数和偶数之间的相对位置不变
  • 链表中倒数第k个结点

    题目描述 输入一个链表 xff0c 输出该链表中倒数第k个结点 思路 xff1a 倒数第k个结点 xff0c 则表示是第n k 43 1个结点 代码如下 xff1a public class ListNode int val ListNod
  • 反转链表

    题目描述 输入一个链表 xff0c 反转链表后 xff0c 输出链表的所有元素 思路 xff1a 用一个pre指向前一个结点 xff0c 用 next指向当前结点 next 61 head next head next 61 pre pre
  • 程序员面试题精选100题(46)-对称子字符串的最大长度

    程序员面试题精选100题 46 xff0d 对称子字符串的最大长度 题目 xff1a 输入一个字符串 xff0c 输出该字符串中对称的子字符串的最大长度 比如输入字符串 google xff0c 由于该字符串里最长的对称子字符串是 goog
  • 合并两个排序的链表

    题目描述 输入两个单调递增的链表 xff0c 输出两个链表合成后的链表 xff0c 当然我们需要合成后的链表满足单调不减规则 思路 xff1a 先创建一个头结点 head xff0c head val为 1 然后创建一个指向该头结点的指针p
  • 树的子结构

    题目描述 输入两棵二叉树A xff0c B xff0c 判断B是不是A的子结构 xff08 ps xff1a 我们约定空树不是任意一个树的子结构 xff09 思路 xff1a 可以先判断A和B的父结点是不是一样的 xff0c 如果一样进入递
  • 二叉树的镜像

    题目描述 操作给定的二叉树 xff0c 将其变换为源二叉树的镜像 输入描述 二叉树的镜像定义 xff1a 源二叉树 8 6 10 5 7 9 11 镜像二叉树 8 10 6 11 9 7 5 思路 xff1a 根节点下面的左子树和右子树分别

随机推荐

  • 顺时针打印矩阵

    题目描述 输入一个矩阵 xff0c 按照从外向里以顺时针的顺序依次打印出每一个数字 xff0c 例如 xff0c 如果输入如下矩阵 xff1a 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1
  • 包含min函数的栈

    题目描述 定义栈的数据结构 xff0c 请在该类型中实现一个能够得到栈最小元素的min函数 思路 xff1a 用一个栈去存储所有元素 然后一个一个去比较 将小的那个值放到变量min里面 xff1b 代码如下 xff1a import jav
  • 栈的压入、弹出序列

    题目描述 输入两个整数序列 xff0c 第一个序列表示栈的压入顺序 xff0c 请判断第二个序列是否为该栈的弹出顺序 假设压入栈的所有数字均不相等 例如序列1 2 3 4 5是某栈的压入顺序 xff0c 序列4 xff0c 5 3 2 1是
  • 从上往下打印二叉树

    题目描述 从上往下打印出二叉树的每个节点 xff0c 同层节点从左至右打印 思路 xff1a 意思就是按层遍历然后放到一个list集合里面去 xff0c 所以创建一个队列每次把一层的结点放进去 xff0c 然后一个一个判别是否有left结点
  • 二叉搜索树的后序遍历序列

    题目描述 输入一个整数数组 xff0c 判断该数组是不是某二叉搜索树的后序遍历的结果 如果是则输出Yes 否则输出No 假设输入的数组的任意两个数字都互不相同 思路 xff1a 因为是二叉搜索树 xff0c 所以根节点的左子树小于右子树 x
  • 二叉树中和为某一值的路径

    题目描述 输入一颗二叉树和一个整数 xff0c 打印出二叉树中结点值的和为输入整数的所有路径 路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径 思路 xff1a 用先序遍历递归的思想去实现 xff0c 到最后叶节点如果不能
  • 复杂链表的复制

    题目描述 输入一个复杂链表 xff08 每个节点中有节点值 xff0c 以及两个指针 xff0c 一个指向下一个节点 xff0c 另一个特殊指针指向任意一个节点 xff09 xff0c 返回结果为复制后复杂链表的head xff08 注意
  • ubantu20下python安装和卸载

    查看系统版本 python3 version 卸载ubantu上的python版本 sudo apt get remove python3 卸载python3及其依赖 sudo apt get remove auto remove pyth
  • [转]DBSCAN聚类算法——机器学习(理论+图解+python代码)

    原文链接 xff1a https blog csdn net huacha article details 81094891 一 前言 二 DBSCAN聚类算法 三 参数选择 四 DBSCAN算法迭代可视化展示 五 常用的评估方法 xff1
  • 求1+2+3+...+n

    题目描述 求1 43 2 43 3 43 43 n xff0c 要求不能使用乘除法 for while if else switch case等关键字及条件判断语句 xff08 A B C xff09 思路 xff1a 用递归 xff08
  • 把字符串转换成整数

    题目描述 将一个字符串转换成一个整数 xff0c 要求不能使用字符串转换整数的库函数 思路 xff1a 设置两个标志位 一个tag 为1表示是正数 xff0c 为0表示是负数 xff0c 一个index xff0c 为 43 则index是
  • 数组中重复的数字

    题目描述 在一个长度为n的数组里的所有数字都在0到n 1的范围内 数组中某些数字是重复的 xff0c 但不知道有几个数字是重复的 也不知道每个数字重复几次 请找出数组中任意一个重复的数字 例如 xff0c 如果输入长度为7的数组 2 3 1
  • 表示数值的字符串

    题目描述 请实现一个函数用来判断字符串是否表示数值 xff08 包括整数和小数 xff09 例如 xff0c 字符串 34 43 100 34 34 5e2 34 34 123 34 34 3 1416 34 和 34 1E 16 34 都
  • 字符流中第一个不重复

    题目描述请实现一个函数用来找出字符流中第一个只出现一次的字符 例如 xff0c 当从字符流中只读出前两个字符 34 go 34 时 xff0c 第一个只出现一次的字符是 34 g 34 当从该字符流中读出前六个字符 google 34 时
  • 链表中环的入口结点

    需要学习资料的 43 微信公众号 学习资源后台找我 本人比较忙 我看到了会在后台帮你 xff0c 谢谢关注啦 题目描述 一个链表中包含环 xff0c 请找出该链表的环的入口结点 思路 xff1a 设置两个引用 A和B 指向头 xff0c 然
  • 删除链表中重复的结点

    需要学习资料的 43 微信公众号 学习资源后台找我 本人比较忙 我看到了会在后台帮你 xff0c 谢谢关注啦 题目描述 在一个排序的链表中 xff0c 存在重复的结点 xff0c 请删除该链表中重复的结点 xff0c 重复的结点不保留 xf
  • 二叉树的下一个结点

    需要学习资料的 43 微信公众号 学习资源后台找我 本人比较忙 我看到了会在后台帮你 xff0c 谢谢关注啦 题目描述 给定一个二叉树和其中的一个结点 xff0c 请找出中序遍历顺序的下一个结点并且返回 注意 xff0c 树中的结点不仅包含
  • 按之字形顺序打印二叉树

    需要学习资料的 43 微信公众号 学习资源后台找我 本人比较忙 我看到了会在后台帮你 xff0c 谢谢关注啦 import java util ArrayList import java util Queue import java uti
  • 对称的二叉树

    需要学习资料的 43 微信公众号 学习资源后台找我 本人比较忙 我看到了会在后台帮你 xff0c 谢谢关注啦 请实现一个函数 xff0c 用来判断一颗二叉树是不是对称的 注意 xff0c 如果一个二叉树同此二叉树的镜像是同样的 xff0c
  • 【unix】unix环境高级编程

    文章目录 1 UNIX基础知识1 基本知识2 文件和目录3 输入和输出4 程序和进程5 出错处理6 用户标识7 信号8 时间9 系统调用和库函数 标准化和实现1 标准化 ISO C POSIX Single UNIX Specificati