【README】Linux系统编程必读:本专栏内容提要以及系统调用接口总结
发布日期:2021-05-06 07:22:36 浏览次数:44 分类:技术文章

本文共 11564 字,大约阅读时间需要 38 分钟。

前言

本专栏是Linux系统编程学习笔记,内容主要是Linux的认识,命令,进程,IPC,信号,文件和多线程等,点击标题可进行跳转

  1. 第一部分:主要是该篇博客涉及到的核心知识点
  2. 第二部分:Linux系统编程中会涉及到大量系统调用,所以会以思维导图的方式总结
  3. 第三部分:将博客中重要的代码操作拿出来,方便复习,练习

第一部分:博客知识点

(1)基础篇

  • 命令lscdtouchmkdirrmcpmvcatgreptarless是读者必须必须要掌握的,他们的各种参数一定要滚瓜烂熟,因为日后他们的使用率几乎高达80%
  • 掌握如何在Linux中添加用户及设置用户密码
  • 理解Linux文件结构
  • 理解相对路径与绝对路径

  • Linux中的超级用户和普通用户
  • su和sudo的区别
  • Linux中的权限管理
  • 权限的两种表示方法:8进制表示法字符表示法
  • 使用chmod修改权限
  • 目录的读写还有执行权限如何理解
  • 使用chown更改文件拥有者,使用chgrp更改文件所属组
  • 理解umask并会用umask设置默认权限
  • 理解粘滞位

此篇博客的内容较为简单,但是很重要——如何在Linux中安装软件,需要大家掌握yum的一些常用用法

(2)入门篇

  • vim编辑器的三种模式
  • vim基本操作

  • 在Linux下体会C语言文件的编译过程
  • 掌握gcc和g++
  • 了解函数库

  • 学会使用makefile

(3)进程入门

  • 理解操作系统的作用
  • 明白系统调用接口和库的关系

  • 操作系统是如何管理进程的
  • PCB与task_struct的关系
  • 什么是进程
  • 掌握系统调用接口fork
  • fork为什么有两个返回值

  • 进程的一些状态的理解
  • 什么是僵尸进程和孤儿进程

  • 如何理解进程的优先级
  • PR和NI是什么

  • 环境变量的理解
  • 如何查看和设置环境变量
  • main函数的三个参数掌握
  • 环境变量全局属性

  • C/C++内存空间复习
  • 深刻理解进程地址空间
  • 页表的作用
  • 虚拟内存和物理内存

  • 对于写时拷贝的理解

  • 进程退出时的三种情况
  • exit的return的区别
  • _exit和exit的区别

  • 进程为什么需要被等待
  • 阻塞式等待和非阻塞式等待的区别
  • wait方法
  • waitpid方法的第二个参数statuts的理解,它是怎样获取到退出码的,以及低16位的变化情况

  • 理解进程程序替换
  • 6个进程程序替换的接口的掌握
  • 重点理解参数的一些意义

  • 理解标准输入,标准输出和标准错误
  • 掌握open,write,read和close的用法

  • 深刻理解文件描述符fd
  • 0,1,2号文件都代表什么
  • 从关闭1号文件角度理解重定向
  • 从内核角度理解操作系统如何管理文件
  • 为什么说一切皆文件

  • 全缓冲和行缓冲的区别
  • 理解C语言提供的用户级缓冲区

  • 掌握重定向的本质
  • 使用dup2完成重定向

  • 理解硬盘的物理结构和逻辑结构
  • 通过叙述如何查找文件,创建文件,删除文件依次达到对inode和block的理解
  • 掌握软硬链接的本质及其他们的区别

  • 深刻理解动态库和静态库的区别
  • 明白库与头文件的关系

  • 明白我们是怎样给别人提供库的
  • 在静态库中如何打包.o文件,如何将头文件和库封装在一个文件中,以及编译时的-I,-L,-l三个参数头代表什么意思
  • 在动态库中编译时加入的选项和打包时用的选项,以及最终编译时如何将动态库添加到动态库的环境变量里面

  • 进程间通信的本质

  • 理解管道是什么
  • 理解管道的四大特性
  • 使用管道完成进程间的通信
  • 匿名管道和命名管道的区别

  • 理解管道和共享内存的区别
  • 掌握共享内存系列的接口的用法

  • 信号的处理动作都有哪些
  • 掌握signal函数的用法,理解其形参及返回值的含义

  • 四种产生信号的方式及其特点
  • 了解core dump
  • 了解SIGALRM信号

  • 信号相关术语:未决,阻塞,递达
  • 信号在内核中的表示:pending位图,block的位图的深刻理解

  • 了解sigset_t类型
  • 掌握信号集操作函数的用法

  • 理解用户态和内核态
  • 信号是什么是处理和捕捉的——详细叙述信号捕捉过程

  • 理解可重入函数
  • 理解volatile关键字的作用
  • 如何使用SIGCHLD信号清理僵尸进程

  • 理解Linux中的线程
  • 理解进程和线程的关系
  • 理解LWP
  • 线程的优缺点

  • 掌握线程创建,终止,,等待和分离的pthread库函数
  • 深化对Linux线程的理解

  • 理解临界区和临界资源
  • 理解互斥锁的作用
  • 理解互斥锁的实现原理
  • 可重入函数与线程安全
  • 死锁相关

  • 线程的同步和互斥的理解
  • 掌握条件变量函数的使用

  • 理解线程池,亲自实现线程池
  • 线程池概念相关的优缺点

