一,linux man命令
1、什么是 man?
man = manual(手册),是 Linux 中查看命令、函数、配置文件等文档的工具。
2、基本用法
man <命令/函数>
3、man 的章节(最重要!)
|---------------|------------------------------------|
| 内容 | 示例 |
| 用户命令(可执行程序) | man ls, man cat |
| 系统调用(内核提供的函数) | man 2 open, man 2 read |
| 库函数(C标准库等) | man 3 printf, man 3 malloc |
| 特殊文件(设备文件) | man 4 tty |
| 文件格式和约定 | man 5 passwd, man 5 fstab |
| 游戏 | |
| 杂项(惯例、协议等) | man 7 man-pages |
| 系统管理员命令 | man 8 ifconfig |
| 内核例程(非标准) | |
4、常用操作
4.1 指定章节查看
man <章节号> <名称>
示例:
bash
man 1 open # 查看open命令(章节1)
man 2 open # 查看open系统调用(章节2,我们写文件IO用的)
man 3 printf # 查看printf库函数(章节3)
```### 2. 搜索所有章节
```bash
man -a <名称> # 逐个显示所有匹配的手册页
man -k <关键字> # 搜索包含关键字的手册页(= apropos)
4.2. 只看简短描述
bash
whatis <命令> # 显示手册页的单行描述
5. 在 man 中的导航(阅读时的按键)
|----------------|--------------|
| 按键 | 功能 |
| Space/PageDown | 向下翻页 |
| b/PageUp | 向上翻页 |
| q | 退出 |
| /pattern | 向下搜索 pattern |
| n | 下一个搜索结果 |
| N | 上一个搜索结果 |
| g/Home | 跳到开头 |
| G/End | 跳到结尾 |
| h | 显示帮助 |
6. 实战:用 man 学文件IO
6.1 查看 open 系统调用
bash
man 2 open
你会看到:
-
头文件:
#include <fcntl.h> -
函数原型
-
参数说明(flags 的各种取值)
-
返回值
-
错误码
6.2. 查看 read 系统调用
bash
man 2 read
6.3. 查看目录操作
bash
man 3 opendir # 查看 opendir 库函数
man 3 readdir # 查看 readdir
6.4. man man - 查看 man 自己的手册
bash
man man
7. 快速示例
bash
man 2 write # 正确,看系统调用
man write # 可能会看到命令,不是我们要的
二、 open函数打开文件
open函数(补充)

