Linux常见命令
1. `ls` - 列出目录内容
- 基本用法:`ls [选项] [目录]`
- 常见选项:
- `-l`:以长格式显示文件信息
- `-a`:显示所有文件,包括隐藏文件
- `-h`:以人类可读的格式显示文件大小
2. `cd` - 更改当前工作目录
- 基本用法:`cd [目录路径]`
- 示例:
- `cd /home/user`:切换到`/home/user`目录
- `cd ..`:切换到上一级目录
- `cd ~`:切换到当前用户的主目录
3. `pwd` - 显示当前工作目录路径
- 基本用法:`pwd`
4. `mkdir` - 创建新目录
- 基本用法:`mkdir [选项] 目录名`
- 常见选项:
- `-p`:递归创建目录,包括父目录
5. `rm` - 删除文件或目录
- 基本用法:`rm [选项] 文件或目录`
- 常见选项:
- `-r`:递归删除目录
- `-f`:强制删除,不提示确认
6. `cp` - 复制文件或目录
- 基本用法:`cp [选项] 源文件 目标文件`
- 常见选项:
- `-r`:递归复制目录
- `-f`:覆盖已存在的目标文件
7. `mv` - 移动或重命名文件或目录
- 基本用法:`mv [选项] 源文件 目标文件`
- 常见选项:
- `-i`:交互式操作,提示确认
8. `cat` - 显示文件内容
- 基本用法:`cat [选项] 文件`
- 常见选项:
- `-n`:显示行号
9. `grep` - 在文件中搜索匹配的文本
- 基本用法:`grep [选项] 模式 文件`
- 常见选项:
- `-i`:忽略大小写
- `-r`:递归搜索目录下的文件
- `-v`:显示不匹配的行
10. `head` - 显示文件开头部分
- 基本用法:`head [选项] 文件`
- 常见选项:
- `-n`:指定显示的行数
11. `tail` - 显示文件结尾部分
- 基本用法:`tail [选项] 文件`
- 常见选项:
- `-n`:指定显示的行数
- `-f`:实时追踪文件内容
12. `wc` - 统计文件中的行数、字数和字符数
- 基本用法:`wc [选项] 文件`
- 常见选项:
- `-l`:显示行数
- `-w`:显示字数
- `-c`:显示字符数
13. `ps` - 显示当前运行的进程
- 基本用法:`ps [选项]`
- 常见选项:
- `aux`:显示所有进程的详细信息
14. `kill` - 终止正在运行的进程
- 基本用法:`kill [选项] 进程ID`
- 常见选项:
- `-9`:强制终止进程
15. `ifconfig` - 显示和配置网络接口信息
- 基本用法:`ifconfig [网络接口] [选项]`
- 常见选项:
- `-a`:显示所有接口信息
- `up`:启用网络接口
- `down`:禁用网络接口
16. `shutdown` - 关闭系统
- 基本用法:`shutdown [选项] 时间`
- 常见选项:
- `-h`:关机
- `-r`:重启
17. `reboot` - 重新启动系统
- 基本用法:`reboot`
18. `chmod` - 修改文件权限
- 基本用法:`chmod [选项] 权限 文件`
- 常见选项:
- `+`:添加权限
- `-`:移除权限
- `=`:设置权限
- 示例:
- `chmod +x script.sh`:赋予`script.sh`可执行权限
- `chmod 644 file.txt`:设置`file.txt`的权限为644
19. `chown` - 修改文件的所有者
- 基本用法:`chown [选项] 用户 文件`
- 常见选项:
- `-R`:递归修改目录下的文件和子目录的所有者
- 示例:
- `chown user1 file.txt`:将`file.txt`的所有者更改为`user1`
- `chown -R user1 directory/`:递归修改`directory/`下所有文件和子目录的所有者为`user1`
20. `chgrp` - 修改文件的所属组
- 基本用法:`chgrp [选项] 组 文件`
- 常见选项:
- `-R`:递归修改目录下的文件和子目录的所属组
- 示例:
- `chgrp group1 file.txt`:将`file.txt`的所属组更改为`group1`
- `chgrp -R group1 directory/`:递归修改`directory/`下所有文件和子目录的所属组为`group1`
21. `useradd` - 创建新用户
- 基本用法:`useradd [选项] 用户名`
- 常见选项:
- `-m`:同时创建用户的主目录
- `-G`:指定用户所属的附加组
- 示例:
- `useradd john`:创建一个名为`john`的用户
- `useradd -m -G group1 john`:创建一个名为`john`的用户,并将其加入`group1`组
22. `passwd` - 设置用户密码
- 基本用法:`passwd [选项] 用户`
- 常见选项:
- `-d`:禁用密码(使用户无法登录)
- `-l`:锁定密码(使用户无法登录)
- `-u`:解锁密码(允许用户登录)
- 示例:
- `passwd john`:为用户`john`设置密码
- `passwd -d john`:禁用用户`john`的密码
23. `tar` - 打包和解包文件
- 基本用法:
- 打包文件:`tar [选项] 目标文件.tar 源文件`
- 解包文件:`tar [选项] 目标目录 -xf 源文件.tar`
- 常见选项:
- `-c`:创建打包文件
- `-x`:解包文件
- `-f`:指定源文件名
- 示例:
- `tar -cf archive.tar file1 file2`:创建名为`archive.tar`的打包文件,包含`file1`和`file2`
- `tar -xf archive.tar -C /target/directory`:将`archive.tar`解包到`/target/directory`
24. `gzip` - 压缩文件
- 基本用法:`gzip [选项] 文件`
- 常见选项:
- `-d`:解压缩文件
- `-r`:递归压缩目录下的所有文件
- 示例:
- `gzip file.txt`:压缩`file.txt`,生成`file.txt.gz`
- `gzip -d file.txt.gz`:解压缩`file.txt.gz`
25. `gunzip` - 解压缩文件
- 基本用法:`gunzip [选项] 文件`
- 示例:
- `gunzip file.txt.gz`:解压缩`file.txt.gz`,生成`file.txt`
Shell脚本
1. Shell脚本是一种用于自动化任务和编写批处理脚本的脚本语言。它们通常在命令行解释器(如Bash)中运行。
2. Shell脚本以`.sh`为文件扩展名,并且需要具有可执行权限才能运行。您可以使用`chmod +x script.sh`命令为脚本添加可执行权限。
3. Shell脚本的第一行通常是`#!/bin/bash`,它指定了要用于解释脚本的Shell解释器。
4. Shell脚本可以包含变量、条件语句、循环、函数等。它们使用简单的语法和命令来完成各种任务。
5. 在Shell脚本中,变量可以通过赋值来创建和访问。例如,`name="John"`定义了一个名为`name`的变量,并将其设置为`John`。
6. 使用`$`符号可以访问变量的值。例如,`echo $name`将打印出变量`name`的值。
7. Shell脚本中的条件语句使用`if-else`结构来执行基于条件的操作。例如:
```shell
if [ $num -gt 10 ]; then
echo "The number is greater than 10."
else
echo "The number is less than or equal to 10."
fi
```
8. 循环结构在Shell脚本中用于重复执行一系列操作。常见的循环有`for`循环和`while`循环。
9. 函数可以在Shell脚本中定义和调用,以便重复使用一段代码。例如:
```shell
greet() {
echo "Hello, $1!"
}
greet "John"
```
10. 使用`$1`、`$2`等特殊变量可以访问传递给脚本或函数的参数。例如,在上面的示例中,`$1`表示第一个参数,即`John`。
Linux C系统
1. 理解Linux系统调用:
- Linux系统调用是与操作系统内核进行交互的接口。它们提供了访问底层系统功能的方法,如文件操作、进程管理、网络通信等。
- 头文件`<unistd.h>`中包含了许多常用的系统调用函数,如`open()`、`read()`和`write()`等。
2. 文件I/O操作:
- Linux C编程中文件I/O是一项基本任务。通过使用系统调用函数,可以打开文件、读取文件内容、写入数据以及关闭文件。
- `open()`函数用于打开文件,`read()`和`write()`函数用于读取和写入文件数据,`close()`函数用于关闭文件。
3. 进程管理:
- Linux是一个多进程操作系统,允许同时运行多个进程。C语言提供了创建、管理和通信进程的功能。
- `fork()`函数用于创建一个新进程,`exec()`函数用于在新进程中执行一个程序,`wait()`函数用于等待子进程结束并获取其退出状态。
4. 线程和并发编程:
- Linux C语言支持线程编程,允许在同一进程中创建多个执行流。线程可以共享进程的资源,提供并发性。
- `pthread_create()`函数用于创建一个新线程,`pthread_join()`函数用于等待线程结束。
5. 内存管理:
- 在Linux C编程中,开发者需要手动管理内存分配和释放。
- `malloc()`和`free()`函数用于动态分配和释放内存,`calloc()`函数用于分配并初始化内存。
6. 网络编程:
- Linux C语言提供了丰富的网络编程功能,允许开发者创建网络应用程序。
- `socket()`函数用于创建套接字,`bind()`函数用于将套接字与特定端口关联,`listen()`函数用于侦听连接请求,`accept()`函数用于接受连接。
7. 错误处理:
- 在Linux C编程中,正确处理错误和异常非常重要。错误处理可确保程序的稳定性和可靠性。
- 多数系统调用函数返回-1表示错误,此时可以使用`errno`变量获取错误代码。`perror()`函数用于打印错误信息。
8. Makefile和编译:
- Makefile是一种用于自动化编译和构建程序的脚本。它定义了源文件、依赖关系和编译规则。
- 使用`gcc`编译器进行编译,可以通过命令行选项指定所需的库和编译标志。
9. 调试和测试:
- 调试是Linux C编程中的重要环节。`gdb`是一个常用的调试器,用于追踪程序执行和检测错误。
- 单元测试和集成测试是保证程序质量的关键步骤,可以使用各种测试框架进行测试。
C语言
1. C语言是一种通用的、高效的编程语言,具有简洁而灵活的语法。
程序由函数组成,每个函数由一系列语句块构成,语句块由花括号括起来。
C语言中的基本数据类型包括整数(int)、字符(char)、浮点数(float、double)和布尔值(bool)等。
变量用于存储数据,在使用变量之前需要声明其数据类型。
C语言支持各种算术、逻辑和关系运算符,用于进行数值计算和条件判断。
表达式是由运算符、操作数和函数调用组成的组合,用于进行计算和生成值。
控制流语句用于根据条件或循环来控制程序的执行流程,包括条件语句(if-else)、循环语句(for、while、do-while)和跳转语句(break、continue、return)等。
数组是一组相同类型的元素的集合,可以通过索引访问和操作数组中的元素。
指针是一个变量,存储一个内存地址,可以用于直接访问和操作内存中的数据。
函数用于将代码模块化,实现代码的可重用性和组织性,函数由函数头和函数体组成。
结构体是一种自定义的数据类型,用于将多个不同类型的数据组合在一起,每个数据称为结构体的成员。
文件操作允许读取和写入文件,可以使用文件指针和文件操作函数来打开、关闭、读取和写入文件。
动态内存分配允许在程序运行时动态地分配和释放内存,可以使用`malloc()`、`calloc()`和`realloc()`等函数进行动态内存分配。
预处理器是C语言编译过程的一部分,用于在实际编译之前对源代码进行处理,宏是预处理器提供的一种机制,允许在程序中定义和使用简单的代码片段。
常见数据结构的总结
1. 数组(Array):
```c
int numbers[5] = {1, 2, 3, 4, 5};
```
数组`numbers`包含了5个整数元素,通过索引可以访问特定位置的元素。例如,`numbers[0]`表示数组的第一个元素,其值为1。
2. 结构体(Structure):
```c
struct Person {
char name[20];
int age;
};
struct Person person1;
strcpy(person1.name, "John");
person1.age = 25;
```
定义了一个名为`Person`的结构体,包含了一个字符串类型的`name`成员和一个整数类型的`age`成员。通过结构体,可以创建一个名为`person1`的实例,并为其成员赋值。
3. 链表(Linked List):
```c
struct Node {
int data;
struct Node* next;
};
struct Node* head = NULL;
void insertAtBeginning(int value) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = head;
head = newNode;
}
void displayList() {
struct Node* current = head;
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
}
```
上述代码展示了使用链表实现头插法插入元素和显示链表的操作。首先,定义了一个名为`Node`的结构体,包含一个整数类型的`data`成员和一个指向下一个节点的指针`next`。然后,通过`insertAtBeginning`函数在链表头部插入新节点。最后,通过`displayList`函数遍历链表并打印节点的值。
4. 栈(Stack):
```c
#define MAX_SIZE 100
int stack[MAX_SIZE];
int top = -1;
void push(int value) {
if (top >= MAX_SIZE - 1) {
printf("Stack Overflow\n");
return;
}
stack[++top] = value;
}
int pop() {
if (top == -1) {
printf("Stack Underflow\n");
return -1;
}
return stack[top--];
}
```
以上代码演示了使用数组实现栈的`push`(入栈)和`pop`(出栈)操作。栈的最大容量由常量`MAX_SIZE`定义,栈顶索引为`top`。通过`push`函数将元素压入栈中,通过`pop`函数从栈中弹出元素。
5. 队列(Queue):
```c
#define MAX_SIZE 100
int queue[MAX_SIZE];
int front = 0;
int rear = -1;
void enqueue(int value) {
if (rear >= MAX_SIZE - 1) {
printf("Queue Overflow\n");
return;
}
queue[++rear] = value;
}
int dequeue() {
if (front > rear) {
printf("Queue Underflow\n");
return -1;
}
return queue[front++];
}
```
上述代码展示了使用数组实现队列的`enqueue`(入队)和`dequeue`(出队)操作。队列的最大容量由常量`MAX_SIZE`定义,队列的前端和后端分别由变量`front`和`rear`表示。通过`enqueue`函数将元素插入队列尾部,通过`dequeue`函数从队列头部移除元素。
树(Tree):
```c
struct TreeNode {
int data;
struct TreeNode* left;
struct TreeNode* right;
};
struct TreeNode* createNode(int value) {
struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));
newNode->data = value;
newNode->left = NULL;
newNode->right = NULL;
return newNode;
}
void insert(struct TreeNode** root, int value) {
if (*root == NULL) {
*root = createNode(value);
return;
}
if (value < (*root)->data) {
insert(&((*root)->left), value);
} else {
insert(&((*root)->right), value);
}
}
void inorderTraversal(struct TreeNode* root) {
if (root != NULL) {
inorderTraversal(root->left);
printf("%d ", root->data);
inorderTraversal(root->right);
}
}
```
上述代码展示了使用结构体和指针实现二叉搜索树(Binary Search Tree)的插入和中序遍历操作。首先,定义了一个名为`TreeNode`的结构体,包含一个整数类型的`data`成员和指向左子树和右子树的指针。然后,通过`createNode`函数创建新节点,并通过`insert`函数将节点插入二叉搜索树。最后,通过`inorderTraversal`函数进行中序遍历,按升序打印树中的节点值。
图(Graph):
```c
#define MAX_VERTICES 100
struct Graph {
int numVertices;
int adjacencyMatrix[MAX_VERTICES][MAX_VERTICES];
};
void initGraph(struct Graph* graph, int numVertices) {
graph->numVertices = numVertices;
int i, j;
for (i = 0; i < numVertices; i++) {
for (j = 0; j < numVertices; j++) {
graph->adjacencyMatrix[i][j] = 0;
}
}
}
void addEdge(struct Graph* graph, int src, int dest) {
if (src >= 0 && src < graph->numVertices && dest >= 0 && dest < graph->numVertices) {
graph->adjacencyMatrix[src][dest] = 1;
graph->adjacencyMatrix[dest][src] = 1;
}
}
void printGraph(struct Graph* graph) {
int i, j;
for (i = 0; i < graph->numVertices; i++) {
for (j = 0; j < graph->numVertices; j++) {
printf("%d ", graph->adjacencyMatrix[i][j]);
}
printf("\n");
}
}
```
以上代码展示了使用邻接矩阵(Adjacency Matrix)实现图的创建、添加边和打印图的操作。首先,定义了一个名为`Graph`的结构体,包含一个整数类型的`numVertices`成员和二维数组`adjacencyMatrix`用于存储图的邻接关系。然后,通过`initGraph`函数初始化图的顶点数量和邻接矩阵。通过`addEdge`函数添加边,将对应位置的邻接矩阵元素设为1表示顶点之间存在边。最后,通过`printGraph`函数打印邻接矩阵,展示图的结构。
学习Linux I/O
1. 标准输入/输出/错误(stdin/stdout/stderr):
- 标准输入(stdin)是程序默认从中读取数据的流。通常情况下,它连接到键盘。
- 标准输出(stdout)是程序默认将数据写入的流。通常情况下,它连接到终端或控制台。
- 标准错误输出(stderr)用于将错误和诊断信息发送到终端或控制台。
2. 文件描述符的使用:
- 文件描述符是非负整数,用于标识打开的文件或其他I/O资源。
- 使用系统调用`open`打开文件时,会返回一个新的文件描述符。
- 标准输入、输出和错误输出的文件描述符分别为0、1和2。
3. 文件打开模式:
- 打开文件时,可以指定打开模式。常见的打开模式包括读取(O_RDONLY)、写入(O_WRONLY)、追加(O_APPEND)等。
- 可以通过逻辑或(|)操作符将多个模式组合在一起,例如读写模式(O_RDWR)。
4. 文件操作函数:
- `open`:打开文件并返回文件描述符。
- `close`:关闭文件。
- `read`:从文件中读取数据。
- `write`:将数据写入文件。
- `lseek`:在文件中进行定位。
- `fcntl`:用于对文件描述符进行各种操作,例如修改文件状态标志。
5. 错误处理:
- 文件操作函数在发生错误时会返回-1或其他特殊值。
- 可以使用全局变量`errno`获取具体的错误代码。
- 可以使用`perror`函数将错误信息打印到标准错误输出(stderr)。
6. 文件和目录的创建、删除和重命名:
- `mkdir`:创建目录。
- `rmdir`:删除目录。
- `unlink`:删除文件。
- `rename`:重命名文件或目录。
7. 文件权限和属性:
- 使用`chmod`可以修改文件的权限。
- 使用`chown`可以修改文件的所有者和所属组。
- 使用`stat`可以获取文件的详细属性信息,如大小、权限、时间戳等。
8. 缓冲I/O和直接I/O:
- 缓冲I/O使用缓冲区进行读写操作,常见的缓冲函数包括`fread`和`fwrite`。
- 直接I/O绕过操作系统的缓冲区,直接读写磁盘,常见的直接I/O函数包括`read`和`write`。
Linux中进程和线程
1. 进程和线程的基本概念:
- 进程是操作系统对正在运行的程序的抽象。它包括程序的代码、数据和资源的实例。
- 线程是进程内的执行单元。一个进程可以包含多个线程,它们共享进程的地址空间和资源,但具有独立的栈空间和线程上下文。
2. 进程的创建和终止:
- 使用系统调用`fork`可以创建一个新的子进程。子进程是父进程的副本,从调用`fork`的位置开始执行。
- 子进程有自己独立的进程标识符(PID),但继承了父进程的打开文件描述符、信号处理程序和部分内存内容。
- 父进程可以使用`wait`或`waitpid`等系统调用等待子进程的终止,并获取子进程的退出状态。
3. 进程间通信(IPC):
- 进程可以使用多种机制进行通信,如管道、共享内存、消息队列和信号量。
- 管道是一个单向的字节流,可用于父子进程或具有共同祖先的进程之间的通信。
- 共享内存允许多个进程访问相同的内存区域,从而实现高效的数据共享。
- 消息队列允许进程通过发送和接收消息来进行通信。
- 信号量用于同步进程和提供互斥访问共享资源。
4. 线程的创建和终止:
- 使用系统调用`pthread_create`可以在进程中创建一个新的线程。
- 线程共享进程的虚拟地址空间、文件描述符和其他资源。
- 线程可以通过调用`pthread_join`等待其他线程的终止,并获取其返回值。
5. 线程同步与互斥:
- 多个线程同时访问共享资源可能导致数据竞争和不一致的问题。
- 互斥量(Mutex)用于确保在给定时间只有一个线程可以访问共享资源。线程可以使用`pthread_mutex_lock`和`pthread_mutex_unlock`来获取和释放互斥量。
- 条件变量(Condition Variable)用于线程之间的等待和通知机制。线程可以使用`pthread_cond_wait`等待条件变量,而其他线程可以使用`pthread_cond_signal`或`pthread_cond_broadcast`通知等待的线程。
6. 线程的调度和优先级:
- 线程调度器决定在给定时间片内运行哪个线程。
- Linux提供了不同的调度策略,如实时调度策略(SCHED_FIFO和SCHED_RR)和普通时间共享调度策略(SCHED_OTHER)。
- 可以使用`pthread_setschedparam`设置线程的调度参数和优先级。
7. 线程的并发编程:
- 并发编程涉及多个线程同时执行,因此需要考虑同步和互斥问题。
- 正确使用互斥量、条件变量和原子操作可以避免竞态条件和数据不一致性。
- 并发编程还需要处理死锁、活锁和饥饿等问题,使用适当的算法和技术来避免这些情况的发生。
8. 进程和线程的调试和监控:
- Linux提供了多种工具和命令来调试和监控进程和线程的行为。
- `gdb`是一个功能强大的调试器,可用于追踪进程和线程的执行、设置断点和检查变量的值。
- `strace`是一个系统调用跟踪工具,可以用于监视进程的系统调用和信号。
- `top`命令可以实时显示系统中运行的进程和线程的状态和资源使用情况。