第二部分:涉及的系统调用命令总结

(1)进程部分

在这里插入图片描述

(2)基础IO部分

在这里插入图片描述

(3)进程间通信部分

在这里插入图片描述

(4)进程信号部分

在这里插入图片描述

(5)多线程部分

在这里插入图片描述

第三部分:重要代码

1:理解fork的作用

#include 
#include
int main(){ prinf("还没有执行fork函数的本进程为:%d\n",getpid()); pid_t=fork();//其返回值是pid类型的 sleep(1); if(ret>0)//父进程返回的是子进程ID { while(1) { printf("----------------------------------------------------------------\n"); printf("我是父进程,我的id是:%d,我的孩子id是%d\n",getpid(),ret); sleep(1); } } else if(ret==0)//子进程fork返回值是0 { while(1) { printf("我是子进程,我的id是%d,我的父亲id是%d\n",getpid(),getppid()); sleep(1); } } else printf("进程创建失败\n"); sleep(1); return 0;}

2:理解僵尸状态

#include 
#include
#include
int main(){ // printf("还没执行fork函数时的本进程为:%d\n",getpid()); pid_t ret=fork();//其返回值类型是pid_t型的 sleep(1); if(ret>0)//父进程返回的是子进程ID { while(1) { printf("----------------------------------------------------\n"); printf("父进程一直在运行\n"); sleep(1); } } else if(ret==0)//子进程fork返回是0 { int count=0; while(count<=10) { printf("子进程已经运行了%d秒\n",count+=1); sleep(1); } exit(0);//让子进程运行10s } else printf("进程创建失败\n"); sleep(1); return 0;}

3:理解进程等待及waitpid第二个参数

#include 
#include
#include
int main(){ pid_t ret=fork();//其返回值类型是pid_t型的 sleep(1); if(ret>0)//父进程返回的是子进程ID { printf("父进程正在等待子进程死亡\n"); int st=0; pid_t rec=waitpid(ret,&st,0);//阻塞 if(rec==ret)//如果返回值是子进程id,等待成功 { printf("等待成功\n"); if(WIFEXITED(st))//如果为真,正常退出 { printf("正常退出且退出码为%d\n",WEXITSTATUS(st)); } else { printf("异常退出,信号值为%d\n",st&0x7f); } } else { printf("等待失败\n"); exit(0); } } else if(ret==0)//子进程fork返回是0 { int count=1; while(count<=10) { printf("子进程[%d]已经运行了%d秒\n",getpid(),count); count++; sleep(1); } exit(3); } else printf("进程创建失败\n"); sleep(1); return 0;}

4:理解进程程序替换

//myprocess.c#include 
#include
#include
int main(){ int count=0; while(count<5) { printf("Hello World\n"); sleep(1); count++; } printf("%s\n",getenv("myenv")); return 0;}//test.c#include
#include
#include
int main(){ char* env[]={ "myenv=you_can_see_this_env",NULL}; printf("替换函数前\n"); execle("./myprocess.exe","myprocess.exe",NULL,env); printf("替换函数后\n");}

5:理解文件描述符

#include 
#include
#include
int main(){ int fd1=open("log1.txt",O_WRONLY);//打开错误 int fd2=open("log2.txt",O_WRONLY|O_CREAT);//打开成功 int fd3=open("log3.txt",O_WRONLY|O_CREAT);//打开成功 int fd4=open("log4.txt",O_WRONLY|O_CREAT);//打开成功 int fd5=open("log5.txt",O_WRONLY);//打开错误 printf("%d\n",fd1); printf("%d\n",fd2); printf("%d\n",fd3); printf("%d\n",fd4); printf("%d\n",fd5);}

6:理解匿名管道

#include  
#include
#include
#include
int main(){ int pipefd[2]={ 0}; pipe(pipefd); pid_t id=fork(); if(id==0)//child { close(pipefd[0]); const char* msg="This is the data that the child process wrote"; while(1) { write(pipefd[1],msg,strlen(msg)); sleep(1); } } else//father { close(pipefd[1]); char buffer[64]; while(1) { ssize_t ret=read(pipefd[0],buffer,sizeof(buffer)-1); if(ret>0)//判断是否读到 { buffer[ret]='\0';//加上结束标志,便于输出 printf("The father process got the information:%s\n",buffer); } } } return 0;}

7:理解命名管道

server.c

#include 
#include
#include
#include
int main(){ umask(0);//屏蔽命令行umask干扰 if(mkfifo("./fifo",0666)==-1)//如果mkfifo返回值是-1,创建失败 { perror("打开失败"); return 1; } int fd=open("fifo",O_RDONLY);//服务端以只读的方式打开管道文件 if(fd>=0) { char buffer[64]; while(1) { printf("客户端正在接受消息\n"); printf("############################\n"); ssize_t ret=read(fd,buffer,sizeof(buffer)-1); if(ret>0) { buffer[ret]='\0'; printf("服务端接受到客户端消息:%s\n",buffer); } else if(ret==0)//如果客户端退出,将会读到文件结束,所以服务端也要退出 { printf("客户端已经下线,服务端下线\n"); break; } else { perror("读取失败\n"); break; } } }}

client.c

#include 
#include
#include
#include
#include
int main(){ int fd=open("fifo",O_WRONLY);//直接打开管道文件 if(fd>=0) { char buffer[64];//从键盘读入数据到这个缓冲区 while(1) { printf("客户端-请输入消息:"); ssize_t ret=read(0,buffer,sizeof(buffer)-1);//从键盘读入数据 if(ret>0) { buffer[ret]='\0'; write(fd,buffer,ret);//读入ret个数据就向管道中写入ret个数据 } } }}

8:理解共享内存

server.c

#include 
#include
#include
#include
#include
#define PATHNAME "tmp"#define PROJ_ID 88#define SIZE 4096int main(){ key_t k=ftok(PATHNAME,PROJ_ID); printf("key值:%#X\n",k); int shmid=shmget(k,SIZE,IPC_CREAT | IPC_EXCL|0666); if(shmid<0) { perror("creat failed"); return 1; } char* shmaddr=shmat(shmid,NULL,0);//挂接,注意强转 while(1)//每1s打印一次 { sleep(1); printf("%s\n",shmaddr); } shmdt(shmaddr);//脱离 shmctl(shmid,IPC_RMID,NULL);//释放 return 0;}

client.c

#include 
#include
#include
#include
#include
#define PATHNAME "tmp"#define PROJ_ID 88#define SIZE 4096int main(){ key_t k=ftok(PATHNAME,PROJ_ID); printf("key值:%#X\n",k); int shmid=shmget(k,SIZE,0);//服务端已经申请了,写成0直接获取 if(shmid<0) { perror("creat failed"); return 1; } char* shmaddr=shmat(shmid,NULL,0);//挂接,注意强转 int i=0; while(i<26) { shmaddr[i]=97+i;每隔5s依次输入a,b,c........................... i++; sleep(5); } shmdt(shmaddr);//脱离 return 0;}

9:理解signal函数

#include 
#include
#include
void handler(int sig){ printf("catch a sin : %d\n",sig);}int main(){ signal(2,handler);//一旦捕捉到2号信号,将会执行handler函数内的操作 while(1) { printf("I Am runnng now...\n"); sleep(1); } return 0;}

10:理解信号集操作函数

#include 
#include
#include
void handler(int sig){ printf("获得信号:%d\n",sig);}void print_pending(sigset_t* pending){ int i=1; for(i=1;i<=31;i++) { if(sigismember(pending,i)) { printf("1");//只要i信号存在,就打印1 } else { printf("0");//不存在这个信号就打印0 } } printf("\n");}int main(){ signal(2,handler);//捕捉 sigset_t pending;//定义信号集变量 sigset_t block,oblock;//定义阻塞信号集变量 sigemptyset(&block); sigemptyset(&oblock);//初始化阻塞信号集 sigaddset(&block,2);//将2号信号添加的信号集 sigprocmask(SIG_SETMASK,&block,&oblock);//设置屏蔽关键字 int cout=0; while(1) { sigemptyset(&pending);//初始化信号集 sigpending(&pending);//读取未决信号集,传入pending print_pending(&pending);//定义一个函数,打印未决信号集 sleep(1); cout++; if(cout==10)//10s后解除阻塞 { printf("解除阻塞\n"); sigprocmask(SIG_SETMASK,&oblock,NULL); } }}

11:理解线程的创建,等待等

#include 
#include
#include
void* new_thread(void* arg){ while(1) { printf("我是新线程,我的线程id是%p\n",pthread_self()); sleep(5); int a=1/0; //浮点异常 }}int main(){ pthread_t tid;//线程的ID pthread_create(&tid,NULL,new_thread,(void*)"我是新线程"); while(1) { printf("-----------------------------------------------------------\n"); printf("我是主线程,我的线程id是:%p,新线程的id是%p\n",pthread_self(),tid); void* ret;//获取退出码 pthread_join(tid,&ret); printf("主线程阻塞等待新线程,退出码码为%d\n",(int)ret); break; }}

12:理解互斥锁

#include 
#include
#include
int tickets=1000;pthread_mutex_t lock;//申请一把锁void scarmble_tickets(void* arg){ long int ID=(long int)arg;//线程ID while(1)//多个线程循环抢票 { pthread_mutex_lock(&lock);//那个线程先到,谁就先锁定资源 if(tickets>0) { usleep(1000); printf("线程%ld号抢到了一张票,现在还有%d张票\n",ID,tickets); tickets--; pthread_mutex_unlock(&lock);//抢到票就解放资源 } else { pthread_mutex_unlock(&lock);//如果没有抢到也要释放资源,否则线程直接退出,其他线程无法加锁 break; } }}int main(){ int i=0; pthread_t tid[4];//4个线程ID pthread_mutex_init(&lock,NULL);//初始化锁 for(i=0;i<4;i++) { pthread_create(tid+1,NULL,scarmble_tickets,(void*)i);//创建4个线程 } for(i=0;i<4;i++) { pthread_join(tid+1,NULL);//线程等待 } pthread_mutex_destroy(&lock);//销毁锁资源 return 0;}

13:理解条件变量

#include 
#include
#include
#include
pthread_mutex_t lock;//锁pthread_cond_t cond;//条件void* thread_a(void* arg)//其他线程被唤醒{ const char* name=(char*)arg;//线程名字 while(1) { pthread_cond_wait(&cond,&lock);//一直在等待条件成熟 printf("%s被唤醒了\n=================================================\n",name); }}void* thread_1(void* arg)//让线程1唤醒其他线程{ const char* name=(char*)arg; while(1) { sleep(rand()%5+1);//随机1-5秒唤醒 pthread_cond_signal(&cond); printf("%s现在正在发送信号\n",name); }}int main(){ pthread_mutex_init(&lock,NULL);//初始化锁 pthread_cond_init(&cond,NULL);//初始化条件 pthread_t t1,t2,t3,t4,t5;//创建两个线程 pthread_create(&t1,NULL,thread_1,(void*)"线程1"); pthread_create(&t2,NULL,thread_a,(void*)"线程2"); pthread_create(&t3,NULL,thread_a,(void*)"线程3"); pthread_create(&t4,NULL,thread_a,(void*)"线程4"); pthread_create(&t5,NULL,thread_a,(void*)"线程5"); pthread_join(t1,NULL); pthread_join(t2,NULL); pthread_join(t3,NULL); pthread_join(t4,NULL); pthread_join(t5,NULL); pthread_mutex_destroy(&lock); pthread_cond_destroy(&cond);//销毁条件}

14:生产者与消费者模型

15:理解线程池

上。

上一篇:8-5:C++继承之多继承,菱形继承,虚继承,虚基表,继承和组合
下一篇:MySQL数据库

发表评论

最新留言

很好
[***.229.124.182]2025年04月07日 10时05分56秒