Linux程序设计入门Word格式.docx
《Linux程序设计入门Word格式.docx》由会员分享,可在线阅读,更多相关《Linux程序设计入门Word格式.docx(20页珍藏版)》请在冰豆网上搜索。
intsem_getvalue(sem_t*sem);
sem_init创建一个信号灯,并初始化其值为value.pshared决定了信号量能否在几个进程
间共享.由于目前Linux还没有实现进程间共享信号灯,所以这个值只能够取0.sem_dest
roy是用来删除信号灯的.sem_wait调用将阻塞进程,直到信号灯的值大于0.这个函数返回
的时候自动的将信号灯的值的件一.sem_post和sem_wait相反,是将信号灯的内容加一同
时发出信号唤醒等待的进程..sem_trywait和sem_wait相同,不过不阻塞的,当信号灯的值
为0的时候返回EAGAIN,表示以后重试.sem_getvalue得到信号灯的值.
由于Linux不支持,我们没有办法用源程序解释了.
这几个函数的使用相当简单的.比如我们有一个程序要向一个系统打印机打印两页.我们
首先创建一个信号灯,并使其初始值为1,表示我们有一个资源可用.然后一个进程调用se
m_wait由于这个时候信号灯的值为1,所以这个函数返回,打印机开始打印了,同时信号灯
的值为0了.如果第二个进程要打印,调用sem_wait时候,由于信号灯的值为0,资源不可
用,于是被阻塞了.当第一个进程打印完成以后,调用sem_post信号灯的值为1了,这个时候
系统通知第二个进程,于是第二个进程的sem_wait返回.第二个进程开始打印了.
不过我们可以使用线程来解决这个问题的.我们会在后面解释什么是线程的.编译包含上
面这几个函数的程序要加上-lrt选贤,以连接librt.so库
2。
SystemV信号量为了解决上面哪个问题,我们也可以使用SystemV信号量.很幸运的
是Linux实现了SystemV信号量.这样我们就可以用实例来解释了.SystemV信号量的函
数主要有下面几个.
sys/types.h>
sys/ipc.h>
sys/sem.h>
key_tftok(char*pathname,charproj);
intsemget(key_tkey,intnsems,intsemflg);
intsemctl(intsemid,intsemnum,intcmd,unionsemunarg);
intsemop(intsemid,structsembuf*spos,intnspos);
structsembuf{
shortsem_num;
/*使用那一个信号*/
shortsem_op;
/*进行什么操作*/
shortsem_flg;
/*操作的标志*/
};
ftok函数是根据pathname和proj来创建一个关键字.semget创建一个信号量.成功时返回
信号的ID,key是一个关键字,可以是用ftok创建的也可以是IPC_PRIVATE表明由系统选用
一个关键字.nsems表明我们创建的信号个数.semflg是创建的权限标志,和我们创建一个
文件的标志相同.
semctl对信号量进行一系列的控制.semid是要操作的信号标志,semnum是信号的个数,cm
d是操作的命令.经常用的两个值是:
SETVAL(设置信号量的值)和IPC_RMID(删除信号灯).
arg是一个给cmd的参数.
semop是对信号进行操作的函数.semid是信号标志,spos是一个操作数组表明要进行什么
操作,nspos表明数组的个数.如果sem_op大于0,那么操作将sem_op加入到信号量的值中
并唤醒等待信号增加的进程.如果为0,当信号量的值是0的时候,函数返回,否则阻塞直
到信号量的值为0.如果小于0,函数判断信号量的值加上这个负值.如果结果为0唤醒等待
信号量为0的进程,如果小与0函数阻塞.如果大于0,那么从信号量里面减去这个值并返回
..
下面我们一以一个实例来说明这几个函数的使用方法.这个程序用标准错误输出来代替我
们用的打印机.
stdio.h>
unistd.h>
limits.h>
errno.h>
string.h>
stdlib.h>
sys/stat.h>
sys/wait.h>
#definePERMSS_IRUSR|S_IWUSR
voidinit_semaphore_struct(structsembuf*sem,intsemnum,
intsemop,intsemflg)
{
/*初始话信号灯结构*/
sem->
sem_num=semnum;
sem_op=semop;
sem_flg=semflg;
}
intdel_semaphore(intsemid)
/*信号灯并不随程序的结束而被删除,如果我们没删除的话(将1改为0)
可以用ipcs命令查看到信号灯,用ipcrm可以删除信号灯的
*/
#if1
returnsemctl(semid,0,IPC_RMID);
#endif
intmain(intargc,char**argv)
charbuffer[MAX_CANON],*c;
inti,n;
intsemid,semop_ret,status;
pid_tchildpid;
structsembufsemwait,semsignal;
if((argc!
=2)||((n=atoi(argv[1]))<
1))
fprintf(stderr,"
Usage:
%snumber\n\a"
argv[0]);
exit
(1);
/*使用IPC_PRIVATE表示由系统选择一个关键字来创建*/
/*创建以后信号灯的初始值为0*/
if((semid=semget(IPC_PRIVATE,1,PERMS))==-1)
[%d]:
AcessSemaphoreError:
%s\n\a"
getpid(),strerror(errno));
/*semwait是要求资源的操作(-1)*/
init_semaphore_struct(&
semwait,0,-1,0);
/*semsignal是释放资源的操作(+1)*/
semsignal,0,1,0);
/*开始的时候有一个系统资源(一个标准错误输出)*/
if(semop(semid,&
semsignal,1)==-1)
IncrementSemaphoreError:
if(del_semaphore(semid)==-1)
DestroySemaphoreError:
/*创建一个进程链*/
for(i=0;
i<
n;
i++)
if(childpid=fork())break;
sprintf(buffer,"
[i=%d]-->
[Process=%d]-->
[Parent=%d]-->
[Child=%d]\n"
i,getpid(),getppid(),childpid);
c=buffer;
/*这里要求资源,进入原子操作*/
while(((semop_ret=semop(semid,&
semwait,1))==-1)&
&
(errno==EINTR));
if(semop_ret==-1)
DecrementSemaphoreError:
else
while(*c!
='
'
)fputc(*c++,stderr);
/*原子操作完成,赶快释放资源*/
semsignal,1))==-1)&
/*不能够在其他进程反问信号灯的时候,我们删除了信号灯*/
while((wait(&
status)==-1)&
/*信号灯只能够被删除一次的*/
if(i==1)
exit(0);
信号灯的主要用途是保护临界资源(在一个时刻只被一个进程所拥有).
3。
SystemV消息队列为了便于进程之间通信,我们可以使用管道通信SystemV也提供了
一些函数来实现进程的通信.这就是消息队列.
sys/msg.h>
intmsgget(key_tkey,intmsgflg);
intmsgsnd(intmsgid,structmsgbuf*msgp,intmsgsz,intmsgflg);
intmsgrcv(intmsgid,structmsgbuf*msgp,intmsgsz,
longmsgtype,intmsgflg);
intmsgctl(Intmsgid,intcmd,structmsqid_ds*buf);
structmsgbuf{
longmsgtype;
/*消息类型*/
......./*其他数据类型*/
msgget函数和semget一样,返回一个消息队列的标志.msgctl和semctl是对消息进行控制
..msgsnd和msgrcv函数是用来进行消息通讯的.msgid是接受或者发送的消息队列标志.
msgp是接受或者发送的内容.msgsz是消息的大小.结构msgbuf包含的内容是至少有一个
为msgtype.其他的成分是用户定义的.对于发送函数msgflg指出缓冲区用完时候的操作.
接受函数指出无消息时候的处理.一般为0.接收函数msgtype指出接收消息时候的操作.
如果msgtype=0,接收消息队列的第一个消息.大于0接收队列中消息类型等于这个值的第
一个消息.小于0接收消息队列中小于或者等于msgtype绝对值的所有消息中的最小一个消
息.我们以一个实例来解释进程通信.下面这个程序有server和client组成.先运行服务
端后运行客户端.
服务端server.c
#defineMSG_FILE"
server.c"
#defineBUFFER255
#definePERMS_IRUSR|S_IWUSR
structmsgtype{
longmtype;
charbuffer[BUFFER+1];
intmain()
structmsgtypemsg;
key_tkey;
intmsgid;
if((key=ftok(MSG_FILE,'
a'
))==-1)
CreatKeyError:
%s\a\n"
strerror(errno));
if((msgid=msgget(key,PERM|IPC_CREAT|IPC_EXCL))==-1)
CreatMessageError:
while
(1)
msgrcv(msgid,&
msg,sizeof(structmsgtype),1,0);
ServerReceive:
%s\n"
msg.buffer);
msg.mtype=2;
msgsnd(msgid,&
msg,sizeof(structmsgtype),0);
----------------------------------------------------------------------------
----
客户端(client.c)
if(argc!
=2)
%sstring\n\a"
if((msgid=msgget(key,PERM))==-1)
msg.mtype=1;
strncpy(msg.buffer,argv[1],BUFFER);
memset(&
msg,'
sizeof(structmsgtype));
msg,sizeof(structmsgtype),2,0);
Clientreceive:
注意服务端创建的消息队列最后没有删除,我们要使用ipcrm命令来删除的.
4。
SystemV共享内存还有一个进程通信的方法是使用共享内存.SystemV提供了以下几个
函数以实现共享内存.
sys/shm.h>
intshmget(key_tkey,intsize,intshmflg);
void*shmat(intshmid,constvoid*shmaddr,intshmflg);
intshmdt(constvoid*shmaddr);
intshmctl(intshmid,intcmd,structshmid_ds*buf);
shmget和shmctl没有什么好解释的.size是共享内存的大小.shmat是用来连接共享内存
的.shmdt是用来断开共享内存的.不要被共享内存词语吓倒,共享内存其实很容易实现和
使用的.shmaddr,shmflg我们只要用0代替就可以了.在使用一个共享内存之前我们调用s
hmat得到共享内存的开始地址,使用结束以后我们使用shmdt断开这个内存.
intshmid;
char*p_addr,*c_addr;
if((shmid=shmget(IPC_PRIVATE,1024,PERM))==-1)
CreateShareMemoryError:
if(fork())
p_addr=shmat(shmid,0,0);
memset(p_addr,'
1024);
strncpy(p_addr,argv[1],1024);
c_addr=shmat(shmid,0,0);
printf("
Clientget%s"
c_addr);
这个程序是父进程将参数写入到共享内存,然后子进程把内容读出来.最后我们要使用ip
crm释放资源的.先用ipcs找出ID然后用ipcrmshmID删除.
后记:
进程通信(IPC)是网络程序的基础,在很多的网络程序当中会大量的使用进程通信的概念
和知识.其实进程通信是一件非常复杂的事情,我在这里只是简单的介绍了一下.如果你想
学习进程通信的详细知识,最好的办法是自己不断的写程序和看联机手册.现在网络上有
了很多的知识可以去参考.可惜我看到的很多都是英文编写的.如果你找到了有中文的版
本请尽快告诉我.谢谢!
7)Linux程序设计入门--线程操作
Linux下线程的创建
介绍在Linux下线程的创建和基本的使用.Linux下的线程是一个非常复杂的问题,由
于我对线程的学习不时很好,我在这里只是简单的介绍线程的创建和基本的使用,关于线
程的高级使用(如线程的属性,线程的互斥,线程的同步等等问题)可以参考我后面给出的
资料.现在关于线程的资料在网络上可以找到许多英文资料,后面我罗列了许多链接,对
线程的高级属性感兴趣的话可以参考一下.等到我对线程的了解比较深刻的时候,我回来
完成这篇文章.如果您对线程了解的详尽我也非常高兴能够由您来完善.
先介绍什么是线程.我们编写的程序大多数可以看成是单线程的.就是程序是按照一定的
顺序来执行.如果我们使用线程的话,程序就会在我们创建线成的地方分叉,变成两个"
程
序"
在执行.粗略的看来好象和子进程差不多的,其实不然.子进程是通过拷贝父进程的地
址空间来执行的.而线程是通过共享程序代码来执行的,讲的通俗一点就是线程的相同的
代码会被执行几次.使用线程的好处是可以节省资源,由于线程是通过共享代码的,所以没
有进程调度那么复杂.
线程的创建和使用
线程的创建是用下面的几个函数来实现的.
pthread.h>
intpthread_create(pthread_t*thread,pthread_attr_t*attr,
void*(*start_