1)fcntl的使用方法
fcntl作用:可以用fcntl函数改变一个已打开的文件属性而不必重新打开文件;
cpp
堆排序是完全二叉树,但不是排序二叉树;
排序二叉树要求兄弟节点之间有大小关系,比如说左小右大;
堆排序仅要求父亲节点和孩子节点有大小关系;
在vim命令行中,打开另一个文件的操作方法:
输入":e test.txt";复制完程序后(按tab按键查找需要打开的文件),再":e#",回到当前的文件中;
//fcntl的使用方法(获取(修改)文件描述符的权限)
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/fcntl.h>
using namespace std;
int main(int argc, char *argv[])
{
int flags;
flags = fcntl(STDIN_FILENO,F_GETFL); //从标准输入文件描述符中读取文件的flag标志;
flags |= O_NONBLOCK;
fcntl(0,F_SETFL,flags); //把修改后的flags写回标准输入文件结构体中去
char buf[10];
ssize_t n;
while(1)
{
n = read(0,buf,5);
sleep(1);
if(n >= 0) break;
if(errno != EAGAIN)
{
perror("read");
return -1;
}
write(1,"try again?\n",11);
}
write(1,buf,n);
return 0;
}
2)%x和%#x的区别;
c语言中%x的意思是16进制输出。2、c语言中符合%#的意思是带格式输出;
3)putchar(10); //换行;
4)epoll的学习
需要包含头文件#include <sys/epoll.h>
STDIN_FILENO等的使用,需要包含unistd.h头文件
F_GETFL等的使用,需要包含#include<sys/fcntl.h>头文件
ioctl如果用的话,需要包含头文件#include<sys/ioctl.h>
操作目录是时候,需要添加dirent.h和sys/types.h 注:dirent[戴尔闰他]其实就是 directory entry 的缩写
errno 是记录系统的最后一次错误代码
O_ACCMODE是文件的进入权限,默认是文件可读可写;
5)fcntl的使用方法
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/fcntl.h>
using namespace std;
int main(int argc, char *argv[])
{
int flags;
flags = fcntl(atoi(argv[1]),F_GETFL);
switch (flags & O_ACCMODE)
{
case O_RDONLY:
printf("read only");
break;
case O_WRONLY:
printf("write only");
break;
case O_RDWR:
printf("read write");
break;
default:
break;
}
// printf("flags is: %#x\r\n",flags);
putchar(10);
return 0;
}
2)ioctl用于向设备发控制和配置命令
cpp
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdlib.h>
using namespace std;
int main(int argc, char *argv[])
{
struct winsize size;
if(isatty(1) == 0)
{
printf("1 is not tty\r\n");
exit(1);
}
if(ioctl(1,TIOCGWINSZ,&size) < 0)
{
perror("ioctl");
exit(1);
}
printf("%drows,%d columns\n",size.ws_row,size.ws_col); //打印当前终端是多少行,多少列
return 0;
}
//做多线程的时候,功能和入口要分开;避免高内聚,实现低耦合;
//所谓线程池:就是固定的n个线程,抢一个任务队列中的任务;
3)线程池的应用
cpp
//线程池计算5000000里面的素数
#include <stdio.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <thread>
#include <iostream>
#include <mutex>
#include <functional>
#include <vector>
#include <unordered_map>
#include <queue>
#include <condition_variable>
using namespace std;
class Task
{
private:
function<void()> func;
public:
template<typename FUNC_T,typename ...ARGS>
Task(FUNC_T f,ARGS ...args)
{
func = bind(f, forward<ARGS>(args)...);
}
void run()
{
func();
}
};
class ThreadPool
{
private:
std::vector<thread*> trr;
unordered_map<thread::id,bool> running;
queue<Task*> task_q; //任务队列
mutex mtx;
condition_variable cond;
bool starting; //线程开始状态
void stop_running() //毒药方法
{
auto id= this_thread::get_id();
running[id] = false;
return;
}
Task* get_Task()
{
unique_lock<mutex> lock(mtx);
while(task_q.empty()) //防止被假唤醒
{
cond.wait(lock);
}
Task* t = task_q.front();
task_q.pop();
return t;
}
public:
ThreadPool(int n = 1) : trr(n),starting(false)
{
this->start();
}
void worker()
{
auto id = this_thread::get_id();
running[id] = true;
while( running[id])
{
//获取任务
Task* t = get_Task();
//执行
t->run(); //动态申请的,所以得用delete删除掉
delete t;
}
}
void stop()
{
for(int i = 0; i<trr.size(); i++)
{
add_task(&ThreadPool::stop_running,this);
}
for(int i = 0; i<trr.size(); i++)
{
trr[i]->join(); //等待者都被毒死
}
for(int i = 0; i<trr.size(); i++)
{
delete trr[i];
trr[i] = nullptr;
}
}
void start()
{
for(int i = 0; i < trr.size(); i++)
{
trr[i] = new thread(&ThreadPool::worker,this);
}
}
template<typename FUNC_T,typename... ARGS>
void add_task(FUNC_T f,ARGS ...args)
{
//上锁
unique_lock<mutex> lock(mtx);
task_q.push(new Task(f,forward<ARGS>(args)...));
//解锁:同时通知队列中有任务的条件已经达成
cond.notify_one(); //锁被解开
}
virtual ~ThreadPool()
{
this->stop();
while(!task_q.empty())
{
delete task_q.front();
task_q.pop();
}
}
};
bool is_prime(int x)
{
for(int i = 2; i*i <= x; i++)
{
if(x%i == 0)
return false;
}
return true;
}
int count_prime(int l,int r)
{
int cnt = 0;
for(int i = l; i <= r; i++)
{
cnt += is_prime(i);
}
return cnt;
}
void work(int l,int r,int& ret) //工作线程
{
cout<<std::this_thread::get_id()<<": begin"<<endl;
ret = count_prime(l,r);
cout<<std::this_thread::get_id()<<": end"<<endl;
}
int main(int argc, char *argv[])
{
const int batch = 5000000;
int ret[10];
ThreadPool tp(5);
for(int i = 0, j = 1; i <10; i++, j += batch)
{
tp.add_task(work,j+batch-1,ref(ret[i]));
}
tp.stop();
int ans = 0;
for(auto x: ret)
{
ans += x;
}
cout<<ans<<endl;
return 0;
}
4)缓冲区的概要
cpp
//缓冲分为全缓冲,行缓冲,无缓冲;
//stdout是行缓冲
//stderr是无缓冲
int main(int argc, char** argv)
{
for(int i = 0; i < 1025; i++) //从测试看,缓冲区是1024;
fputc('A',stdout);
while(1) //不能让程序结束,如果程序结束,则会自动调用写文件操作;
{
;
}
return 0;
}
5)open打开方式的mode介绍
cpp
S_IRWXU中的S是 stat的意思; 权限位的设置
//如果要用O_CREAT,则要用第3个参数mode,经过测试,如果没有O_CREAT,也能创建文件,但是得到的文件不对;
系统的open的mode用的是S_IRWXU等等,fopen用的如是"r r+"等等; 一个'r',其实带有好多信息;
open中的flags分为必选项和可选项;
errno:错误码,用stderr输出的叫错误描述符
printf("errno = %d\n",errno); //打印错误代码errno
fprintf(stderr,"stderr\n"); //标准出错输出stderr
perror("perror:"); //输出perror:具体错误信息,相当于做翻译用的
printf("strerror:%s\n", strerror(errno)); //输出strerror:具体错误信息
//注意下面两个是不同的头文件
#include<error.h>
#include<errno.h>
3)open的使用方法
int main(int argc, char** argv)
{
if(argc < 2)
{
printf("usage: cmd filename\r\n");
return -1;
}
int fd = open(argv[1],O_WRONLY | O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); //一大堆,其实就是0644
printf("%s\r\n",argv[1]);
if(fd < 0)
{
perror("open rgv[1]");
exit(-1);
}
else
{
perror("open rgv[1]");
}
return 0;
}
6)read,write,ftell,rewind,lseek,fseek,fgetc,fputc等函数的使用及测试文件;
cpp
4) umask的使用 终端输入umask,则输出0002;
exit(-1)和return (-1)的区别在哪里?
exit是退出当前进程,而retrun退出当前函数,所以exit范围更广;
5)如何用终端的命令,查看函数的返回值呢?"echo $?"就可以了;
在文件创建中,flag设置O_CREAT|O_EXCL,目的是这个文件必须是自己创建的,已有文件不可以。EXCL的全程是:exclusive美 [ɪkˈskluːsɪv] 独有的
说白了就是:当前进程必须自己创建一个新的文件,而不能使用已有的文件!!!
6)ssize_t的第一个s代表的是sign,是有符号的意思;
n = read(fd,buf,10); //read的第3个参数是希望读10个数据,但具体多少要看返回值n是多少数据;
//read的使用方法
int main(int argc, char** argv)
{
char buf[128];
if(argc < 2)
{
printf("usage: cmd filename\r\n");
return -1;
}
int fd = open(argv[1],O_RDONLY);
ssize_t n = read(fd,buf,10);
printf("read bytes is %d\r\n",n);
for(ssize_t i = 0; i < n; i++)
printf("%c",buf[i]);
putchar(10);
n = read(fd,buf,10); //read的第3个参数是希望读10个数据,但具体多少要看返回值n是多少数据;
printf("read bytes is %d\r\n",n);
for(ssize_t i = 0; i < n; i++)
printf("%c",buf[i]);
putchar(10);
close(fd);
return 0;
}
7) 从终端设备读,通常以行为单位,读到换行符就返回了,再把读到的数据输出到屏幕上;
ssize_t n = read(STDIN_FILENO,buf,10); //shell一直监视终端stdin是否有数据,
当一个终端的程序运行起来的时候,shell程序就会退到后台,不运行了,知道终端的程序运行完,才可以使shell继续运行;
int main(int argc, char** argv)
{
char buf[128];
ssize_t n = read(STDIN_FILENO,buf,10);
if(n < 0)
{
perror("read stdin");
exit(-1);
}
n = write(STDOUT_FILENO,buf,n);
if(n < 0)
{
perror("write stdin");
exit(-1);
}
return 0;
}
8)程序的三种状态,睡眠状态,和运行状态;运行状态又分两种,正在被调度执行状态和就绪状态;
#define EWOULDBLOCK EAGAIN /* Operation would block */
EWOULDBLOCK == EAGAIN;EWOULDBLOCK = would block 虚拟语气,应该阻塞
终端就是:/dev/tty = STDIN_FILENO|STDOUT_FILENO;
9)非阻塞从终端上读取数据,并打印到屏幕
int main(int argc, char** argv)
{
char buf[128];
ssize_t n;
int fd = open("/dev/tty",O_RDONLY|O_NONBLOCK); //O_RDONLY|O_NONBLOCK
if(fd < 0)
{
perror("open /dev/tty");
exit(-1);
}
while(1)
{
ssize_t n = read(fd,buf,10);
if(n >= 0 )
{
printf("read num %d\r\n",n);
write(STDOUT_FILENO,buf,n);
break;
}
if(errno != EAGAIN)
{
perror("other error");
exit(-1);
}
write(STDOUT_FILENO,"try try?\n",9);
sleep(1);
}
close(fd);
return 0;
}
10)EOF的使用及fgetc的使用
EOF宏就是-1; //对"-1"的补码是255,按位取反是0;如:while(~(c = fgetc(fp)))
int main(int argc, char** argv)
{
FILE* fp = fopen("./test.txt","r");
char c;
while((c = fgetc(fp)) != EOF) //千万别写成这样: while(c = fgetc(fp) != EOF)
printf("%c",c);
fclose(fp);
return 0;
}
11)r r+ w w+分别代表:
r: 以只读方式打开文件;
r+: 以读写方式打开文件,原有文件不清除;
w: 以只写方式打开文件,如果文件不存在,则自动创建。如果文件已经存在,把原有的文件内容删除光。再写入新的内容
w+: 以读写模式打开文件,原有文件清空;
int main(int argc, char** argv)
{
FILE* fp = fopen(argv[1],"w+");
if(!fp)
{
perror("open file");
exit(-1);
}
fputc('J',fp);
fputc('L',fp);
fputc('5',fp);
int n = fgetc(fp); //如果在fopen中,模式是"w",则输出的是"-1";
printf("-------- %d\r\n",n);
fclose(fp);
return 0;
}
12)利用fgetc和fputc实现文件的拷贝,案例如下
int main(int argc, char** argv)
{
if(argc < 3)
{
printf("uage: cmd + src + dest\r\n");
return 1;
}
FILE* fp1 = fopen(argv[1],"r");
if(!fp1)
{
perror("open read file");
exit(-1);
}
FILE* fp2 = fopen(argv[2],"w");
if(!fp2)
{
perror("open write file");
exit(-1);
}
char c;
while(~(c = fgetc(fp1)))
fputc(c,fp2);
fclose(fp1);
fclose(fp2);
return 0;
}
13)fseek的使用,seek是寻找的意思;fseek函数的功能:设置文件指针stream的位置。
whence is:SEEK_CUR,SEEK_END,SEET_SET;
SEET_SET是从文件指针的开始位置计算,按道理说应该写SEET_START更好;
rewind函数:C程序中的库函数,功能是将文件内部的指针重新指向一个流的开头
ftell函数: 在c语言中,ftell函数用来返回当前文件指针的位置;
//fseek,rewind,ftell等函数的使用案例:
int main(int argc, char** argv)
{
FILE* fp = fopen(argv[1],"r+");
if(!fp)
{
perror("open file");
exit(-1);
}
// rewind(fp);
fseek(fp,3,SEEK_SET);
printf("pos = %d\n",ftell(fp));
fputc('M',fp);
fputc('D',fp);
printf("pos = %d\n",ftell(fp));
fclose(fp);
return 0;
}
14)判断一个文件的大小,案例如下:
int main(int argc, char** argv)
{
FILE* fp = fopen(argv[1],"r");
if(!fp)
{
perror("open file");
exit(-1);
}
fseek(fp,0,SEEK_END);
printf("file size is: = %d\n",ftell(fp));
fclose(fp);
return 0;
}
14)lseek的使用方法:lseek的返回值是有意义的,会返回文件的pos位置;从0开始计算;
计算文件的开始位置的偏移量;
int main(int argc, char** argv)
{
char buf[10];
int n = 0;
int fd = open("./test2.txt",O_RDONLY);
if(fd < 0)
{
perror("open file");
exit(1);
}
//123MDN789
n = read(fd,buf,1);
write(STDOUT_FILENO,buf,n); //Y
lseek(fd,5,SEEK_CUR); //如果SEEK_SET,则是N,如果是SEEK_CUR,则是7;
n = read(fd,buf,1);
write(STDOUT_FILENO,buf,n); //N
close(fd);
return 0;
}