#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
2010年9月16日
C/C++复习
2010年9月17日上午
1、静态库文件与动态库文件
2、vm使用与windows相连
a、Linux设置网卡vi /etc/sysconfig/network-scripts/ifcfg-eth0,修改IP地址
b、设置虚拟网卡IP与linux中的IP在同一网段。
c、在vm中设置以太网卡为虚拟网卡连接vm机。
d、service network restart / service iptables stop / smbpasswd 改smb服务密码
就可以在windows中通过ip 访问linux服务器了。
e、改成在windows中可写linux中的文件vi /etc/samba/smb.conf 改[root]
下的writeable = yes 。添加path = / 则可以在windows中看到linux中的所有目录了。Linux 中的/,在windows中即反映为root目录下面再有root,home,etc等等。
【注】若把虚拟机也设在与主机在同一网段上(如win192.168.0.70;linux192.168.0.211)则虚拟机的网卡设置时,直接设置成[网络连接:桥接,直接连接到物理网络]。
2010年9月17日下午
Markfile
//func.c
#include
int func()
{puts("func(),haha!"); return 0;}
//main.c
#include
int func();
{func(); return 0;}
#makefile
OBJS := $(patsubst %.c,%.o,$(wildcard *.c)) #定义变量
CC := gcc #定义变量CFLAGS := -O2 #定义变量
a.out: $(OBJS) #调用变量
$(CC) $^ -o $@
#%.o:%.c
# $(CC) $(CFLAGS) -c $< -o $@
clean: #定义一个执行体$(RM) *.o *.out -rf
test:
@echo "source file list:$(patsubst %.c,%.o,$(wildcard *.c))"
.PHONY : clean test
2010年9月18日上午
讲课内容:makefile
Makefile中的变量与函数
作业:
#include
int main()
{puts("a.c"); return 0;}
#include
int main()
{puts("b.c"); return 0;}
#include
int main()
{puts("c.c"); return 0;}
写makefile 一次性编译链接三个可执行文件
a.c;
b.c;
c.c
a.out
b.out
c.out
#makefile内容如下:
OUTS := $(patsubst %.c,%.out,$(wildcard *.c))
CC := gcc
all:$(OUTS)
%.out:%.c
$(CC) $^ -o $@ -g –Wall -lpthread
clean:
$(RM) *.o *.out -rf
.PHONY : clean
2010年9月18日下午
Gdb调试工具
G++编译时,带-g –Wall (表示输出所有的警告Warning + all信息。)
Gdb a.out 或gdb 然后用file a.out.
List命令:显示源程序,eg:L 1, 从第一行开始,显示10行
L 1,20 从第一行开始,显示20行。
L a.cpp:main 显示main函数
Ptype CmyVector 看变量的类型。
Bt 显示执行到哪里了,还可以显示函数的调用关系。显示的这一行表示马上要执行的行。P m_size 打印m_size的值。P /x m_size 表示以16进制显示。
Next/step(进入函数内部):单步调试。
Finish 完成当前的函数。即跳出已经进入的函数,回到调入点。
Break(b) 72 即在72行设置断点。删除断点d 72 ; d 则是删除所有断点linux y/n时,默认为n,输入y才表示确定。在某函数处设断点b main。查看断点信息info b
Continue 让程序执行到下一个断点。
退出gdb quit(q)
作业,gdb调试以下程序
#include
using namespace std;
/*
填充该类,以便main()中的调用能正常
工作,注意,不要修改main()中的任何语句
*/
class CMyVector
{
public:
CMyVector()
{
m_pData=NULL;
m_iSize=0;
}
~CMyVector()
{
free(m_pData);
}
CMyVector(const CMyVector &src)
{
m_iSize=src.m_iSize;
m_pData=(int *)malloc(4*m_iSize);
memcpy(m_pData,src.m_pData,4*m_iSize);
}
void push_back(int data)
{
m_iSize++;
m_pData=(int *)realloc(m_pData,4*m_iSize);
m_pData[m_iSize-1]=data;
}
int size()
{
return m_iSize;
}
int operator[](int iIndex)
{
return m_pData[iIndex];
}
CMyVector &operator=(const CMyVector &src)
{
free(m_pData);
m_iSize=src.m_iSize;
m_pData=(int *)malloc(4*m_iSize);
memcpy(m_pData,src.m_pData,4*m_iSize);
return *this;
}
private:
int *m_pData;
int m_iSize;
};
int main()
{
CMyVector v;
v.push_back(2);
v.push_back(6);
v.push_back(4);
v.push_back(5);
for(int i=0; i cout< cout< //上面语句应该输出"2 6 4 5 " CMyVector v2=v; v2.push_back(6); for(i=0; i cout< cout< //上面语句应该输出"2 6 4 5 6 " CMyVector v3; //i的变量要提出在这里写以兼容linux与windows. //int i = 0; v3=v2; v3.push_back(0); for(i=0; i //scoping using obsolete binding at 'i' //?????可以把for 看成一个小函数,另行开stock,i在{}还存在,但是在for ()里不存在了。 cout< cout< //上面语句应该输出"2 6 4 5 6 0 " return 0; } 不清楚则把每个变量还原. 2010年9月20日上午 内容,linux下文件读取方法 Open,write close 与C标准库的fopen,fwrite,fclose区别: 1\write不用关闭即写入到文件,没有缓冲区,比较低级的操作方式。Fwrite有缓冲区,所以fwrite更高效。不用总是写硬盘。所以以后要选用fwrite方式。 2\fwrite移植性好。 3\若在同一进程中,用两种方式打开不同文件(或先关闭再打开另一个),所用的文件打开表是同一个,但是在文件打开表中的文件描述符是不同的。 重要函数fileno(fp) //得到文件指针所指向的文件打开表文件描述符。 fdopen(fd,”wb”) //C标准库函数,通过文件打开表的文件描述符得到文件指针 #include #include #include #include #include #include int main(void) { char buf[] = "hello"; /* 定义文件指针,然后通过文件打开表中的文件描述符写入 FILE *fp; fp = fopen("./file.txt","wb"); printf("fileno %d",fileno(fp)); // fclose(fp); int fd = fileno(fp); //int fd = open("./file.txt",O_CREAT|O_TRUNC|O_WRONLY,0666); printf("fd %d\n",fd); ssize_t ssize = write(fd,buf,strlen(buf)); printf("ssize_t %d\n",ssize); close(fd); fclose(fp); //*/ //* 定义文件fd,再通过文件指针写入 int fd = open("./file.txt",O_CREAT|O_TRUNC|O_WRONLY,0666); FILE *fp; fp = fdopen(fd,"wb"); //属于c标准库 fwrite(buf,sizeof(buf),1,fp); fflush(fp); close(fd); fclose(fp); //*/ return 0; } 注意fd总是从3开始,说明留有0,1,2给系统用的,即标准输入stdin,标准输出stdout,标准错误stderr三个文件用。 思考题。 #include #include #include #include #include #include int main() { FILE *fp; close(1); //关闭第二个stdout fp=fopen("a.txt","wb"); //从下开始找空闲的文件描述符,1 puts("hello,linux!"); fflush(stdout); fclose(fp); return 0; } 2010年9月21日上午(下午无课) 讲课内容: fork与exec parent.out int main() { int fd = open("./a.txt",O_WRONLY|O_CREAT,0666);//在这里打开. char ff[100] = '0'; strcpy(ff,"fd="); char *fdfd; itoa(fd,ff,10); strcat(ff,fdfd); char *env[] = { ff, NULL}; puts(ff); execle("./child.out",NULL,env); puts("error!"); //*/ return 0; } Child.out #include "h.h" int main(int argc,char args[]) { //int fd; char buf[] = "abcd"; write(3,buf,strlen(buf));//在这里写入. close(3); return 0; } 留练习: 1\验证父进程中打开的文件描述符,是否可以在子进程中使用. (可以) 2\先fork建子进程,再在子进程中加载进程. 2010年9月22日上午 内容:第一种进程间通信方式,内存中建立管道(无名管道) 关闭掉所有的连接,则此管道在内存中消失. 1\ 2\ 代码 int main() { int i = 0; if( fork() > 0) { i = 1; }else{ sleep(1); printf(i); } //结果输出i为0; return 0; } 说明父子进程代码虽混在一起,但通信并不容易,父子进程都有i ,地址都一样(但映射到不同的物理内存区域),但仍各用各的,互不干扰. 3\进程间通信方式一,无名管道 3.1\验证同一进程中的管道,写端与读端关闭情况. 关闭读端写,则会导至程序崩溃.先关闭写端读,则反回0,表示已经读到末尾或文件关闭. 3.2\验证共享管道 int main() { int fdPipe[2]; pipe(fdPipe); char *szbuf;// = "abcd"; //赋值了就拷贝到了子进制,在这里赋值就是父子进程共享了初始值. if ( fork() == 0 ) { //child进程,读 //read(fdPipe[0],szbuf,sizeof(szbuf)); //如果不读,则输入szbuf为空.所以,必须 通过管道通信. printf("child,szbuf:%s\n",szbuf); } else { //parent进程,写 write(fdPipe[1],szbuf,sizeof(szbuf)); char *szbuf = "abcd"; //在这里赋值则子进程不会被复制过去 printf("parent,szbuf:%s\n",szbuf); } return 0; } 3.3\popen函数,思考,写一个MyOpen popen函数功能演示: parent.out int main() { FILE *pfp = popen("./child.out","wb"); fputs("aaa",pfp); fputs("bbb",pfp); fputs("ccc",pfp); fclose(pfp); return 0; } Child.out int main(int argc,char args[]) { int i = 0 for (i = 0; i< argc, ++i) printf(“child.out,%s”,args[i]); } return 0; --------------------------------------------------------------------- MyPopen(const char *command,const char *type) //自己实现popen. Parent.out #include"h.h" #include //步骤1创建管道,2复制进程,3改管道的为stdio,4加载进程,要返回文件指针FILE * MyPopen(const char *command,const char *type) { int fdPipe[2]; pipe(fdPipe); FILE *fp; if (fork() == 0) { //子进程完全复制父进程的管道信息. close(0); //关闭子进程端管道的标准输入端 dup(fdPipe[0]); //改管道的读端为标准输入端,即stdin现在为管 道的读端. 相当于dup2(fdPipe[0]),0) close(fdPipe[1]); //关于子进程的管道写入端 //注:加载进程后,文件打开表是可以存下来的. execlp(command,command,NULL); //加载进程至子进程.子进程全面由child.out接管}else{ close(fdPipe[0]); //关闭读端 fp = fdopen(fdPipe[1],type); //把写端返回成文件指针.注type需要“wb. } return fp; } int main() { FILE *pfp = MyPopen("./child.out","wb"); fputs("aaa",pfp); fputs("bbb",pfp); fclose(pfp); return 0; } Child.out #include"h.h" int main(int argc,const char args[]) { char buf[100]; fgets(buf,sizeof(buf),stdin);//stdin也即是fdPipe[0],但不可以直接 用这个变量.因为子进程中加载进程后,不存在这个了.不能直接使用0(为什么?) printf("child.out,szbuf:%s",buf); } 2010年9月22日下午 内容:讲另外两种进程间通信方式 第二种进程间通信方式(命名管道,有名管道) 建立永久的管道,命名管道,用于进程间数据共享. 重要命令mkfifo myPipe #创立在磁盘上的管道. 第三种进程间通信方式(共享内存) 共享内存段 重要函数,mmap,munmap dd if=/dev/zero of=./data.bin bs=1024 count =1 #生成一个1kb的文件,内容全是0. a.out与 b.out通过data.bin共享数据. a.out int main(int argc, char *argv[]) { sleep(3); char *pchData; int fd = open("./data.bin",O_RDWR); pchData = (char *)mmap(NULL,1024,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); //strcpy(pchData,"abcd"); memset(pchData,'a',10); printf("pchData = %s\n",pchData); munmap(NULL,1024); close(fd); return 0; } b.out int main() { int fd = open("./data.bin",O_RDWR); //两者要对应。 char *pchData = ( char *)mmap(NULL,1024,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); while(*pchData ==0) sleep(1); printf("有人改了共享文件%s\n" , pchData); munmap(NULL,1024); close(fd); return 0; } 查错方法:可以到处加puts(”xxx”),语句.,这样的好处是,可以折半找错,不用通遍找. 2010年9月23日上午 第四种进程间通信方式(消息队列) 重要函数: int msgget(key_t key, int msgflg); //在进程中取得一个消息队列,若取同一消息队列, //则根据key_t key的值来指定. int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg); ssize_t msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg); 函数参数说明: int msqid:通过msgget得到的消息队列标识.即key_t key struct msgbuf *msgp:接收数据和发送数据,都必须有以下这样的结构体来装数据. struct msgbuf { long mtype; /* message type, must be > 0 */ char mtext[1]; /* message data */ }; //注:在api中char mtext[1]通常表示这个成员是可以扩充的. size_t msgsz, 数据包的大小,指承载数据的大小,即消息结构体大小减掉sizeof(long mtype) long msgtyp, 消息类型.发送接收中,可以通过这个区别接收消息. int msgflg,消息类型.一般置0表示根据消息类型按先后次序发送接收, IPC_NOWAIT, MSG_EXCEPT, MSG_NOERROR(read the first message on the queue with message type that differs from msgtyp.) 消息队列与管道的区别: 1\消息队列可以区别消息类型,通过long mtype标识. 2\管道使用数据流,流可以多次发送,一次接收,可以组合接收.消息队列则使用数据包,必须发一次接收一次,一一发送接收. 代码: a.out(send) #include"h.h" struct u_info{ char *data; }; struct msgbufa{ long mtype; struct u_info uifo; }; int main(int argc, char *argv[]) { int iRet; int iMsg = msgget(123,IPC_CREAT|0666); struct msgbufa package = {2,{"aaaaaaaaaa"}};//实例化结构体 iRet = msgsnd(iMsg,&package,sizeof(package)-4,0); if ( iRet == -1) perror("没有发送成功!"); // perror打印一些系统调用的错误信息printf("%d,%d",iMsg,iRet); return 0; } b.out(recive) #include"h.h" struct u_info{ char *data1; }data; struct msgbuff{ long mtype; struct u_info data; }; int main() { //取得一个消息队列 ssize_t ssize; struct msgbuff package; int iMsg = msgget(123,IPC_CREAT|0666); //从消息队列里接收数据 ssize = msgrcv(iMsg,&package,sizeof(package)-4,2,0); printf("接收到%d",ssize); printf("msgrcv=%s",package.data.data1); return 0; } 练习: 写一程序,不断增长数字写入a.txt,并且可以随时接收shell输入字符,保存到a.txt. int main(int argc, char *argv[]) { int i = 0; FILE *fp = fopen("./a.txt","ab"); if ( fork() == 0) //子进程 { while(1) { fprintf(fp,"%d\n",i); sleep(1); i++; fflush(fp); } fclose(fp); } else { //父进程 char buf[40]; while(1) { fgets(buf,40,stdin); fputs(buf,fp); fflush(fp); } fclose(fp); } return 0; //以上程序可以改为fd的方式写.功能一样. #include"h.h" //fd怎样写二进制?. int main(int argc, char *argv[]) { int i = 0; int fd = open("./a.txt",O_CREAT|O_RDWR,0666); if ( fork() == 0) //子进程 { char buf[40]; while(1) { sprintf(buf,"%d",i); strcat(buf,"\n\0"); write(fd,buf,strlen(buf)); sleep(1); i++; } close(fd); } else//父进程 { char buf[40];//= {'\0'}; while(1) { memset(buf,0,40); // read(0,buf,40); //strcat(buf,"\0"); //回车就是换行,所以,只要加\0就行了. write(fd,buf,strlen(buf)); } close(fd); } return 0; } //问题是写进去的怎么会是二进制的? 2010年9月23日下午 第一部分:并发处理,线程: 把上午的程序改成多线程处理. #include"h.h" //多线程,全局变量共享.一个改了,另一个也就改了. //多进程中,各改各的,互不干涉.但可以通过创建进程前的变量赋值实现数据初始值共享。 int i = 0; FILE *fp = fopen("./a.txt","ab"); void * mt(void *arg) //线程所执行的函数, //必须是这个格式(返回值void *,参数void *){ while(1){ fprintf(fp,"%d\n",i); sleep(1); i++; fflush(fp); } } int main(int argc, char *argv[]) { char buf[40]; //创建另一线程,执行生成连续数 pthread_t t1; int iRet = pthread_create(&t1,NULL,mt,NULL); if (iRet != 0 ) perror("创建线程失败!"); while(1) { fgets(buf,40,stdin); fputs(buf,fp); fflush(fp); } fclose(fp); return 0; } 第二部分:多线程的其他情况讲解: 重要函数: pthread_join:主线程等子线程执行完毕,清理完成资源,并返回值pthread_detach:分离子线程,让其自生自灭.自行清理自己的资源pthread_self() 得到本线程的id. 以及传结构体去子线程. 代码: #include"h.h" struct INFO{ int iId; char szName[8]; }; void * ThreadFun(void * arg) { /* struct INFO *pInfo = (struct INFO *)arg; printf("%d,%s\n",pInfo->iId,pInfo->szName); //加\n有区别,为什么? free(pInfo); //*/ printf("%d,%s",((struct INFO *)arg)->iId,((struct INFO *)arg)->szName); //这样也可以,但必须强制转换后再->用成员. //free(arg); //与下一句区别? 答案,一样的,free要求的就是传入void 型的指针。 free((struct INFO *)arg); return NULL; } pthread_t Test() { pthread_t t1; struct INFO *pInfo = (struct INFO *)malloc(sizeof (struct INFO)); pInfo->iId = 1; strcpy(pInfo->szName, "aaaa"); pthread_create(&t1,NULL,ThreadFun,pInfo); //sleep(2); return t1; } int main()//主线程 { pthread_t tid = Test(); //传结构体进去,有三种方式 //1,传栈指针,在主调函数中sleep(1),再让主调函数退栈,以防内存已经修改//2,把结构体定义在全局区,子线程直接存取。 //3,把结构体开在堆上,各线程共享堆上的内存。 pthread_join(tid,NULL);//合并线程 //等待子线程执行完毕,因为主线程结束,会导致整个进程结束,清除所有的资源,还没有执行完毕的线程则会直接被清除掉了。 //但是把多条执行线路合并,虽然可以把子线程执行完毕,所分资源全部清空。但容易把并发逻辑搞成串行逻辑。 //若不用pthread_join收回线程资源,则又会形成线程资源收不回。若原线程不需要从这些线程得到任何返回数据,并且让它们 //自动清除所分资源,则可以让他分离出去,独立 (唯一的作用就是自动清理资源) pthread_detach(tid); //主线程长期运行的情况,若子线程执行时间长的 话,则用pthread_join printf("pthread_self = %d",pthread_self());//打印线程的ID return 0; } 注:一般来说,不得已才用多进程与多线程,若可以用一个线程,一个进程解决的问题,则用一个线程,一个进程的方式效率会更高. 2010年9月24日上午 ] 重要函数 #include pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//定义一把锁,声明+初始化 相当于以下两条语句. pthread_mutex_t mutex;//声明 int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); //初始化restrict这个关键可以当它不存在.表示编译器不对它进行优化的变量. int pthread_mutex_lock(pthread_mutex_t *mutex); //等待互斥锁解开然后再锁住互斥量. int pthread_mutex_trylock(pthread_mutex_t *mutex);//不知道干什么用的? int pthread_mutex_unlock(pthread_mutex_t *mutex);//给互斥量解锁 int pthread_mutex_destroy(pthread_mutex_t *mutex);//销毁锁 代码: #include"h.h" int j = 0; //全局区里的变量,线程都可以访问. pthread_mutex_t mutex; //声明一个锁 void *ThreadFunc(void *arg) { //static int i = 0 ,j = 0 ; int i = 0;//j = 0; for ( i = 0; i<1000000; i++) pthread_mutex_lock(&mutex); //加锁 j++; //不加锁,则有可能同时写,所以数据有可能是随机的,乱的. pthread_mutex_unlock(&mutex); //解锁 printf("j:%d\n",j); return NULL; } int main() { pthread_t tid1,tid2; pthread_mutex_init(&mutex,NULL); //初始化锁 pthread_create(&tid1,NULL,ThreadFunc,NULL); pthread_create(&tid2,NULL,ThreadFunc,NULL); pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_mutex_destroy(&mutex); //销毁锁 printf("j=%d\n",j); return 0; } ----------------- 另一种示例,写文件---------------- pthread_mutex_t lock; FILE *fp=NULL; void *fun(void *arg) { char buf1[]="hello,my name is "; char buf2[]="hanjinwei"; pthread_mutex_lock(&lock); //不加此锁则写文件乱的. //内容无顺序,两线程抢着写. fputs(buf1,fp); sleep(1); fputs(buf2,fp); pthread_mutex_unlock(&lock); return NULL; } int main() { pthread_t tid1,tid2; fp=fopen("a.txt","wb"); pthread_mutex_init(&lock,NULL); pthread_create(&tid1,NULL,fun,NULL); pthread_create(&tid2,NULL,fun,NULL); printf("the new thread1 id is %u\n",tid1); printf("the new thread2 id is %u\n",tid2); pthread_join(tid1,NULL); pthread_join(tid2,NULL); fclose(fp); pthread_mutex_destroy(&lock); return 0; } 解决方法之二,信号量实现互斥锁: 重要函数 #include int sem_init(sem_t *sem, int pshared, unsigned value);//实始化信号量 int sem_trywait(sem_t *sem);//不知道干什么的? int sem_wait(sem_t *sem);//减1. If the semaphore value is currently zero, then the calling thread shall not return from the call to sem_wait() until it either locks the semaphore or the call is interrupted by a signal. 如果当前信号灯的值为0,则其他线程不可以进来保护的代码段执行,直到执行完毕后,由sem_post恢复(+1),让出位置,其他线程才可以进来执行.,进来一个线程减1. int sem_post(sem_t *sem); sem_post执行完一个线程,在这里+1,即信号量的值只是简单地增加.如果本函数执行成功,sem_post()会返回0,否则,会返回-1 代码: #include"h.h" #include int j = 0; //全局区里的变量,线程都可以访问. sem_t sem; //声明 void *ThreadFunc(void *arg) { //static int i = 0 ,j = 0 ; int i = 0;//j = 0; for ( i = 0; i<100000; i++) { sem_wait(&sem); // -1 此处=0时,则不充许有线程进来执行. j++; //通过设定实始化信号量为,只充许在同一时间一个线程进来,则有可能同时写,所以数据有可能是随机的,乱的. sem_post(&sem); // +1 } printf("j:%d\n",j); return NULL; } int main() { pthread_t tid1,tid2; pthread_create(&tid1,NULL,ThreadFunc,NULL); pthread_create(&tid2,NULL,ThreadFunc,NULL); sem_init(&sem,0,1); //初始化//改为则同时可以二个线程进去执行,池的概念,在不是修改数据的情况下.如查询 pthread_join(tid1,NULL); pthread_join(tid2,NULL); //销毁 printf("最后的j=%d\n",j); return 0; } 第二部分:线程清理 重要函数 int pthread_cancel(pthread_t thread);//直接杀死一个线程 立即被杀,引发的问题是不会自动清理资源,必须等到本进程结束后,才得以清除.如已经打开的fd,堆上的内存等.造成浪费. 还有另一种比较温和的方式,即通过设置标志stop = 0 输入kill即stop = 1即退出.while(! = stop) 代码如下: #include"h.h" int stop = 0; void *ThreadFunc(void *arg) { int i = 0; while(!stop) { FILE *fp=fopen("./a.txt","ab"); fprintf(fp,"%d\n",i++); fclose(fp); sleep(2); } return NULL; } int main() { pthread_t tid; char szBuf[32]; FILE *fp; pthread_create(&tid,NULL,ThreadFunc,NULL); while( fgets( szBuf,sizeof(szBuf),stdin) ) { if(strstr(szBuf,"kill"))