Linux系统编程

Linux系统基础学习笔记

一、shell

Unix shell Bon

Linux bash

查看shell cat /etc/shell

bash字体放大ctrl+shift++ 恢复正常ctrl+0

功能快捷键助记
ctrl+p
ctrl+n
ctrl+b
ctrl+f
delctrl+ddelete光标之后的
Homectrl+athe first letter
Endctrl+eend
Backspacebackspce删除光标之前的

二、目录结构

bin:存放二进制可执行文件

boot: 存放开机启动程序

dev: 存放设备文件

home: 存放用户

etc: 用户信息和系统配置文件

lib: 库文件

root: 管理员宿主目录(家目录)

usr: 用户资源管理目录

Linux文件类型:

普通文件:-

目录文件: d

字符设备文件: c

块设备文件:b

软连接:l

管道文件: p

套接字:s

未知文件

三、vim

vim三种模式转换关系如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xoKaWC2a-1608271664677)(/media/libaisong/学习资料/A工作相关/Linux系统编程图片/vim三种模式转换关系.jpg)]

文本模式下的操作

​ i: 光标之前拆插入

​ a: 光标之后插入

​ o: 光标所在行的下一行行首

​ I: 光标所在行的行首

​ A: 光标所在行的行尾

​ O: 光标所在行的上一行行首

​ s: 删除一个字符的模式

​ S: 删除一正行为代价切换工作模式

命令模式下的操作:

​ h : 左移

​ j : 下移

​ k : 上移

​ l : 右移

跳转到指定行: 88G(命令模式) :88(末行模式)

跳转文件首:g(命令模式) 跳转文件尾 : G(命令模式)

自动格式化程序:gg=G(命令模式)

匹配大括号(小括号)对应:%(命令模式)

光标移动至行首:0;光标移动至行尾:$ (命令模式)

删除单个字符:x(命令模式)

替换单个字符:光标选中代替换的字母,按下,r再输入替换的字母

删除指定区域:按V 切换为“可视模式”,使用hjkl移动光标选择删除区域,再按D删除

整行删除:dd

删除制定N行:光标在删除首行,输入Ndd

删除一个单词:dw

删除光标前的所有字符,并移动光标至行首:d0

删除整行,并移动光标至行尾:D 或 d$

复制粘贴:

yy:复制一行

p : 粘贴到光标所在下一行

P(大写):粘贴到光标所在上一行

粘贴块:按V选择粘贴区域,yy复制。p/P粘贴

查找替换:

  • 1、查找设想内容:命令模式 /+查找内容。 n:查找下一个

  • 2、查找看到的内容:在命令模式下,将光标置于单词的任意一个字符上,按 # / * 查找。

  • 3、单行替换:光标在替换行上,进入末行模式,输入 😒 /原数据/新数据。

  • 4、通篇替换:光标在替换行上,进入末行模式,输入 :%s /原数据/新数据,(只会替换每一行的第一个);输入 :%s /原数据/新数据/g

  • 5、区域(指定行)替换:%其实行号:终止行号s /原数据/新数据.

  • 6、末行模式:ctr+p : 呼出之前的操作命令; ctr+n

  • 7、撤销:u(命令模式)、反撤销:ctrl+r

分屏::sp (末行模式分横屏) ctr+ww 在不同屏幕中切换

:vsp (竖屏分)

