【README】Linux系统编程必读:本专栏内容提要以及系统调用接口总结

文章目录

前言

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

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

第一部分:博客知识点

(1)基础篇

Linux系统编程1:Linux中使用率最高的一些命令

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

Linux系统编程2:详解Linux中的权限问题

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

Linux系统编程3:基础篇之详解Linux软件包管理器yum

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

(2)入门篇

Linux系统编程4:入门篇之最强编辑器vim的使用攻略

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

Linux系统编程5:Linux系统编程5:入门篇之程序编译过程

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

Linux系统编程6:入门篇之如何拿下gdb调试器

Linux系统编程7:入门篇之Linux项目自动化构建工具

  • 学会使用makefile

(3)进程入门

Linux系统编程9:进程入门之操作系统及其管理

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

Linux系统编程10:进程入门之系统编程中之进程

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

Linux系统编程11:进程入门之进程状态

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

Linux系统编程12:进程入门之进程的优先级及PR和NI

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

Linux系统编程13:进程入门之Linux中的环境变量的概念及其相关命令

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

Linux系统编程14:进程入门之进程地址空间

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

Linux系统编程15:进程控制之如何创建进程和写时拷贝技术

  • 对于写时拷贝的理解

Linux系统编程16:进程控制之进程终止以及终止进程的三种情况

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

Linux系统编程17:进程控制之进程等待

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

Linux系统编程18:超详解进程程序替换及相关接口

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

Linux系统编程19:基础IO之Linux中的标准输入和输出

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

Linux系统编程20:基础IO之Linux是如何管理文件的

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

Linux系统编程21:基础IO之全缓冲和行缓冲的区别

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

Linux系统编程22:基础IO之掌握重定向的本质和使用dup2完成重定向

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

Linux系统编程23:基础IO之了解硬盘结构和链接

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

Linux系统编程24:基础IO之动静态库

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

Linux系统编程25:基础IO之亲自实现一个动静态库

Linux系统编程26:进程间通信之进程间通信的基本概念

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

Linux系统编程27:进程间通信之管道的基本概念

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

Linux系统编程28:进程间通信之共享内存和相关通信接口

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

Linux系统编程29:进程信号之什么是信号及signal函数

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

Linux系统编程30:进程信号之产生信号的四种方式

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

Linux系统编程31:进程信号之什么是信号的阻塞及相关术语

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

Linux系统编程32:进程信号之详解信号集操作函数

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

Linux系统编程33:进程信号之详解信号的捕捉过程,用户态和内核态

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

Linux系统编程34:进程信号之可重入函数,volatile关键字

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

Linux系统编程35:多线程之如何理解Linux中的线程以及轻量级进程LWP

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

Linux系统编程36:多线程之线程控制之pthread线程库

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

Linux系统编程37:多线程之什么是临界区和临界资源

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

Linux系统编程38:多线程之什么是线程同步以及条件变量函数

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

Linux系统编程39:多线程之基于阻塞队列生产者与消费者模型

Linux系统编程40:多线程之基于环形队列的生产者与消费者模型

Linux系统编程41:多线程之线程池的概念及实现

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

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

(1)进程部分

在这里插入图片描述

(2)基础IO部分

在这里插入图片描述

(3)进程间通信部分

在这里插入图片描述

(4)进程信号部分

在这里插入图片描述

(5)多线程部分

在这里插入图片描述

第三部分:重要代码

1:理解fork的作用

#include <stdio.h>
#include <unistd.h>

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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
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 <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
  char* env[]={"myenv=you_can_see_this_env",NULL};
  printf("替换函数前\n");
  execle("./myprocess.exe","myprocess.exe",NULL,env);
  printf("替换函数后\n");

}


5:理解文件描述符

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

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  <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

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 <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

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 <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


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 <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <sys/shm.h>

#define PATHNAME "tmp"
#define PROJ_ID 88
#define  SIZE 4096

int 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 <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <sys/shm.h>

#define PATHNAME "tmp"
#define PROJ_ID 88
#define  SIZE 4096

int 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 <stdio.h>
#include <unistd.h>
#include <signal.h>

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 <stdio.h>
#include <unistd.h>
#include <signal.h>

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 <stdio.h>
#include <unistd.h>
#include <pthread.h>


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 <stdio.h>
#include <unistd.h>
#include <pthread.h>


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 <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>

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:生产者与消费者模型

Linux系统编程39:多线程之基于阻塞队列生产者与消费者模型

Linux系统编程40:多线程之基于环形队列的生产者与消费者模型

15:理解线程池

上。

  • 15
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐江湖

创作不易,感谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值