对于Linux操作系统的学习,是每一位所必须了解和掌握的重要知识!
在Linux系统中,磁盘上的文件和目录被组成一颗目录树,每个节点都是目录或者文件,在根目录之下又被分为多个子目录,我们用画树状图的形式来进行表示
在linux操作系统之中,我们应该明白一个“一切皆文件”法则,这样能够更好的帮助我们进行后期的学习,也就是对于任何的文件,命令,网卡,磁盘等都可以满足文件的打开,读,写,关闭的操作。
对于Linux的命令来说,他是有着很多的参数的,我们也是不可能全部记住,因此我们可以通过联机手册来获取帮助进行查看,而访问联机手册的命令则是man语法:man [选项] 命令
对于man手册来说,他一共被分为8章,而我们所需要学习的只包括了其中的6章,分别为:
1. 第一章:用户命令
2. 第二章:系统调用
3. 第三章:C库函数的解释
4. 第四章:不学习
5. 第五章:配置文件
6. 第六章:不学习
7. 第七章:某一个主题
8. 第八章:系统管理命令
功能:pwd (print working dir)的缩写
功能:显示用户当前所在的目录
示例:
语法:ls [选项] [目录或文件]
功能:对于目录,该命令列出该目录下的所有子目录与文件;对于文件,将列出文件名以及其他信息。
常用选项:
语法:cd [路径]
功能:更改路径,改变工作目录,将当前的工作目录改变到指定的目录之下
常用选项:
小技巧: clear 在Linux之中是清屏操作。
语法:touch [选项] 文件
功能:touch命令参数可以更改文档或者目录的日期时间,包括存取时间和更改时间,或者新建一个不存在的文件,更多的被使用于创建一个新文件。
常用选项:
vim [文件名]
旨在创建一个新的项目,之后会自动进入我们所创建到的这个项目之中。i
就可以进行写入操作了。Esc
进行退出编辑模式,也就是我们所说到的书写模式。:
,之后按wq
进行保存,或者分步进行,先按w
,之后再重复上述步骤后按q
。l
光标向右移动h
光标向左移动j
光标向下移动k
光标向上移动i
插入到光标当前位置处I
插入到光标当前行的行首位置处O
插入到光标当前行的上面插入一行o
插入到光标当前行的下面插入一行a
插入到光标的下一个位置处A
插入到光标当前行的末尾进行追加yw
复制光标位置的完整单词nyy
复制光标位置开始的n行y$
复制光标当前位置到行尾y^
复制光标当前位置到行首p
粘贴u
撤销上一步操作ctrl + r
重做此操作ndd
删除当前光标位置开始的总共n行x
删除光标所在的当前字符r
替换光标当前所在位置的字符D
删除光标位置到当前行的行尾R
从光标位置开始进行替换,按Esc
结束dG
删除从当前行到文件的结尾位置:%s/old/new/
替换操作,一行如果有多个的话,则只替换第一个找到的:%s/old/new/g
替换操作,不论一行有多少个,全部进行替换:3,5s/old/new/g
替换操作,替换从第三行到第五行的所有存在的:$
在查看一个文件时候,按此操作,则直接移动到文件的最后位置。Ctrl + v
块可视化操作v
字符可视化操作shift + v
行可视化操作:xxx.h
把xxx.h的内容放到目前光标所存在的位置处,一般用于包含头文件。ctrl + v
进入块可视化I
按大写的i后输入//
Esc
操作进行退出Linux下有两种用户:超级用户(root),普通用户;超级用户的命令提示符时#
,普通用户的命令提示符时$
。
useradd 用户名
表示创建一个用户id 用户名
查看当前用户名的具体信息/home/用户名
表示家目录 之后ls -a
将能够查看配置信息passwd
设置密码,如果自己练习的话尽可能地把密码设置简单一些/etc/passwd
中增加一条记录,来记录此用户的信息;而会在/etc/shadow之中保存我们设置好的密码userdel 用户名
删除此用户 -r
同时删除用户的家目录usermod
修改用户信息 -d
修改用户的家目录su - 用户
从当前用户切换到需要切换的用户,切记切换之后要退出,否则会引起不断的递归操作groupadd
创建组操作usermod -g group1 user1
将user1 加入到group1之中usermod -g group1 user2
将user2 也加入到group1之中sudo
此操作表示以自己的身份去运行其他用户身份才能够运行的命令
其中前面的w表示他的用户名,x表示密码占位符,/home/w2
表示家目录,/bin/bash
表示当前的状态是可以登录的,若是上面/nologin
的话,则表示此用户无法进行操作。
而shadow
之中所存放的则是w用户乱码密码!
对于修改文件的权限有着两种方式,一种是用命令+[对应指令] ,而另一种则是使用十进制的方法来进行修改。
当然了,文件有着对应的权限,目录也有着属于自己的对应的权限
目录的权限:
对于用户设置位的话,为了方便记忆和理解,我将其理解成为了网吧老板,网管关系,一般来说实际用户也就是网吧老板,有效用户是网管(一般情况就是老板),而在设置用户设置位后,也就相当于开通了管理的权限,网管不一定是老板了,但一定是管理这家网吧的那个拥有者。
查看网络状态 ifconfig
命令,一般情况下网卡信息都是eth0
,如果不是eth0
,则需要进行修改(其中网卡改名牵扯到两个相关的信息,就是biosdevname 和net.fnames
)
vim /etc/default/grub
进入之后将这句话最后的位置改为我们需要改正的后保存退出。cd/etc/sysconfig/network-scripts/
之后ls
我们会看到一个名为ifcfg-ens33
的文件夹,我们将其改名mv ifcfg-ens33 ifcfg-eth0
vim ifcfg-eth0
,将其中的内容改为和图片中一样的即可保存退出。ln 原本的文件名 新文件名
旨在让多个文件名指向同一个索引,也就是i节点。ln -s 第一个文件名,第二个文件名
找到hehe
目录号,寻找到test.txt,之后根据test.txt去找到他的目录号,寻找到数据块中的内容。gdb -g
命令
调试工具的作用:
quit
退出gdb调试。gdb
——————> file + 要调试的程序
;
gdb 程序名
gdb 程序名 core文件
gdb 程序名 pid
使用这条命令之后被调试的程序将会被暂停了continue
才能够继续运行
run 运行到第一个断点
start 运行在程序的开始处停下来
info breakpoints
查看断点信息
delete
删除我们所设置的断点。
step
有函数则进入函数的内部
until
行号 跳到这个行
jump+ 行号
同样为跳到这个行
return
遇见问题时,强制离开此函数
print 变量
打印变量的信息
set var p=malloc()
调试过程中 重新制定变量的指向
_start
从main函数开始进行运行,若是没有main函数,也是可以直接从缺省的地方开始进行运行
[root@luckily ~]# yum install ibus - libpinyin
#include "add.h"
int add(int a, int b) {
return a + b;
}
//add头文件
#ifndef __ADD_H__
#define __ADD_H__
int add(int a, int b);
#endif //__ADD_H__
ine __SUB_H__
int sub(int a, int b);
#endif //__SUB_H__
//sub函数
#include "sub.h"
int sub(int a, int b) {
return a - b;
}
#ifndef __MUL_H__
#define __MUL_H__
int mul(int a, int b);
#endif //__MUL_H__
//mul函数
#include "mul.h"
int mul(int a, int b) {
return a * b;
}
#ifndef __PARSE_H__
#define __PARSE_H__
void parse(char* buf);
#endif //__PARSE_H__
//parse函数
#include
#include
#include "parse.h"
#include "add.h"
#include "sub.h"
#include "mul.h"
#define IN 1
#define OUT 0
typedef int FUNC(int, int);
FUNC* p_fun = NULL;
char op;
struct expr_t {
FUNC* pFun;//add(int ,int )
char* name;//add sub
char op; //+ - fuhao
};
struct expr_t expr[] = {
{add,"add",'+'},
{sub,"sub",'-'},
{mul,"mul",'*'},
{NULL,NULL,0}
};
void do_action(int argc, char* argv[]) {
int left = atoi(argv[1]);
int right = atoi(argv[2]);
int ret = p_fun(left, right);
printf("%s%c%s=%d\n", argv[1], op, argv[2], ret);
}
void main_math(int argc, char* argv[]) {
int i;
p_fun = NULL;
for (i = 0; ; i++) {
if (expr[i].name == NULL) {
printf("%s ciminglingbucunzai1n", argv[0]);
return;
}
if (strcmp(expr[i].name, argv[0]) == 0) {
p_fun = expr[i].pFun;
op = expr[i].op;
break;
}
}
do_action(argc, argv);
}
void parse(char* buf) {
int i;
int argc = 0;
char* argv[8] = {};
int flag = OUT;
for (i = 0; buf[i] != 0; i++) {
//haizaidanciwaim ,bingqie dangqiande weizhibushi kongbaifu
if (flag == OUT && !isspace(buf[i])) {
argv[argc++] = buf + i;
flag = IN;
}
//ruguo zai danci nei ,bingqie dangqian zifu shi kongbaifu ,jiangdangqian zifuqingling ,zhicheng OUT zhuangtai
if (flag == IN && isspace(buf[i])) {
buf[i] = '\0';
flag = OUT;
}
}
main_math(argc, argv);
}
#include
#include
#include "parse.h"
int main(void) {
char buf[512] = {};
while (1) {
printf("ji suan qi\n ");
printf("please in you data:");
memset(buf, 0x00, sizeof(buf));
scanf("%[^\n]%*c", buf);
//printf("buf=[%s]\n",buf);
parse(buf);
}
}
#我 的 第 一 个 makefile
# $@ 目标
# $^ 所有依赖文件
# $ < 第一个依赖文件
# .PHONY 后面写的目标,不论是否是最新的,都强制执行
.PHONY : clean main all
CC = gcc
SRCS = $(wildcard * .c) #wildcard 过滤(通配)函数 当前目录下的所有.c文件
# /root/study/day02/progress
BASEDIR = $(shell pwd)
# /root/study/day02/progress/obj
OBJDIR = $(BASEDIR) / obj
$(shell mkdir - p $(OBJDIR))
OBJS = $(SRCS:.c = .o) #将SRCS对应的.c换乘.o
#/root/study/day02/progress/obj/main.o
OBJS : = $(addprefix $(OBJDIR) / , $(OBJS))
BIN : = main
all : $(BIN)
main : $(OBJS)
$(CC) - o $@ $ ^
# %通配符
$(OBJDIR) / % .o : % .c
$(CC) - c $ < -o $@
clean :
rm - rf $(OBJS)
rebuild : clean main
int main() {
int num;
do {
printf("请输入:");
if (scanf("%d", &num) != 1) {
printf("输入非法\n");
scanf("%d*[^\n]%*c");
continue;
}
break;
} while (1);
printf("你充值了多少%d\n",num);
}
int main(void) {
int num;
do {
printf("请输入:");
if (scanf("%d", &num) != 1) {
printf("输入非法\n");
scanf("%d[^\n]%*c");
continue;
}
break;
} while (1);
printf("你充值%d\n", num);
}
#include
#include
int main(void) {
char buf[100] = { '#' };
char* p = "|/-\\";
int i;
for (i = 0; i < 100; i++) {
buf[i] = '#';
printf("[% -100s][%d%%][%c]\r", buf, i + 1, p[i % 4]);
fflush(stdout);
usleep(500000);
}
}
进程中一个重要的练习:
int main(){
const int a =10;
int* p=(int*)&a;
*p=11;
printf("p =%d,a=%d\n",*p,a);
}
C语言编译后打出来的是“11 ,11”,
C++编译后打出来的是“11,10”;
const是一个不变的变量,它自身所修饰的变量是没有办法改变的,但是变量的空间是可以通过其他方式来进行改变的。
volatile const int a = 10;
volatile 阻止编译器进行优化 防止其他地方将其进行修改,所以打印出来后是“11,11”.每一次都要从内存之中取数据。
先运行A进程,当需要运行B进程时,将A进程得环境信息存放到内存之中,开始运行B进程,当需要再次运行A进程得时候,将B进程的环境信息存放倒内存之中,再从内存之中将A进程的环境信息恢复出来。
在Linux中描述进程的结构体叫做task_struct,是Linux内核的一种数据结构,它会被装载倒RAM(内存)里并且包含着进程的信息。
task_struct 中管理的信息:
我们将进程看作一个链表,因此操作系统管理进程的话,只要抓住链表的头就可以了:
task_struct中的mm成员 ,所对应的struct mm_struct管理内存的
mmap指向虚拟的内存空间,它是个链表,每个节点代表的是链表中的一个段, 不如(有的节点代表数据段,有的节点代表代码段,有的节点代表映射段)每一段又拥有起始地址和结束地址
操作系统的内核的话:是想访问那个空间就访问那个空间;用户的代码是不能够这样做的。
局部性 在有限的物理内存中跑更多的进程(把当前运行的代码放到内存之中,当前尚未运行的代码放到交换分区当中,下一次需要运行的时候再从交换分区拉出来)
int g_data = 100;
int main(void) {
pid_t pid = fork();
switch (pid) {
case -1:
printf("fork error\n");
break;
case 0:
g_data = 200;
printf("child : %p,&g_data = %p,g_data=%d\n", &main, &g_data, g_data);
break;
default:
sleep(1);
printf("parent : %p, &g_data = %p,g_data = %d\n", &main, &g_data, g_data);
break;
}
sleep(1);
}
以此代码为例,所打印出来的main函数地址和data地址是相同的,但是两个data的数据确实不同的。
env
export +自己定义的环境变量
PATH
进程和程序的区别是什么?
编号的范围:0- 131072(此值是通过下面这样操作来查找出来的,每个人的是不同的)cat/proc/sys/kernel/pid_max
0号进程,负责创建1号和2号进程,负责交换内存和swap
附加命令行参数 argc 和argv知识:main函数的命令函所传给它的
argc
命令行参数的个数
argv
命令行参数
int main(int argc ,char* argv[]){
}
for(i=0;i<envp[i]!=NULL;i++){
printf("%d : %s\n",i,envp[i]);
}
获取环境变量,给一个环境名,打印出环境变量getnev()
char* shell = getenv("SHELL"); //获取环境变量
printf("%s\n", shell == NULL ? "NULL" : shell);
设置环境变量:
if (putenv("AAA=abc") == 0) {
printf("putenv() ok\n");
}
else {
printf("putenv() error\n");
}
创建进程的一般过程:
#include
#include
#include
void Fun1(void) {
printf("Fun1()\n");
}
void Fun2(void) {
printf("Fun2()\n");
}
int main(void) {
//后注册的先调用
atexit(Fun1);
atexit(Fun2);
//1. 输出两次
//printf("alibaba");
//fork();
//2.输出一次
printf("alibaba\n");
// fork();
while (getchar() != '\n')
;
exit(0);//默认
}
带有换行符的在结束时已自动刷新缓存,而没有换行符的则等待函数结束后刷新
exit 结束符
exit(0) 0-255 只有16位是保存退出码的
exit和atexit的实现:
#include
#include
void Fun1(void) {
printf("Fun1()\n");
}
void Fun2(void) {
printf("Fun2()\n");
}
typedef struct Node {
void(*f)(void);
struct Node* next;
}Node_t;
Node_t* head = NULL;
int my_atexit(void(*f)(void)) {
Node_t* p = malloc(sizeof(Node_t));
p->f = f;
p->next = NULL;
if (head == NULL) {
head = p;
}
else {
p->next = head;
head = p;
}
}
void my_exit(int s) {
fflush(stdout);//刷新缓存
//换行退出处理程序
while (head != NULL) {
head->f();
head = head->next;
}
_exit(s);
}
int main(void) {
//后注册的先调用
my_atexit(Fun1);
my_atexit(Fun2);
//1. 输出两次
//printf("alibaba");
//fork();
//2.输出一次
printf("alibaba");
// fork();
while (getchar() != '\n')
;
my_exit(0);
}
收取僵尸
#include
#include
//#include
int main(void) {
pid_t pid;
/*
if((pid=fork())==0){
int i;
for(i=0;i<10;i++){
printf(".");
fflush(stdout);
sleep(1);
}
}
else{
*/
pid_t r = wait(NULL);
if (r == -1) {
printf("r==-1\n");
}
else {
printf("wait return\n");
for (;;) {
printf("#");
fflush(stdout);
sleep(1);
}
}
// }
}
readelf -h
查看头信息
从磁盘之中先拿出来后,之后将其放到虚拟地址空间之中,依据所对应的页表,放入到物理内存之中;如果切换时,将其放入交换区,将新的进程开始运行,再次使用时,从交换区再取出来。
服务器都是专用的,不会存在中途切出去后被别的进程打断,数据被清楚掉的情况存在。