本专栏是Linux系统编程学习笔记,内容主要是Linux的认识,命令,进程,IPC,信号,文件和多线程等,点击标题可进行跳转
ls
,cd
,touch
,mkdir
,rm
,cp
,mv
,cat
,grep
,tar
,less
是读者必须必须要掌握的,他们的各种参数一定要滚瓜烂熟,因为日后他们的使用率几乎高达80%此篇博客的内容较为简单,但是很重要——如何在Linux中安装软件,需要大家掌握yum的一些常用用法
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 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
#include
#include
#include
#include
#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
#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:生产者与消费者模型
Linux系统编程39:多线程之基于阻塞队列生产者与消费者模型
Linux系统编程40:多线程之基于环形队列的生产者与消费者模型
15:理解线程池
上。