cpp
#include <stdio.h> // 标准输入输出,printf等
#include <errno.h> // 错误号,errno变量
#include <string.h> // 字符串函数,strerror等
#include <unistd.h> // UNIX标准函数,read/write/close/sleep
/*
* ./open 1.txt //运行方式:./open 1.txt
* argc = 2 //argc = 2:有2个参数(程序名+文件名)
* argv[0] = "./open" //argv[0]:程序名 "./open"
* argv[1] = "1.txt" //argv[1]:文件名 "1.txt"
*/
int main(int argc, char **argv)
{
int fd;
if (argc != 2) //检查用户是否提供了文件名- 如果没有给文件名(argc不等于2),打印使用说明
{
printf("Usage: %s <file>\n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDWR);//argv[1]:要打开的文件名,O_RDWR:以读写模式打开,返回值赋值给 fd(文件描述符)
if (fd < 0)//打开失败
{
printf("can not open file %s\n", argv[1]);
print[]f("errno = %d\n", errno);
printf("err: %s\n", strerror(errno));
perror("open");
}
else//打开成功
{
printf("fd = %d\n", fd);
}
while (1)
{
sleep(10);
}
close(fd);//关闭文件
return 0;
}

在实战过程中遇到不了解的函数,可以直接使用man命令查询

命令逐行解释:
- 运行程序测试
cpp
./open open.c
/*
输出: fd = 3
运行程序,成功打开文件
返回的文件描述符是 3
*/
- 后台运行程序
cpp
./open ./open.c &
/*
输出: [1] 4669
符号 说明
& 后台运行(程序在后台跑,不占终端)
[1] 作业号
4669 进程ID (PID)
*/
- 查看进程列表
cpp
ps
/*
输出:
PID TTY TIME CMD
2994 pts/1 00:00:00 bash
4669 pts/1 00:00:00 open ← 我们的程序
4670 pts/1 00:00:00 ps
*/
- 进入正确的proc目录
cpp
cd /proc/4724/
/*
/proc/4669/ = 4669号进程的信息目录
Linux的 /proc 文件系统是个虚拟文件系统,里面存着所有进程的信息!---
*/
- 查看进程目录内容
cpp
ls
输出一大堆文件/目录,重点看:
|--------|-------------|
| 项目 | 作用 |
| fd | 文件描述符目录 |
| exe | 指向可执行文件 |
| cwd | 当前工作目录 |
| maps | 内存映射 |
| status | 进程状态 |
6.查看该进程打开的文件描述符
cpp
ls fd
输出: 0 1 2 3
7. 详细查看文件描述符
cpp
cd /proc/4724/fd
ls -l
或者
cd /proc/4724/
ls fd -l
| fd | 指向 | 说明 |
|---|---|---|
0 |
/dev/pts/1 |
标准输入 → 终端 |
1 |
/dev/pts/1 |
标准输出 → 终端 |
2 |
/dev/pts/1 |
标准错误 → 终端 |
3 |
/home/book/01_open/open.c |
🔴 我们用 open() 打开的文件! |
三、使用open函数创建文件
创建文件:
cpp
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
/*
* ./create 1.txt
* argc = 2
* argv[0] = "./open"
* argv[1] = "1.txt"
*/
int main(int argc, char **argv)
{
int fd;
if (argc != 2)
{
printf("Usage: %s <file>\n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0777);
/*
O_RDWR 读写模式打开
O_CREAT 如果文件不存在,就创建它!
O_TRUNC 如果文件已存在,就清空它!
0777 = 所有者、组、其他用户都有读写执行权限
1 表示执行权限,2 表示可读权限 4 表示可写权限 可仔细累加
*/
if (fd < 0)
{
printf("can not open file %s\n", argv[1]);
printf("errno = %d\n", errno);
printf("err: %s\n", strerror(errno));
perror("open");
}
else
{
printf("fd = %d\n", fd);
}
while (1)
{
sleep(10);
}
close(fd);
return 0;
}
执行结果如下:


第一个权限 0700 表示如下:
cpp
S_IRWXU ← 拆成三部分看
│││││││
││││││└─ U = User (所有者)
│││││└── X = eXecute (执行)
││││└─── W = Write (写)
│││└──── R = Read (读)
││└───── RWX = 读+写+执行
└─────── S = (权限常量前缀)
要点:umask
1、什么是 umask?
umask = U ser MASK(用户权限掩码)
-
它是一个权限过滤器
-
用来"屏蔽"掉某些权限
-
创建新文件/目录时,它决定默认权限
2、umask 的工作原理
公式:
bash
实际权限 = 请求权限 - umask
举例说明:
| 场景 | 请求权限 | umask | 实际得到的权限 |
|---|---|---|---|
| 创建文件 | 0666 (rw-rw-rw-) |
0002 |
0664 (rw-rw-r--) |
| 创建目录 | 0777 (rwxrwxrwx) |
0002 |
0775 (rwxrwxr-x) |
3、常用 umask 值
| umask值 | 作用 | 文件默认权限 | 目录默认权限 |
|---|---|---|---|
0000 |
不屏蔽任何权限 | 0666 (rw-rw-rw-) |
0777 (rwxrwxrwx) |
0002 |
屏蔽其他用户写权限 | 0664 (rw-rw-r--) |
0775 (rwxrwxr-x) |
0022 |
屏蔽组和其他用户写权限 | 0644 (rw-r--r--) |
0755 (rwxr-xr-x) |
0027 |
严格屏蔽 | 0640 (rw-r-----) |
0750 (rwxr-x---) |
4、查看 umask```bash
umask # 输出:0002(八进制) umask -S # 输出:u=rwx,g=rwx,o=rx(符号形式,更直观)
bash
五、修改 umask 临时修改(只对当前终端有效):
umask 0022 # 改成0022
umask 0002 # 改回0002
永久修改(对所有终端有效):
bash
# 编辑 ~/.bashrc 或 ~/.profile
echo 'umask 0022' >> ~/.bashrc
source ~/.bashrc
5、umask 位详解
bash
umask: 0002
││││
│││└─ 其他用户权限掩码(2 = -w-)
││└── 组用户权限掩码(0 = ---)
│└─── 所有者权限掩码(0 = ---)
└──── 特殊位(通常不用管)
| umask位 | 八进制值 | 屏蔽的权限 |
|---|---|---|
| 0 | 0 | 不屏蔽 |
| 1 | 1 | 屏蔽执行权限(--x) |
| 2 | 2 | 屏蔽写权限(-w-) |
| 3 | 3 | 屏蔽写和执行(-wx) |
| 4 | 4 | 屏蔽读权限(r--) |
| 5 | 5 | 屏蔽读和执行(r-x) |
| 6 | 6 | 屏蔽读和写(rw-) |
| 7 | 7 | 屏蔽所有权限(rwx) |
6、回到之前的例子
bash
fd = open("1.txt", O_RDWR | O_CREAT | O_TRUNC, 0777);
// 请求权限
```**实际过程:**
请求权限:0777 (rwxrwxrwx) umask: 0002 (-------w-)
实际权限:0775 (rwxrwxr-x)
八、为什么需要 umask?
安全!
如果没有 umask:
创建的文件默认权限 `0666` = 任何人都能写!
创建的目录默认权限 `0777` = 任何人都能删文件!
有了 umask(默认0002):
文件:`0664` = 只有所有者和组能写
目录:`0775` = 只有所有者和组能删文件
---
九、快速记忆
| umask | 用途 |
|-------|------|
| `0002` | 普通用户(默认) |
| `0022` | 服务器/root用户(更安全) |
四、 使用write写文件

cpp
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
/*
* ./write 1.txt str1 str2
* argc = 4
* argv[0] = "./write"
* argv[1] = "1.txt"
*/
int main(int argc, char **argv)
{
int fd;
int i;
int len;
if (argc < 3)//参数小于3,argv[0] = 程序名,argv[1] = 文件名,argv[2] = 第1个要写的字符串
{
printf("Usage: %s <file> <string1> <string2> ...\n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0644);
if (fd < 0)
{
printf("can not open file %s\n", argv[1]);
printf("errno = %d\n", errno);
printf("err: %s\n", strerror(errno));
perror("open");
}
else
{
printf("fd = %d\n", fd);
}
for (i = 2; i < argc; i++)//i=2,从第三个参数开始写入
{
len = write(fd, argv[i], strlen(argv[i]));
if (len != strlen(argv[i]))
{
perror("write");
break;
}
write(fd, "\r\n", 2);
}
close(fd);
return 0;
}

五、 使用read函数读文件
cpp
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
/*
* 程序功能:读取文件内容并打印到标准输出
* 使用方法:./read 1.txt
* 参数说明:
* argc = 2(程序名 + 文件名)
* argv[0] = "./read"(程序名)
* argv[1] = "1.txt"(要读取的文件名)
*/
int main(int argc, char **argv)
{
int fd; // 文件描述符,用来标识打开的文件
int i; // 循环变量(虽然定义了但本程序中没用到)
int len; // 实际读取的字节数
unsigned char buf[100]; // 缓冲区,用来存放从文件读取的数据,大小100字节
/* 检查参数个数:必须提供一个文件名 */
if (argc != 2)
{
printf("Usage: %s <file>\n", argv[0]); // 打印使用说明
return -1; // 参数不对,异常退出
}
/* 打开文件:只读模式 */
fd = open(argv[1], O_RDONLY);
/*
* argv[1]: 要打开的文件名
* O_RDONLY: 只读模式打开(不创建、不清空)
* 返回值 fd:
* - 成功: 返回非负整数(文件描述符)
* - 失败: 返回 -1
*/
/* 检查文件是否打开成功 */
if (fd < 0)
{
/* 打开失败,打印三种方式的错误信息 */
printf("can not open file %s\n", argv[1]); // 自定义错误提示
printf("errno = %d\n", errno); // 打印错误号
printf("err: %s\n", strerror(errno)); // 把错误号转成文字描述
perror("open"); // 自动打印 "open: 错误描述"
}
else
{
/* 打开成功,打印文件描述符 */
printf("fd = %d\n", fd); // 通常是3(0,1,2被标准输入输出占用)
}
/* ==========================================
* 核心部分:循环读取文件并打印
* ==========================================
*/
while (1) // 无限循环,直到读到文件末尾
{
/* 从文件读取数据到缓冲区 */
len = read(fd, buf, sizeof(buf)-1);
/*
* fd: 文件描述符
* buf: 存放数据的缓冲区
* sizeof(buf)-1: 最多读取 99 字节(留1字节给字符串结束符 '\0')
* len: 实际读取到的字节数
* - len > 0: 成功读取了 len 字节
* - len = 0: 到达文件末尾(EOF)
* - len < 0: 读取出错
*/
/* 情况1:读取出错 */
if (len < 0)
{
perror("read"); // 打印错误信息
close(fd); // 关闭文件
return -1; // 异常退出
}
/* 情况2:到达文件末尾(EOF) */
else if (len == 0)
{
break; // 跳出循环,结束读取
}
/* 情况3:成功读取到数据 */
else
{
/*
* 此时缓冲区中有数据:
* buf[0], buf[1], ..., buf[len-1] 是读取到的数据
* 但是!read() 读取的是原始字节,不是C字符串!
* C字符串需要以 '\0' 结尾,所以我们要手动加上!
*/
buf[len] = '\0'; // 在读取的数据末尾加上字符串结束符
printf("%s", buf); // 以字符串形式打印出来
}
}
/* 读取完毕,关闭文件 */
close(fd);
return 0; // 正常退出
}

| 特性 | write.c | read.c |
|---|---|---|
| open标志 | `O_RDWR | O_CREAT |
| 核心循环 | 遍历参数写入 | 循环读取直到EOF |
| 缓冲区处理 | 不需要 | 必须加 \0 |
| 判断结束 | 参数遍历完 | len == 0 |