跳转至man手册:

  • 蒋光标置于代查看的函数单词上,使用 K(命令模式) 跳转,可以指定具体第几卷,3K
  • 查找宏定义:[d (命令模式)

在末行模式执行shell命令:

​ :!命令 :!ls -l

配置头文件
  • 切换至root账户:sudo su

  • 切换到vim文件夹,打开vimrc配置文件

    • cd /etc/vim
    • 打开vimrc配置文件 vim vimrc
  • "添加头文件
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    """""新文件标题""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    "新建.c,.h,.sh,.java文件,自动插入文件头 
    autocmd BufNewFile *.[ch],*.hpp,*.cpp exec ":call SetTitle()" 
      
      "加入注释
      func SetComment()
          call setline(1,"/*===============================================================") 
          call append(line("."),   "*   Copyright (C) ".strftime("%Y")." All rights reserved.")
          call append(line(".")+1, "*   ") 
          call append(line(".")+2, "*   文件名称:".expand("%:t")) 
          call append(line(".")+3, "*   创 建 者:李柏松")
         call append(line(".")+4, "*   创建日期:".strftime("%Y年%m月%d日")) 
         call append(line(".")+5, "*   描    述:") 
         call append(line(".")+6, "*")
         call append(line(".")+7, "*   更新日志:") 
         call append(line(".")+8, "*") 
         call append(line(".")+9, "================================================================*/") 
     endfunc
     
     "定义函数SetTitle,自动插入文件头 
     func SetTitle()
         call SetComment()
         if expand("%:e") == 'hpp' 
      call append(line(".")+10, "#ifndef _".toupper(expand("%:t:r"))."_H") 
      call append(line(".")+11, "#define _".toupper(expand("%:t:r"))."_H") 
      call append(line(".")+12, "#ifdef __cplusplus") 
      call append(line(".")+13, "extern \"C\"") 
      call append(line(".")+14, "{") 
      call append(line(".")+15, "#endif") 
      call append(line(".")+16, "") 
      call append(line(".")+17, "#ifdef __cplusplus") 
      call append(line(".")+18, "}") 
      call append(line(".")+19, "#endif") 
    call append(line(".")+20, "#endif //".toupper(expand("%:t:r"))."_H") 
      elseif expand("%:e") == 'h' 
      call append(line(".")+10, "#pragma once") 
         elseif &filetype == 'c' 
      call append(line(".")+10,"#include <stdio.h>") 
      call append(line(".")+11,"#include <stdlib.h>") 
      call append(line(".")+12,"#include <sys/types.h> // 基本数据类型的头文件") 
      call append(line(".")+13,"#include <sys/stat.h> //文件状态所在的伪标准头文件") 
      call append(line(".")+14,"#include <unistd.h> //有read\\write等函数") 
      call append(line(".")+15,"int main(int argc, char* argv[])") 
      call append(line(".")+16,"{") 
      call append(line(".")+17,"              ") 
      call append(line(".")+18,"              ") 
      call append(line(".")+19,"    return 0;") 
      call append(line(".")+20,"}") 
        elseif &filetype == 'cpp' 
      call append(line(".")+10, "#include \"".expand("%:t:r").".h\"") 
         endif
    "新建文件后,自动定位到文件末尾
    autocmd BufNewFile * normal G
    endfunc
    

四、gcc

gcc编译四阶段

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Z3ett19-1608271664680)(/media/libaisong/学习资料/A工作相关/Linux系统编程图片/gcc编译程序4步骤.png)]

  • gcc常用命令参数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zGeKrEQP-1608271664683)(/media/libaisong/学习资料/A工作相关/Linux系统编程图片/gcc常用命令参数.png)]

gcc常用命令:
  • -I: 指定头文件所在目录
  • -c:只做预处理、编译、汇编,不链接。得到二进制文件。
  • -o:-o filename 生成名字为filename的可执行文件,如果不指定名字,则默认为a.out
  • -g:编译时添加调试语句,主要支持gdb调试
  • -Wall:显示所有警告信息
  • -D: 向程序中”动态“注册宏定义

五、动态库和静态库

1、区别

静态库实际上是将该库复制到使用该库的函数中,因此占用空间大。在使用时才动态绑定

静态库:对空间要求较低,对时间要求较高的核心程序中。

动态库:对空间要求较高,对时间要求较低的核心程序中。

2、静态库
2.1 制作静态库步骤

ar rcs/rs libmylib.a xxx.o xxx.o

  • 1、编译自定义的函数
//加法静态库 add.c
int add(int a, int b)
{
    return a+b;
}
//减法静态库 sub.c
int add(int a, int b)
{
    return a - b;
}
  • 2、将.c函数编译为 .o文件
gcc -c add.c -o add.o  //将add.c文件编译为.o文件
gcc -c sub.c -o sub.o //将sub.c文件编译成.o文件

​ 这儿也可以使用gcc -c add.c,直接生成add.o

  • 3、使用ar rs libxxx.a xx.o xx.o...:将.o文件制作成静态库
ar rcs libmyMath.a add.o sub.o dev1.o  
  • 4、使用静态库(编译静态库到可执行文件中)
gcc testStaticlib.c libMyMath.a -o test; //编译
./test  //执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xatePmiU-1608271664686)(/media/libaisong/学习资料/A工作相关/Linux系统编程图片/静态库执行结果.png)]

上图中的implicit警告信息,是因为编译器在碰到函数时需要知道函数的声明或定义,否则编译器就会做隐士声明。

==但是编译器只能声明 返回值为int类型的函数。==如果是其他类型则会出现错误,而不是warning

所以使用一个.h包含文件声明

头文件守卫:防止头文件被重复包含

#ifndef \_MYMATH_H_
#define \_MYMATH_H_
int add(int,int);
int sub(int, int);

#endif
3、动态库

制作动态库的步骤:

  • 1、将 .c 生成为 .o 文件(生成与位置无关的代码 -fPIC)
gcc -c add.c -o add.o -fPIC
  • 2、使用 gcc -shared 制作动态库
gcc -shared -o lib库名.so add.o sub.o div1.o
  • 3、编译可执行程序时,指定使用的动态库
    • -l: 指定库名
    • -L: 指定库路径
    • -I(大写字母i):指定头文件 .h 路径
gcc test.c -o a.out -lmyMath -L
gcc testDayLib.c -o a.out -lmyMath -L./lib -I./inc
  • 4、运行 a.out出错:./a.out: error while loading shared libraries: libmyMath.so: cannot open shared object file: No such file or directory. ldd a.out查看错误信息

    • 原因(动态链接器未找到库):
      • 链接器:工作于链接阶段,工作需要 -l -L 指定库名和库文件路径
      • 动态链接器:工作与程序运行阶段,工作时需要提供动态库所在目录的位置
      • 链接器和动态链接器没有关系
    • 解决方法:

    【1】通过环境变量改变,使其找到动态可所在目录的位置。

    • export LD_LIBRARY_PATH=./lib/
      
    • 再次运行 ./a.out成功 (临时生效。这只是单次生效,重启终端后失效)

    【2】更改 ~/.bashrc文件使其上面的操作永久生效。

    1.vi ~/.bashrc 打开bashrc文件

    1. 写入 export LD_LIBRARY_PATH=./lib/ 保存
    2. (1) . .bashrc; (2) source.bashrc; (3) 重启终端 (让其生效的三种方法),让修改后的bashrc文件生效。
    3. 运行./a.out成功

    【3】将自定义库拷贝到 /lib (系统c库)

    【4】修改配置文件

    1. sudo vi /etc/ld.so.conf
    2. 写入动态库绝对路径
    3. sudo ldconfig -v 使配置文件生效
    4. ./a.out 成功   可以使用 ldd a.out查看详细信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FMt9XIoL-1608271664687)(/media/libaisong/学习资料/A工作相关/Linux系统编程图片/内存分布.png)]

地址回填:

六、gdb

七、makefile项目管理

八、文件操作

man 2 函数名字:查看函数具体的使用

1. open()函数
//SYNOPSIS
   #include <sys/types.h>
   #include <sys/stat.h>
   #include <fcntl.h>
//函数原型
   int open(const char *pathname, int flags); //打开已有文件
   int open(const char *pathname, int flags, mode_t mode);  //打开已有文件,如果不存在则创建,指定文件的访问权限

   int creat(const char *pathname, mode_t mode);

   int openat(int dirfd, const char *pathname, int flags);
   int openat(int dirfd, const char *pathname, int flags, mode_t mode);
int open(const char *pathname, int flags, mode_t mode);
/*
	pathname:文件路径
	
	flags:
			主类:O_RDONLY, O_WRONLY, or O_RDWR
			 副类:O_CLOEXEC, O_CREAT(不存在就创建), O_DIRECTORY, O_EXCL, O_NOCTTY, O_NOFOLLOW, O_TMPFILE, O_TRUNC(如果文件已存在则删除原文件中原有数据), O_APPEND(以追加的方式打开)
	
	mod:参数2指定了O_CREAT,取8进制数,设置文件的访问权限。
				但是注意:创建文献的最终权限 = mode & ~umask(umask默认为0022(八进制),取反之后为 0111 1101 1101)
				
	返回值:成功返回对应的文件描述符。失败返回-1
*/
#include<stdio.h>
 #include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
 #include <fcntl.h>
 
 int main()
 {

   int fd1 = open("./test01.txt", O_RDONLY|O_CREAT,0777);
   printf("fd1 = %d\n",fd1);
   return 0;
 }
2. read()函数
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>

int main()
{

    //函数原型:ssize_t read(int fd, void *buf, size_t count);
    //ssize_t:  表示有符号整型,size_t表示无符号整型
    //buf:  存数据的缓冲区
    //count:  缓冲区的大小,一般为1024或2048
    //返回值:ssize_t类型, 
    // 失败的返回-1,  0表示读到文件末尾, 其他整数为读取数据的大小
    //返回-1 , 并且errno = EAGIN或 EWOULDBLOCK 说明不是read失败,而是read在以 【非阻塞方式】 读一个设备文件或网络文件,并且文件无数据

    int fd1 = open("./open.c",O_RDONLY|O_CREAT,0742);
    char buf[1024]; //缓冲区一般定义为1024或2048个字节大小
    int fd = read(fd1, buf, 1024);
    printf("读取字节数为:fd1 = %d\n",fd);

    close(fd1);//关闭文件
    return 0;
}
3. write()函数
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>
int main()
{
    
    //函数原型:ssize_t write(int fd, const void *buf, size_t count);
    //fd:文件描述符
    //buf:缓冲区
    //count:写入的字节数
    //返回值:写入的字数节,-1为错误
    char buf[1024];                                             //缓冲区
    int n = 0;                                                  //n为每次写入的字节数
    int fd1 = open("./open.c",O_RDONLY);                        //read
    int fd2 = open("./test03.c",O_RDWR|O_CREAT|O_TRUNC,0664);   //write,注意权限,要设置未可以write,即RDWR
    while((n = read(fd1,buf,1024)) != 0)
    {
       int res =  write(fd2,buf,n);
       if(res == -1){                                           //如果为-1,则出现错误
            perror("open error");                               //perror为系统函数,自动打印错误信息
            exit(1);                                            //退出
       }
       printf("写入的字节数为:res = %d\n",res);
    }

    close(fd1);                                             //不要忘记关闭文件
    close(fd2);
    return 0;
}
4.文件描述符
4. 1 预读入缓输出
  • strace命令查看具体执行过程,可以查看到fputs默认4096k数据,才切换到内核态写入、读取数据。而read、write函数时系统函数,指定buf的大小为多少,就固定多少字节数据切换一次用户态到内核态读取、写入数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g3wzPqwd-1608271664688)(/media/libaisong/学习资料/A工作相关/Linux系统编程图片/预读入缓输出.png)]

4.2 文件描述符
  • PCB进程控制块,本质是结构体
  • 结构体成员对应的是文件描述符表
  • 文件描述符:0/1/2/3/…/1023。使用原则是使用文件描述符中最小能够使用的
  • 0—STDIN_FIFENO; 1—STDOUT_FIFENO; 2—STDERR_FIFENO

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wr0FGLcA-1608271664689)(/media/libaisong/学习资料/A工作相关/Linux系统编程图片/PCB文件描述符.png)]

5.阻塞、非阻塞

产生阻塞的场景:读设备文件和网络文件(读常规文集爱你无阻塞概念)

/dev/tty —终端文件

阻塞、非阻塞是设备文件的属性

6. fcntl修改文件属性
#include <unistd.h>
#include <fcntl.h>
//函数原型
int fcntl(int fd, int cmd, ... /* arg */ );

int flags = fcntl(fd, F_SETEL);
flag |= O_NONBLOCK;
fcntl(fd, F_SETEL, flag);
//获取文件状态:F_GETFL
//设置文件状态:F_SETFL
7. lseek()函数
#include <sys/types.h>
#include <unistd.h>
//函数原型
off_t lseek(int fd, off_t offset, int whence);
 
  • 参数:
    fd:文件描述符号
    offset:偏移量
    whence:从何处开始偏移,取值 SEEK_SET/SEEK_CUR/SEEK_END 如下所示:
    SEEK_SET
    The file offset is set to offset bytes.
    SEEK_CUR
    The file offset is set to its current location plus offset bytes.
    SEEK_END
    The file offset is set to the size of the file plus offset bytes.
  • 返回值:
    成功:校起始偏移位置的偏移量
    失败:-1 errno
  • 应用场景:
    • 文件的“读”、“写”使用同一偏移位置
    • 使用lseek获取文件大小
    • 使用lseek扩展文件大小:要想使文件大小真正扩展,必须引起IO操作
      • 使用truncate函数,直接扩展文件
8. 传入传出参数
1. 传入参数
  • 指针作为函数参数
  • 通常由const关键字修饰
  • 指针指向有效区域,在函数内部**做读操作**

starcpy:

2. 传出参数
  • 指针作为函数参数
  • 在函数调用之前,指针指向的空间可以无意义,但必须有效
  • 在函数内部,做写操作
  • 函数调用结束后,充当函数返回值

strcpy(): char *strcpy(char *dest, const char *src);

3.传入传出参数
  • 指针作为函数参数
  • 在函数调用之前,指针指向的空间有实际意义
  • 在函数内部,先做读操作,后做写操作
  • 函数调用结束后,充当函数返回值
9. stat()函数

函数原型:int stat(const char *path, struct stat *buf)

参数:

  • path: 文件路径
  • buf: (传出参数)存放文件属性

返回值

  • 0: 成功
  • -1: 失败,errno
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include<stdio.h>
#include<stdlib.h>

int main(int argc, char* argv[])
{
    struct stat subuf;

    int ret = stat(argv[1], &subuf);
 
    if(ret == -1)
    {
        perror("stat error");
    }

    printf("file size: %ld\n",subuf.st_size);
   // printf("file mode");

    return 0;
}
  • 获取文件大小:buf.st_size

  • 获取文件类型: buf.st_mode

  • 获取文件权限: buf.st_model

  • 符号穿透:stat有符号穿透问题, lstat无

10. 目录权限

文件与目录权限的区别

rwx
文件文件的内容可以被查看:cat、more、 less…内容可以被修改: vi、>….可以运行产生一个进程: .文件名
目录目录可以被浏览: ls、tree…创建、删除、修改文件: mv、touch、mkdir可以被打开、进入
  • 目录操作函数:opendir() readdir() closedir()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值