进程和线程编程

多进程和多线程编程 1、多进程编程 #include #include #include int main() { pid_t child_pid; /* 创建一个子进程 */ child_pid = fork(); if(child_pid == 0) { printf("child pid\n"); exit(0); } else { printf("father pid\ 阅读详情

进程和线程编程

目 录

  1. 进程和线程编程
    1. 原始管道
      1. pipe()
      2. dup()
      3. dup2()
      4. popen()和pclose()
    2. 命名管道
      1. 创建FIFO
      2. 操作FIFO
      3. 阻塞FIFO
    3. 消息队列
      1. msgget()
      2. msgsnd()
      3. msgrcv()
      4. msgctl()
    4. 信号量
      1. semget()
      2. semop()
      3. semctl()
    5. 共享内存
      1. shmget()
      2. shmat()
      3. shmctl()
      4. shmdt()
    6. 线程
      1. 线程同步
      2. 使用信号量协调程序
      3. 代码例子
        1. newthread
        2. exitthead
        3. getchannel
        4. def
        5. release
        6. redezvous
        7. unbouded


进程和线程编程


    看一下UNIX系统中的进程和Mach的任务和线程之间的关系。在UNIX系统中,一个进程包括一个可执行的程序和一系列的资源,例如文件描述符表和地址 空间。在Mach中,一个任务仅包括一系列的资源;线程处理所有的可执行代码。一个Mach的任务可以有任意数目的线程和它相关,同时每个线程必须和某个 任务相关。和某一个给定的任务相关的所有线程都共享任务的资源。这样,一个线程就是一个程序计数器、一个堆栈和一系列的寄存器。所有需要使用的数据结构都 属于任务。一个UNIX系统中的进程在Mach中对应于一个任务和一个单独的线程。

[目录]



原始管道


    使用C语言创建管道要比在shell下使用管道复杂一些。如果要使用C语言创建一个简单的管道,可以使用系统调用pipe()。它接受一个参数,也就是一 个包括两个整数的数组。如果系统调用成功,此数组将包括管道使用的两个文件描述符。创建一个管道之后,一般情况下进程将产生一个新的进程。

    可以通过打开两个管道来创建一个双向的管道。但需要在子进程中正确地设置文件描述必须在系统调用fork()中调用pipe(),否则子进程将不会继承文 件描述符。当使用半双工管道时,任何关联的进程都必须共享一个相关的祖先进程。因为管道存在于系统内核之中,所以任何不在创建管道的进程的祖先进程之中的 进程都将无法寻址它。而在命名管道中却不是这样。

[目录]



pipe()


系统调用:pipe();
原型:intpipe(intfd[2]);
返回值:如果系统调用成功,返回0
如果系统调用失败返回-1:errno=EMFILE(没有空闲的文件描述符)
EMFILE(系统文件表已满)
EFAULT(fd数组无效)
注意fd[0]用于读取管道,fd[1]用于写入管道。
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
main()
{
intfd[2];
pipe(fd);
..
}
一旦创建了管道,我们就可以创建一个新的子进程:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
main()
{
intfd[2];
pid_t childpid;
pipe(fd);
if((childpid=fork())==-1)
{
perror("fork");
exit(1);
}..
}

    如果父进程希望从子进程中读取数据,那么它应该关闭fd1,同时子进程关闭fd0。反之,如果父进程希望向子进程中发送数据,那么它应该关闭fd0,同时 子进程关闭fd1。因为文件描述符是在父进程和子进程之间共享,所以我们要及时地关闭不需要的管道的那一端。单从技术的角度来说,如果管道的一端没有正确 地关闭的话,你将无法得到一个EOF。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
main()
{
intfd[2];
pid_t childpid;
pipe(fd);
if((childpid=fork())==-1)
{
perror("fork");
exit(1);
}
if(childpid==0)
{
/*Child process closes up in put side of pipe*/
close(fd[0]);
}
else
{
/*Parent process closes up out put side of pipe*/
close(fd[1]);
}..
}

    正如前面提到的,一但创建了管道之后,管道所使用的文件描述符就和正常文件的文件描述符一样了。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
intmain(void)
{
intfd[2],nbytes;
pid_tchildpid;
charstring[]="Hello,world!/n";
charreadbuffer[80];
pipe(fd);
if((childpid=fork())==-1)
{
perror("fork");
exit(1);
}
if(childpid==0)
{
/*Child process closes up in put side of pipe*/
close(fd[0]);
/*Send"string"through the out put side of pipe*/
write(fd[1],string,strlen(string));
exit(0);
}
else
{
/*Parent process closes up out put side of pipe*/
close(fd[1]);
/*Readinastringfromthepipe*/
nbytes=read(fd[0],readbuffer,sizeof(readbuffer));
printf("Receivedstring:%s",readbuffer);
}
return(0);
}

    一般情况下,子进程中的文件描述符将会复制到标准的输入和输出中。这样子进程可以使用exec()执行另一个程序,此程序继承了标准的数据流。

 

 

 

[目录]



dup()


系统调用:dup();
原型:intdup(intoldfd);
返回:如果系统调用成功,返回新的文件描述符
如果系统调用失败,返回-1:errno=EBADF(oldfd不是有效的文件描述符)
EBADF(newfd超出范围)
EMFILE(进程的文件描述符太多)

    注意旧文件描述符oldfd没有关闭。虽然旧文件描述符和新创建的文件描述符可以交换使用,但一般情况下需要首先关闭一个。系统调用dup()使用的是号码最小的空闲的文件描述符。

再看下面的程序:
..
childpid=fork();
if(childpid==0)
{
/*Close up standard input of the child*/
close(0);
/*Dup licate the input side of pipe to stdin*/
dup(fd[0]);
execlp("sort","sort",NULL);
.
}
    因为文件描述符0(stdin)被关闭,所以dup()把管道的输入描述符复制到它的标准输入中。这样我们可以调用execlp(),使用sort程序覆 盖子进程的正文段。因为新创建的程序从它的父进程中继承了标准输入/输出流,所以它实际上继承了管道的输入端作为它的标准输入端。现在,最初的父进程送往 管道的任何数据都将会直接送往sort函数。

 

 

 

[目录]



dup2()


系统调用:dup2();
原型:intdup2(intoldfd,intnewfd);
返回值:如果调用成功,返回新的文件描述符
如果调用失败,返回-1:errno=EBADF(oldfd不是有效的文件描述符)
EBADF(newfd超出范围)
EMFILE(进程的文件描述符太多)
注意dup2()将关闭旧文件描述符。

    使用此系统调用,可以将close操作和文件描述符复制操作集成到一个系统调用中。另外,此系统调用保证了操作的自动进行,也就是说操作不能被其他的信号 中断。这个操作将会在返回系统内核之前完成。如果使用前一个系统调用dup(),程序员不得不在此之前执行一个close()操作。请看下面的程序:
..

childpid=fork();
if(childpid==0)
{
/*Close stdin,dup licate the input side of pipe to stdin*/
dup2(0,fd[0]);
execlp("sort","sort",NULL);
..
}

 

 

 

[目录]



popen()和pclose()


如果你认为上面创建和使用管道的方法过于繁琐的话,你也可以使用下面的简单的方法:

库函数:popen()和pclose();
原型:FILE*popen(char*command,char*type);
返回值:如果成功,返回一个新的文件流。
如果无法创建进程或者管道,返回NULL。
    此标准的库函数通过在系统内部调用pipe()来创建一个半双工的管道,然后它创建一个子进程,启动shell,最后在shell上执行command参 数中的命令。管道中数据流的方向是由第二个参数type控制的。此参数可以是r或者w,分别代表读或写。但不能同时为读和写。在Linux系统下,管道将 会以参数type中第一个字符代表的方式打开。所以,如果你在参数type中写入rw,管道将会以读的方式打开。

    虽然此库函数的用法很简单,但也有一些不利的地方。例如它失去了使用系统调用pipe()时可以有的对系统的控制。尽管这样,因为可以直接地使用shell命令,所以shell中的一些通配符和其他的一些扩展符号都可以在command参数中使用。
使用popen()创建的管道必须使用pclose()关闭。其实,popen/pclose和标准文件输入/输出流中的fopen()/fclose()十分相似。


库函数:pclose();
原型:intpclose(FILE*stream);
返回值:返回系统调用wait4()的状态。
如果stream无效,或者系统调用wait4()失败,则返回-1。
    注意此库函数等待管道进程运行结束,然后关闭文件流。库函数pclose()在使用popen()创建的进程上执行wait4()函数。当它返回时,它将破坏管道和文件系统。
    在下面的例子中,用sort命令打开了一个管道,然后对一个字符数组排序:

#include<stdio.h>
#defineMAXSTRS5
intmain(void)
{
intcntr;
FILE*pipe_fp;
char*strings[MAXSTRS]={"echo","bravo","alpha",
"charlie","delta"};
/*Createonewaypipelinewithcalltopopen()*/
if((pipe_fp=popen("sort","w"))==NULL)
{
perror("popen");
exit(1);
}
/*Processingloop*/
for(cntr=0;cntr<MAXSTRS;cntr++){
fputs(strings[cntr],pipe_fp);
fputc('/n',pipe_fp);
}
/*Closethepipe*/
pclose(pipe_fp);
return(0);
}
因为popen()使用shell执行命令,所以所有的shell扩展符和通配符都可以使用。此外,它还可以和popen()一起使用重定向和输出管道函数。再看下面的例子:
popen("ls~scottb","r");
popen("sort>/tmp/foo","w");
popen("sort|uniq|more","w");
下面的程序是另一个使用popen()的例子,它打开两个管道(一个用于ls命令,另一个用于
sort命令):
#include<stdio.h>
intmain(void)
{
FILE*pipein_fp,*pipeout_fp;
charreadbuf[80];
/*Createonewaypipelinewithcalltopopen()*/
if((pipein_fp=popen("ls","r"))==NULL)
{
perror("popen");
exit(1);
}
/*Createonewaypipelinewithcalltopopen()*/
if((pipeout_fp=popen("sort","w"))==NULL)
{
perror("popen");
exit(1);
}
/*Processingloop*/
while(fgets(readbuf,80,pipein_fp))
fputs(readbuf,pipeout_fp);
/*Closethepipes*/
pclose(pipein_fp);
pclose(pipeout_fp);
return(0);
}
最后,我们再看一个使用popen()的例子。此程序用于创建一个命令和文件之间的管道:
#include<stdio.h>
intmain(intargc,char*argv[])
{
FILE*pipe_fp,*infile;
charreadbuf[80];
if(argc!=3){
fprintf(stderr,"USAGE:popen3[command][filename]/n");
exit(1);
}
/*Open up input file*/
if((infile=fopen(argv[2],"rt"))==NULL)
{
perror("fopen");
exit(1);
}
/*Create one way pipe line with call topopen()*/
if((pipe_fp=popen(argv[1],"w"))==NULL)
{
perror("popen");
exit(1);
}
/*Processingloop*/
do{
fgets(readbuf,80,infile);
if(feof(infile))break;
fputs(readbuf,pipe_fp);
}while(!feof(infile));
fclose(infile);
pclose(pipe_fp);
return(0);
}
下面是使用此程序的例子:
popen3sortpopen3.c
popen3catpopen3.c
popen3morepopen3.c
popen3catpopen3.c|grepmain

 

 

 

[目录]



命名管道


    命名管道和一般的管道基本相同,但也有一些显著的不同:

*命名管道是在文件系统中作为一个特殊的设备文件而存在的。
*不同祖先的进程之间可以通过管道共享数据。
*当共享管道的进程执行完所有的I/O操作以后,命名管道将继续保存在文件系统中以便以后使用。

    一个管道必须既有读取进程,也要有写入进程。如果一个进程试图写入到一个没有读取进程的管道中,那么系统内核将会产生SIGPIPE信号。当两个以上的进程同时使用管道时,这一点尤其重要。

 

[目录]



创建FIFO


    可以有几种方法创建一个命名管道。头两种方法可以使用shell。

mknodMYFIFOp
mkfifoa=rwMYFIFO
    上面的两个命名执行同样的操作,但其中有一点不同。命令mkfifo提供一个在创建之后直接改变FIFO文件存取权限的途径,而命令mknod需要调用命令chmod。
    一个物理文件系统可以通过p指示器十分容易地分辨出一个FIFO文件。

$ls-lMYFIFO
prw-r--r--1rootroot0Dec1422:15MYFIFO|

    请注意在文件名后面的管道符号“|”。
    我们可以使用系统调用mknod()来创建一个FIFO管道:

库函数:mknod();
原型:intmknod(char*pathname,mode_tmode,dev_tdev);
返回值:如果成功,返回0
如果失败,返回-1:errno=EFAULT(无效路径名)
EACCES(无存取权限)
ENAMETOOLONG(路径名太长)
ENOENT(无效路径名)
ENOTDIR(无效路径名)

    下面看一个使用C语言创建FIFO管道的例子:

mknod("/tmp/MYFIFO",S_IFIFO|0666,0);

    在这个例子中,文件/tmp/MYFIFO是要创建的FIFO文件。它的存取权限是0666。存取权限
也可以使用umask修改:

final_umask=requested_permissions&~original_umask

    一个常用的使用系统调用umask()的方法就是临时地清除umask的值:
umask(0);
mknod("/tmp/MYFIFO",S_IFIFO|0666,0);

    另外,mknod()中的第三个参数只有在创建一个设备文件时才能用到。它包括设备文件的
主设备号和从设备号。
}
}

 

 

 

[目录]



操作FIFO


    FIFO上的I/O操作和正常管道上的I/O操作基本一样,只有一个主要的不同。系统调用open用来在物理上打开一个管道。在半双工的管道中,这是不必 要的。因为管道在系统内核中,而不是在一个物理的文件系统中。在我们的例子中,我们将像使用一个文件流一样使用管道,也就是使用fopen()打开管道, 使用fclose()关闭它。
    请看下面的简单的服务程序进程:
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<unistd.h>
#include<linux/stat.h>
#defineFIFO_FILE"MYFIFO"
intmain(void)
{
FILE*fp;
charreadbuf[80];
/*CreatetheFIFOifitdoesnotexist*/
umask(0);
mknod(FIFO_FILE,S_IFIFO|0666,0);
while(1)
{
fp=fopen(FIFO_FILE,"r");
fgets(readbuf,80,fp);
printf("Receivedstring:%s/n",readbuf);
fclose(fp);
return(0);
    因为FIFO管道缺省时有阻塞的函数,所以你可以在后台运行此程序:
$fifoserver&
    再来看一下下面的简单的客户端程序:
#include<stdio.h>
#include<stdlib.h>
#defineFIFO_FILE"MYFIFO"
intmain(int argc,char* argv[])
{
FILE*fp;
if(argc!=2){
printf("USAGE:fifoclient[string]/n");
exit(1);
}
if((fp=fopen(FIFO_FILE,"w"))==NULL){
perror("fopen");
exit(1);
}
fputs(argv[1],fp);
fclose(fp);
return(0);
}

 

 

[目录]



阻塞FIFO


    一般情况下,FIFO管道上将会有阻塞的情况发生。也就是说,如果一个FIFO管道打开供读取的话,它将一直阻塞,直到其他的进程打开管道写入信息。这种 过程反过来也一样。如果你不需要阻塞函数的话,你可以在系统调用open()中设置O_NONBLOCK标志,这样可以取消缺省的阻塞函数。

 

 

 

[目录]



消息队列


    在UNIX的SystemV版本,AT&T引进了三种新形式的IPC功能(消息队列、信号量、以及共享内存)。但BSD版本的UNIX使用套接口作为主要的IPC形式。Linux系统同时支持这两个版本。

[目录]



msgget()


系统调用msgget()

    如果希望创建一个新的消息队列,或者希望存取一个已经存在的消息队列,你可以使用系统调用msgget()。

系统调用:msgget();
原型:intmsgget(key_t key,int msgflg);
返回值:如果成功,返回消息队列标识符
如果失败,则返回-1:errno=EACCESS(权限不允许)
EEXIST(队列已经存在,无法创建)
EIDRM(队列标志为删除)
ENOENT(队列不存在)
ENOMEM(创建队列时内存不够)
ENOSPC(超出最大队列限制)

    系统调用msgget()中的第一个参数是关键字值(通常是由ftok()返回的)。然后此关键字值将会和其他已经存在于系统内核中的关键字值比较。这时,打开和存取操作是和参数msgflg中的内容相关的。
IPC_CREAT如果内核中没有此队列,则创建它。
IPC_EXCL当和IPC_CREAT一起使用时,如果队列已经存在,则失败。

    如果单独使用IPC_CREAT,则msgget()要么返回一个新创建的消息队列的标识符,要么返回具有相同关键字值的队列的标识符。如果 IPC_EXCL和IPC_CREAT一起使用,则msgget()要么创建一个新的消息队列,要么如果队列已经存在则返回一个失败值-1。 IPC_EXCL单独使用是没有用处的。
下面看一个打开和创建一个消息队列的例子:
intopen_queue(key_t keyval)
{
intqid;
if((qid=msgget(keyval,IPC_CREAT|0660))==-1)
{
return(-1);
}
return(qid);
}

 

 

 

[目录]



msgsnd()


系统调用msgsnd()

    一旦我们得到了队列标识符,我们就可以在队列上执行我们希望的操作了。如果想要往队列中发送一条消息,你可以使用系统调用msgsnd():

系统调用:msgsnd();
原型:intmsgsnd(int msqid,struct msgbuf*msgp,int msgsz,int msgflg);
返回值:如果成功,0。
如果失败,-1:errno=EAGAIN(队列已满,并且使用了IPC_NOWAIT)
EACCES(没有写的权限)
EFAULT(msgp地址无效)
EIDRM(消息队列已经删除)
EINTR(当等待写操作时,收到一个信号)
EINVAL(无效的消息队列标识符,非正数的消息类型,或
者无效的消息长度)
ENOMEM(没有足够的内存复制消息缓冲区)

    系统调用msgsnd()的第一个参数是消息队列标识符,它是由系统调用msgget返回的。第二个参数是msgp,是指向消息缓冲区的指针。参数msgsz中包含的是消息的字节大小,但不包括消息类型的长度(4个字节)。
    参数msgflg可以设置为0(此时为忽略此参数),或者使用IPC_NOWAIT。

    如果消息队列已满,那么此消息则不会写入到消息队列中,控制将返回到调用进程中。如果没有指明,调用进程将会挂起,直到消息可以写入到队列中。
    下面是一个发送消息的程序:

intsend_message(int qid,struct mymsgbuf *qbuf)
{
intresult,length;
/*The length is essentially the size of the structure minus sizeof(mtype)*/
length=sizeof(structmymsgbuf)-sizeof(long);
if((result=msgsnd(qid,qbuf,length,0))==-1)
{
return(-1);
}
return(result);
}

    这个小程序试图将存储在缓冲区qbuf中的消息发送到消息队列qid中。下面的程序是结合了上面两个程序的一个完整程序:

#include<stdio.h>
#include<stdlib.h>
#include<linux/ipc.h>
#include<linux/msg.h>
main()
{
intqid;
key_t msgkey;
struct mymsgbuf{
longmtype;/*Message type*/
intrequest;/*Work request number*/
doublesalary;/*Employee's salary*/
}msg;
/*Generateour IPC key value*/
msgkey=ftok(".",'m');
/*Open/createthequeue*/
if((qid=open_queue(msgkey))==-1){
perror("open_queue");
exit(1);
}
/*Load up the message with a r bitrary test data*/
msg.mtype=1;/*Messagetypemustbeapositivenumber!*/
msg.request=1;/*Dataelement#1*/
msg.salary=1000.00;/*Data element #2(my yearly salary!)*/
/*Bombsaway!*/
if((send_message(qid,&msg))==-1){
perror("send_message");
exit(1);
}
}
    在创建和打开消息队列以后,我们将测试数据装入到消息缓冲区中。最后调用send_messag把消息发送到消息队列中。现在在消息队列中有了一条消息, 我们可以使用ipcs命令来查看队列的状态。下面讨论如何从队列中获取消息。可以使用系统调用msgrcv():

 

 

[目录]



msgrcv()


系统调用:msgrcv();
原型:intmsgrcv(intmsqid,structmsgbuf*msgp,intmsgsz,longmtype,intmsgflg);
返回值:如果成功,则返回复制到消息缓冲区的字节数。
如果失败,则返回-1:errno=E2BIG(消息的长度大于msgsz,没有MSG_NOERROR)
EACCES(没有读的权限)
EFAULT(msgp指向的地址是无效的)
EIDRM(队列已经被删除)
EINTR(被信号中断)
EINVAL(msgqid无效,或者msgsz小于0)
ENOMSG(使用IPC_NOWAIT,同时队列中的消息无法满足要求)

    很明显,第一个参数用来指定将要读取消息的队列。第二个参数代表要存储消息的消息缓冲区的地址。第三个参数是消息缓冲区的长度,不包括mtype的长度,它可以按照如下的方法计算:
        msgsz=sizeof(structmymsgbuf)-sizeof(long);
    第四个参数是要从消息队列中读取的消息的类型。如果此参数的值为0,那么队列中最长时间的一条消息将返回,而不论其类型是什么。
如果调用中使用了IPC_NOWAIT作为标志,那么当没有数据可以使用时,调用将把ENOMSG返回到调用进程中。否则,调用进程将会挂起,直到队列中 的一条消息满足msgrcv()的参数要求。如果当客户端等待一条消息的时候队列为空,将会返回EIDRM。如果进程在等待消息的过程中捕捉到一个信号, 则返回EINTR。
    下面就是一个从队列中读取消息的程序:

intread_message(int qid,long type,struct mymsgbuf*qbuf)
{
intresult,length;
/*The length is essentially the size of the structure minus sizeof(mtype)*/
length=sizeof(structmymsgbuf)-sizeof(long);
if((result=msgrcv(qid,qbuf,length,type,0))==-1)
{
return(-1);
}
return(result);
}
    在成功地读取了一条消息以后,队列中的这条消息的入口将被删除。
    参数msgflg中的MSG_NOERROR位提供一种额外的用途。如果消息的实际长度大于msgsz,同时使用了MSG_NOERROR,那么消息将会 被截断,只有与msgsz长度相等的消息返回。一般情况下,系统调用msgrcv()会返回-1,而这条消息将会继续保存在队列中。我们可以利用这个特点 编制一个程序,利用这个程序可以查看消息队列的情况,看看符合我们条件的消息是否已经到来:

intpeek_message(int qid,long type)
{
intresult,length;
if((result=msgrcv(qid,NULL,0,type,IPC_NOWAIT))==-1)
{
if(errno==E2BIG)
return(TRUE);
}
return(FALSE);
}
    在上面的程序中,我们忽略了缓冲区的地址和长度。这样,系统调用将会失败。尽管如此,我们可以检查返回的E2BIG值,它说明符合条件的消息确实存在。

 

 

[目录]



msgctl()


系统调用msgctl()

    下面我们继续讨论如何使用一个给定的消息队列的内部数据结构。我们可以使用系统调用msgctl ( )来控制对消息队列的操作。

系统调用: msgctl( ) ;
调用原型: int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
返回值: 0 ,如果成功。
- 1,如果失败:errno = EACCES (没有读的权限同时cmd 是IPC_STAT )
EFAULT (buf 指向的地址无效)
EIDRM (在读取中队列被删除)
EINVAL (msgqid无效, 或者msgsz 小于0 )
EPERM (IPC_SET或者IPC_RMID 命令被使用,但调用程序没有写的权限)
下面我们看一下可以使用的几个命令:
IPC_STAT
读取消息队列的数据结构msqid_ds,并将其存储在b u f指定的地址中。
IPC_SET
设置消息队列的数据结构msqid_ds中的ipc_perm元素的值。这个值取自buf参数。
IPC_RMID
从系统内核中移走消息队列。
    我们在前面讨论过了消息队列的数据结构(msqid_ds)。系统内核中为系统中的每一个消息队列保存一个此数据结构的实例。通过使用IPC_STAT命令,我们可以得到一个此数据结构的副本。下面的程序就是实现此函数的过程:

int get_queue_ds( int qid, struct msgqid_ds *qbuf )
{
if( msgctl( qid, IPC_STAT, qbuf) == -1)
{
return(-1);
}
return(0);
}

    如果不能复制内部缓冲区,调用进程将返回-1。如果调用成功,则返回0。缓冲区中应该包括消息队列中的数据结构。
    消息队列中的数据结构中唯一可以改动的元素就是ipc_perm。它包括队列的存取权限和关于队列创建者和拥有者的信息。你可以改变用户的id、用户的组id以及消息队列的存取权限。
    下面是一个修改队列存取模式的程序:

int change_queue_mode(int qid, char *mode )
{
struct msqid_ds tmpbuf;
/* Retrieve a current copy of the internal data structure */
get_queue_ds( qid, &tmpbuf);
/* Change the permissions using an old trick */
sscanf(mode, "%ho", &tmpbuf.msg_perm.mode);
/* Update the internal data structure */
if( msgctl( qid, IPC_SET, &tmpbuf) == -1)
{
return(-1);
}
return(
}

    我们通过调用get_queue_ds来读取队列的内部数据结构。然后,我们调用sscanf( )修改数据结构msg_perm中的mode 成员的值。但直到调用msgctl()时,权限的改变才真正完成。在这里msgctl()使用的是IPC_SET命令。
    最后,我们使用系统调用msgctl ( )中的IPC_RMID命令删除消息队列:

int remove_queue(int qid )
{
if( msgctl( qid, IPC_RMID, 0) == -1)
{
return(-1);
}
return(0);
}
};

 

 

[目录]



信号量


    信号量是一个可以用来控制多个进程存取共享资源的计数器。它经常作为一种锁定机制来防止当一个进程正在存取共享资源时,另一个进程也存取同一资源。下面先简要地介绍一下信号量中涉及到的数据结构。

1.内核中的数据结构semid_ds
和消息队列一样,系统内核为内核地址空间中的每一个信号量集都保存了一个内部的数据结构。数据结构的原型是semid_ds。它是在linux/sem.h中做如下定义的:
/*One semid data structure for each set of semaphores in the system.*/
structsemid_ds{
structipc_permsem_perm;/*permissions..seeipc.h*/
time_tsem_otime;/*last semop time*/
time_tsem_ctime;/*last change time*/
structsem*sem_base;/*ptr to first semaphore in array*/
structwait_queue*eventn;
structwait_queue*eventz;
structsem_undo*undo;/*undo requestson this array*/
ushortsem_nsems;/*no. of semaphores in array*/
};
sem_perm是在linux/ipc.h定义的数据结构ipc_perm的一个实例。它保存有信号量集的存取权限的信息,以及信号量集创建者的有关信息。
sem_otime最后一次semop()操作的时间。
sem_ctime最后一次改动此数据结构的时间。
sem_base指向数组中第一个信号量的指针。
sem_undo数组中没有完成的请求的个数。
sem_nsems信号量集(数组)中的信号量的个数。

2.内核中的数据结构sem
在数据结构semid_ds中包含一个指向信号量数组的指针。此数组中的每一个元素都是一个
数据结构sem。它也是在linux/sem.h中定义的:
/*One semaphore structure for each semaphore in the system.*/
structsem{
shortsempid;/*pid of las toperation*/
ushortsemval;/*current value*/
ushortsemncnt;/*num procs awaiting increase in semval*/
ushortsemzcnt;/*num procs awaiting semval=0*/
};
sem_pid最后一个操作的PID(进程ID)。
sem_semval信号量的当前值。
sem_semncnt等待资源的进程数目。
sem_semzcnt等待资源完全空闲的进程数目。

 

[目录]



semget()


    我们可以使用系统调用semget()创建一个新的信号量集,或者存取一个已经存在的信号量集:

系统调用:semget();
原型:intsemget(key_t key,int nsems,int semflg);
返回值:如果成功,则返回信号量集的IPC标识符。如果失败,则返回-1:errno=EACCESS(没有权限)
EEXIST(信号量集已经存在,无法创建)
EIDRM(信号量集已经删除)
ENOENT(信号量集不存在,同时没有使用IPC_CREAT)
ENOMEM(没有足够的内存创建新的信号量集)
ENOSPC(超出限制)

    系统调用semget()的第一个参数是关键字值(一般是由系统调用ftok()返回的)。系统内核将此值和系统中存在的其他的信号量集的关键字值进行比 较。打开和存取操作与参数semflg中的内容相关。IPC_CREAT如果信号量集在系统内核中不存在,则创建信号量集。IPC_EXCL当和 IPC_CREAT一同使用时,如果信号量集已经存在,则调用失败。如果单独使用IPC_CREAT,则semget()要么返回新创建的信号量集的标识 符,要么返回系统中已经存在的同样的关键字值的信号量的标识符。如果IPC_EXCL和IPC_CREAT一同使用,则要么返回新创建的信号量集的标识 符,要么返回-1。IPC_EXCL单独使用没有意义。参数nsems指出了一个新的信号量集中应该创建的信号量的个数。信号量集中最多的信号量的个数是 在linux/sem.h中定义的:

#defineSEMMSL32/*<=512maxnumofsemaphoresperid*/
下面是一个打开和创建信号量集的程序:
intopen_semaphore_set(key_t keyval,int numsems)
{
intsid;
if(!numsems)
return(-1);
if((sid=semget(mykey,numsems,IPC_CREAT|0660))==-1)
{
return(-1);
}
return(sid);
}
};

 

 

 

[目录]



semop()


系统调用:semop();
调用原型:int semop(int semid,struct sembuf*sops,unsign ednsops);
返回值:0,如果成功。-1,如果失败:errno=E2BIG(nsops大于最大的ops数目)
EACCESS(权限不够)
EAGAIN(使用了IPC_NOWAIT,但操作不能继续进行)
EFAULT(sops指向的地址无效)
EIDRM(信号量集已经删除)
EINTR(当睡眠时接收到其他信号)
EINVAL(信号量集不存在,或者semid无效)
ENOMEM(使用了SEM_UNDO,但无足够的内存创建所需的数据结构)
ERANGE(信号量值超出范围)

    第一个参数是关键字值。第二个参数是指向将要操作的数组的指针。第三个参数是数组中的操作的个数。参数sops指向由sembuf组成的数组。此数组是在linux/sem.h中定义的:

/*semop systemcall takes an array of these*/
structsembuf{
ushortsem_num;/*semaphore index in array*/
shortsem_op;/*semaphore operation*/
shortsem_flg;/*operation flags*/
sem_num将要处理的信号量的个数。
sem_op要执行的操作。
sem_flg操作标志。

    如果sem_op是负数,那么信号量将减去它的值。这和信号量控制的资源有关。如果没有使用IPC_NOWAIT,那么调用进程将进入睡眠状态,直到信号 量控制的资源可以使用为止。如果sem_op是正数,则信号量加上它的值。这也就是进程释放信号量控制的资源。最后,如果sem_op是0,那么调用进程 将调用sleep(),直到信号量的值为0。这在一个进程等待完全空闲的资源时使用。

 

 

 

[目录]



semctl()


系统调用:semctl();
原型:int semctl(int semid,int semnum,int cmd,union semunarg);
返回值:如果成功,则为一个正数。
如果失败,则为-1:errno=EACCESS(权限不够)
EFAULT(arg指向的地址无效)
EIDRM(信号量集已经删除)
EINVAL(信号量集不存在,或者semid无效)
EPERM(EUID没有cmd的权利)
ERANGE(信号量值超出范围)

    系统调用semctl用来执行在信号量集上的控制操作。这和在消息队列中的系统调用msgctl是十分相似的。但这两个系统调用的参数略有不同。因为信号 量一般是作为一个信号量集使用的,而不是一个单独的信号量。所以在信号量集的操作中,不但要知道IPC关键字值,也要知道信号量集中的具体的信号量。这两 个系统调用都使用了参数cmd,它用来指出要操作的具体命令。两个系统调用中的最后一个参数也不一样。在系统调用msgctl中,最后一个参数是指向内核 中使用的数据结构的指针。我们使用此数据结构来取得有关消息队列的一些信息,以及设置或者改变队列的存取权限和使用者。但在信号量中支持额外的可选的命 令,这样就要求有一个更为复杂的数据结构。
系统调用semctl()的第一个参数是关键字值。第二个参数是信号量数目。

    参数cmd中可以使用的命令如下:
    ·IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
    ·IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
    ·IPC_RMID将信号量集从内存中删除。
    ·GETALL用于读取信号量集中的所有信号量的值。
    ·GETNCNT返回正在等待资源的进程数目。
    ·GETPID返回最后一个执行semop操作的进程的PID。
    ·GETVAL返回信号量集中的一个单个的信号量的值。
    ·GETZCNT返回这在等待完全空闲的资源的进程数目。
    ·SETALL设置信号量集中的所有的信号量的值。
    ·SETVAL设置信号量集中的一个单独的信号量的值。

    参数arg代表一个semun的实例。semun是在linux/sem.h中定义的:
/*arg for semctl systemcalls.*/
unionsemun{
intval;/*value for SETVAL*/
structsemid_ds*buf;/*buffer for IPC_STAT&IPC_SET*/
ushort*array;/*array for GETALL&SETALL*/
structseminfo*__buf;/*buffer for IPC_INFO*/
void*__pad;

    val当执行SETVAL命令时使用。buf在IPC_STAT/IPC_SET命令中使用。代表了内核中使用的信号量的数据结构。array在使用GETALL/SETALL命令时使用的指针。
    下面的程序返回信号量的值。当使用GETVAL命令时,调用中的最后一个参数被忽略:

intget_sem_val(intsid,intsemnum)
{
return(semctl(sid,semnum,GETVAL,0));
}

    下面是一个实际应用的例子:

#defineMAX_PRINTERS5
printer_usage()
{
int x;
for(x=0;x<MAX_PRINTERS;x++)
printf("Printer%d:%d/n/r",x,get_sem_val(sid,x));
}

    下面的程序可以用来初始化一个新的信号量值:

void init_semaphore(int sid,int semnum,int initval)
{
union semunsemopts;
semopts.val=initval;
semctl(sid,semnum,SETVAL,semopts);
}

    注意系统调用semctl中的最后一个参数是一个联合类型的副本,而不是一个指向联合类型的指针。

 

 

 

[目录]



共享内存


    共享内存就是由几个进程共享一段内存区域。这可以说是最快的IPC形式,因为它无须任何的中间操作(例如,管道、消息队列等)。它只是把内存段直接映射到调用进程的地址空间中。这样的内存段可以是由一个进程创建的,然后其他的进程可以读写此内存段。

    每个系统的共享内存段在系统内核中也保持着一个内部的数据结构shmid_ds。此数据结构是在linux/shm.h中定义的,如下所示:

/* One shmid data structure for each shared memory segment in the system. */
struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
time_t shm_atime; /* last attach time */
time_t shm_dtime; /* last detach time */
time_t shm_ctime; /* last change time */
unsigned short shm_cpid; /* pid of creator */
unsigned short shm_lpid; /* pid of last operator */
short shm_nattch; /* no. of current attaches */
/* the following are private */
unsigned short shm_npages; /* size of segment (pages) */
unsigned long *shm_pages; /* array of ptrs to frames -> SHMMAX */
struct vm_area_struct *attaches; /* descriptors for attaches */
};

shm_perm 是数据结构ipc_perm的一个实例。这里保存的是内存段的存取权限,和其他的有关内存段创建者的信息。
shm_segsz 内存段的字节大小。
shm_atime 最后一个进程存取内存段的时间。
shm_dtime 最后一个进程离开内存段的时间。
shm_ctime 内存段最后改动的时间。
shm_cpid 内存段创建进程的P I D。
shm_lpid 最后一个使用内存段的进程的P I D。
shm_nattch 当前使用内存段的进程总数。

[目录]



shmget()


系统调用:shmget();
原型:int shmget(key_t key,int size,int shmflg);
返回值:如果成功,返回共享内存段标识符。如果失败,则返回-1:errno=EINVAL(无效的内存段大小)
EEXIST(内存段已经存在,无法创建)
EIDRM(内存段已经被删除)
ENOENT(内存段不存在)
EACCES(权限不够)
ENOMEM(没有足够的内存来创建内存段)

    系统调用shmget()中的第一个参数是关键字值(它是用系统调用ftok()返回的)。其他的操作都要依据shmflg中的命令进行。
    ·IPC_CREAT如果系统内核中没有共享的内存段,则创建一个共享的内存段。
    ·IPC_EXCL当和IPC_CREAT一同使用时,如果共享内存段已经存在,则调用失败。
    当IPC_CREAT单独使用时,系统调用shmget()要么返回一个新创建的共享内存段的标识符,要么返回一个已经存在的共享内存段的关键字值。如果 IPC_EXCL和IPC_CREAT一同使用,则要么系统调用新创建一个共享的内存段,要么返回一个错误值-1。IPC_EXCL单独使用没有意义。

    下面是一个定位和创建共享内存段的程序:

int open_segment(key_t keyval,int segsize)
{
int shmid;
if((shmid=shmget(keyval,segsize,IPC_CREAT|0660))==-1)
{
return(-1);
}
return(shmid);
}

    一旦一个进程拥有了一个给定的内存段的有效IPC标识符,它的下一步就是将共享的内存段映射到自己的地址空间中。

 

 

 

 

[目录]



shmat()


系统调用: shmat();
原型:int shmat ( int shmid, char *shmaddr, int shmflg);
返回值:如果成功,则返回共享内存段连接到进程中的地址。如果失败,则返回- 1:errno = EINVAL (无效的IPC ID 值或者无效的地址)
ENOMEM (没有足够的内存)
EACCES (存取权限不够)

    如果参数a d d r的值为0,那么系统内核则试图找出一个没有映射的内存区域。我们推荐使用这种方法。你可以指定一个地址,但这通常是为了加快对硬件设备的存取,或者解决和其他程序的冲突。
    下面的程序中的调用参数是一个内存段的I P C标识符,返回内存段连接的地址:

char *attach_segment(int shmid)
{
return(shmat(shmid, 0, 0));
}

    一旦内存段正确地连接到进程以后,进程中就有了一个指向该内存段的指针。这样,以后就可以使用指针来读取此内存段了。但一定要注意不能丢失该指针的初值。

 

 

 

[目录]



shmctl()


系统调用:shmctl ( ) ;
原型:int shmctl( int shmqid, int cmd, struct shmid_ds *buf );
返回值: 0 ,如果成功。
-1,如果失败:errno = EACCES (没有读的权限,同时命令是IPC_STAT)
EFAULT(buf指向的地址无效,同时命令是IPC_SET和IPC_STAT )
EIDRM (内存段被移走)
EINVAL (shmqid 无效)
EPERM (使用IPC_SET 或者IPC_RMID 命令,但调用进程没有写的权限)
IPC_STAT 读取一个内存段的数据结构shmid_ds,并将它存储在buf参数指向的地址中。
IPC_SET 设置内存段的数据结构shmid_ds中的元素ipc_perm的值。从参数buf中得到要设置的值。
IPC_RMID 标志内存段为移走。

    命令IPC_RMID并不真正从系统内核中移走共享的内存段,而是把内存段标记为可移除。进程调用系统调用shmdt()脱离一个共享的内存段。

 

 

 

[目录]



shmdt()


系统调用:shmdt();
调用原型:int shmdt ( char *shmaddr );
返回值:如果失败,则返回- 1:errno = EINVAL (无效的连接地址)

    当一个进程不在需要共享的内存段时,它将会把内存段从其地址空间中脱离。但这不等于将共享内存段从系统内核中移走。当进程脱离成功后,数据结构shmid_ds中元素shm_nattch将减1。当此数值减为0以后,系统内核将物理上把内存段从系统内核中移走。

 

 

 

[目录]



线程


    线程通常叫做轻型的进程。虽然这个叫法有些简单化,但这有利于了解线程的概念。因为线程和进程比起来很小,所以相对来说,线程花费更少的CPU资源。进程 往往需要它们自己的资源,但线程之间可以共享资源,所以线程更加节省内存。Mach的线程使得程序员可以编写并发运行的程序,而这些程序既可以运行在单处 理器的机器上,也可以运行在多处理器的机器中。另外,在单处理器环境中,当应用程序执行容易引起阻塞和延迟的操作时,线程可以提高效率。

    用子函数pthread_create创建一个新的线程。它有四个参数:一个用来保存线程的线程变量、一个线程属性、当线程执行时要调用的函数和一个此函数的参数。例如:
pthread_ta_thread ;
pthread_attr_ta_thread_attribute ;
void thread_function(void *argument);
char * some_argument;
pthread_create( &a_thread, a_thread_attribute, (void *)&thread_function,
(void *) &some_argument);
    线程属性只指明了需要使用的最小的堆栈大小。在以后的程序中,线程的属性可以指定其他的值,但现在大部分的程序可以使用缺省值。不像UNIX系统中使用 fork系统调用创建的进程,它们和它们的父进程使用同一个执行点,线程使用在pthread_create中的参数指明要开始执行的函数。

    现在我们可以编制第一个程序了。我们编制一个多线程的应用程序,在标准输出中打印“Hello Wo r l d”。首先我们需要两个线程变量,一个新线程开始执行时可以调用的函数。我们还需要指明每一个线程应该打印的信息。一个做法是把要打印的字符串分开,给每 一个线程一个字符串作为开始的参数。请看下面的代码:
void print_message_function( void *ptr );
main( )
{
pthread_t thread1, thread2;
char *message1 = "Hello";
char *message2 = "Wo r l d " ;
pthread_create( &thread1, pthread_attr_default,
(void*)&print_message_function, (void*) message1);
pthread_create(&thread2, pthread_attr_default,
(void*)&print_message_function, (void*) message2);
exit( 0 ) ;
}
void print_message_function( void *ptr )
{
char *message;
message = (char *) ptr;
printf("%s ", message);
}

    程序通过调用pthread_create创建第一个线程,并将“Hello”作为它的启动参数。第二个线程的参数是“World”。当第一个线程开始执 行时,它使用参数“Hello”执行函数print_message_function。它在标准输出中打印“Hello”,然后结束对函数的调用。线程 当离开它的初始化函数时就将终止,所以第一个线程在打印完“Hello”后终止。当第二个线程执行时,它打印“World”然后终止。但这个程序有两个主 要的缺陷。
    首先也是最重要的是线程是同时执行的。这样就无法保证第一个线程先执行打印语句。所以你很可能在屏幕上看到“World Hello”,而不是“Hello World”。请注意对exit的调用是父线程在主程序中使用的。这样,如果父线程在两个子线程调用打印语句之前调用exit,那么将不会有任何的打印输 出。这是因为exit函数将会退出进程,同时释放任务,所以结束了所有的线程。任何线程(不论是父线程或者子线程)调用exit 都会终止所有其他线程。如果希望线程分别终止,可以使用pthread_exit函数。
我们可以使用一个办法弥补此缺陷。我们可以在父线程中插入一个延迟程序,给子线程足够的时间完成打印的调用。同样,在调用第二个之前也插入一个延迟程序保证第一个线程在第二个线程执行之前完成任务。

void print_message_function( void *ptr );
main ( )
{
pthread_t thread1, thread2;
char *message1 = "Hello”;
char *message2 = "Wo r l d " ;
pthread_create( &thread1, pthread_attr_default,
(void *) &print_message_function, (void *) message1);
sleep (10) ;
pthread_create(&thread2, pthread_attr_default,
(void *) &print_message_function, (void *) message2);
sleep ( 10 ) ;
exit (0) ;
}
void print_message_function( void *ptr )
{
char *message;
message = (char *) ptr;
printf("%s", message);
pthread_exit(0) ;
}

    这样是否达到了我们的要求了呢?不尽如此,因为依靠时间的延迟执行同步是不可靠的。这里遇到的情形和一个分布程序和共享资源的情形一样。共享的资源是标准的输出设备,分布计算的程序是三个线程。
其实这里还有另外一个错误。函数sleep和函数e x i t一样和进程有关。当线程调用sleep时,整个的进程都处于睡眠状态,也就是说,所有的三个线程都进入睡眠状态。这样我们实际上没有解决任何的问题。希 望使一个线程睡眠的函数是pthread_delay_np。例如让一个线程睡眠2秒钟,用如下程序:

struct timespec delay;
delay.tv_sec = 2;
delay.tv_nsec = 0;
pthread_delay_np( &delay );
}

[目录]



线程同步


POSIX提供两种线程同步的方法,mutex和条件变量。mutex是一种简单的加锁的方法来控制对共享资源的存取。我们可以创建一个读/写程序,它们共用一个共享缓冲区,使用mutex来控制对缓冲区的存取。
void reader_function(void);
void writer_function(void);
char buf f e r ;
int buffer_has_item = 0;
pthread_mutex_t mutex;
struct timespec delay;
main( )
{
pthread_t reader;
delay.tv_sec = 2;
delay.tv_nsec = 0;
pthread_mutex_init(&mutex, pthread_mutexattr_default);
pthread_create( &reader, pthread_attr_default, (void*)&reader_function,
N U L L ) ;
writer_function( )
void writer_function(void)
{
while( 1 )
{
pthread_mutex_lock( &mutex );
if ( buffer_has_item == 0 )
{
buffer = make_new_item();
buffer_has_item = 1;
}
pthread_mutex_unlock( &mutex );
pthread_delay_np( &delay );
}
}
void reader_function(void)
{
while( 1 )
{
pthread_mutex_lock( &mutex );
if ( buffer_has_item == 1)
{
consume_item( buffer );
buffer_has_item = 0;
}
pthread_mutex_unlock( &mutex );
pthread_delay_np( &delay );
}
}
在上面的程序中,我们假定缓冲区只能保存一条信息,这样缓冲区只有两个状态,有一条信息或者没有信息。使用延迟是为了避免一个线程永远占有mutex。
但mutex的缺点在于它只有两个状态,锁定和非锁定。POSIX的条件变量通过允许线程阻塞和等待另一个线程的信号方法,从而弥补了mutex的不足。当接受到一个信号时,阻塞线程将会被唤起,并试图获得相关的mutex的锁。

 

 

 

[目录]



使用信号量协调程序


我们可以使用信号量重新看一下上面的读/写程序。涉及信号量的操作是semaphore_up、semaphore_down、 semaphore_init、semaphore_destroy和semaphore_decrement。所有这些操作都只有一个参数,一个指向信 号量目标的指针。
void reader_function(void);
void writer_function(void);
char buffer ;
Semaphore writers_turn;
Semaphore readers_turn;
main( )
{
pthread_t reader;
semaphore_init( &readers_turn );
semaphore_init( &writers_turn );
/* writer must go first */
semaphore_down( &readers_turn );
pthread_create( &reader, pthread_attr_default,
(void *)&reader_function, NULL);
w r i t e r _ f u n c t i o n ( ) ;
}
void writer_function(void)
{
w h i l e ( 1 )
{
semaphore_down( &writers_turn );
b u ffer = make_new_item();
semaphore_up( &readers_turn );
}
}
void reader_function(void)
{
w h i l e ( 1 )
{
semaphore_down( &readers_turn );
consume_item( buffer );
semaphore_up( &writers_turn );
}
}
这个例子也没有完全地利用一般信号量的所有函数。我们可以使用信号量重新编写“Hello world” 的程序:
void print_message_function( void *ptr );
Semaphore child_counter;
Semaphore worlds_turn;
main( )
{
pthread_t thread1, thread2;
char *message1 = "Hello";
char *message2 = "Wo r l d " ;
semaphore_init( &child_counter );
semaphore_init( &worlds_turn );
semaphore_down( &worlds_turn ); /* world goes second */
semaphore_decrement( &child_counter ); /* value now 0 */
semaphore_decrement( &child_counter ); /* value now -1 */
/*
* child_counter now must be up-ed 2 times for a thread blocked on it
* to be released
*
* /
pthread_create( &thread1, pthread_attr_default,
(void *) &print_message_function, (void *) message1);
semaphore_down( &worlds_turn );
pthread_create(&thread2, pthread_attr_default,
(void *) &print_message_function, (void *) message2);
semaphore_down( &child_counter );
/* not really necessary to destroy since we are exiting anyway */
semaphore_destroy ( &child_counter );
semaphore_destroy ( &worlds_turn );
e x i t ( 0 ) ;
}
void print_message_function( void *ptr )
{
char *message;
message = (char *) ptr;
printf("%s ", message);
fflush(stdout);
semaphore_up( &worlds_turn );
semaphore_up( &child_counter );
p t h r e a d _ e x i t ( 0 ) ;
}
信号量c h i l d _ c o u n t e r用来强迫父线程阻塞,直到两个子线程执行完p r i n t f语句和其后的semaphore_up( &child_counter )语句才继续执行。

Semaphore.h

#ifndef SEMAPHORES
#define SEMAPHORES
#include
#include
typedef struct Semaphore
{
int v;
pthread_mutex_t mutex;
pthread_cond_t cond;
}
S e m a p h o r e ;
int semaphore_down (Semaphore * s);
int semaphore_decrement (Semaphore * s);
int semaphore_up (Semaphore * s);
void semaphore_destroy (Semaphore * s);
void semaphore_init (Semaphore * s);
int semaphore_value (Semaphore * s);
int tw_pthread_cond_signal (pthread_cond_t * c);
int tw_pthread_cond_wait (pthread_cond_t * c, pthread_mutex_t * m);
int tw_pthread_mutex_unlock (pthread_mutex_t * m);
int tw_pthread_mutex_lock (pthread_mutex_t * m);
void do_error (char *msg);
# e n d i f

Semaphore.c

#include "semaphore.h"
/ *
* function must be called prior to semaphore use.
*
* /
v o i d
semaphore_init (Semaphore * s)
{
s->v = 1;
if (pthread_mutex_init (&(s->mutex), pthread_mutexattr_default) == -1)
do_error ("Error setting up semaphore mutex");
if (pthread_cond_init (&(s->cond), pthread_condattr_default) == -1)
do_error ("Error setting up semaphore condition signal");
* function should be called when there is no longer a need for
* the semaphore.
*
* /
v o i d
semaphore_destroy (Semaphore * s)
{
if (pthread_mutex_destroy (&(s->mutex)) == -1)
do_error ("Error destroying semaphore mutex");
if (pthread_cond_destroy (&(s->cond)) == -1)
do_error ("Error destroying semaphore condition signal");
}
/ *
* function increments the semaphore and signals any threads that
* are blocked waiting a change in the semaphore.
*
* /
i n t
semaphore_up (Semaphore * s)
{
int value_after_op;
tw_pthread_mutex_lock (&(s->mutex));
( s - > v ) + + ;
value_after_op = s->v;
tw_pthread_mutex_unlock (&(s->mutex));
tw_pthread_cond_signal (&(s->cond));
return (value_after_op);
}
/ *
* function decrements the semaphore and blocks if the semaphore is
* <= 0 until another thread signals a change.
*
* /
i n t
semaphore_down (Semaphore * s)
{
int value_after_op;
tw_pthread_mutex_lock (&(s->mutex));
while (s->v <= 0)
{
tw_pthread_cond_wait (&(s->cond), &(s->mutex));
}
( s - > v ) - - ;
value_after_op = s->v;
tw_pthread_mutex_unlock (&(s->mutex));
return (value_after_op);
}
/ *
* function does NOT block but simply decrements the semaphore.
* should not be used instead of down -- only for programs where
* multiple threads must up on a semaphore before another thread
* can go down, i.e., allows programmer to set the semaphore to
* a negative value prior to using it for synchronization.
*
* /
i n t
semaphore_decrement (Semaphore * s)
{
int value_after_op;
tw_pthread_mutex_lock (&(s->mutex));
s - > v - - ;
value_after_op = s->v;
tw_pthread_mutex_unlock (&(s->mutex));
return (value_after_op);
}
/ *
* function returns the value of the semaphore at the time the
* critical section is accessed. obviously the value is not guarenteed
* after the function unlocks the critical section. provided only
* for casual debugging, a better approach is for the programmar to
* protect one semaphore with another and then check its value.
* an alternative is to simply record the value returned by semaphore_up
* or semaphore_down.
*
* /
i n t
semaphore_value (Semaphore * s)
{
/* not for sync */
int value_after_op;
tw_pthread_mutex_lock (&(s->mutex));
value_after_op = s->v;
tw_pthread_mutex_unlock (&(s->mutex));
return (value_after_op);
}
/* -------------------------------------------------------------------- */
/* The following functions replace standard library functions in that */
/* they exit on any error returned from the system calls. Saves us */
/* from having to check each and every call above. */
/* -------------------------------------------------------------------- */
i n t
tw_pthread_mutex_unlock (pthread_mutex_t * m)
{
int return_value;
if ((return_value = pthread_mutex_unlock (m)) == -1)
do_error ("pthread_mutex_unlock");
return (return_value);
}
i n t
tw_pthread_mutex_lock (pthread_mutex_t * m)
{
int return_value;
if ((return_value = pthread_mutex_lock (m)) == -1)
do_error ("pthread_mutex_lock");
return (return_value);
}
i n t
tw_pthread_cond_wait (pthread_cond_t * c, pthread_mutex_t * m)
{
int return_value;
if ((return_value = pthread_cond_wait (c, m)) == -1)
do_error ("pthread_cond_wait");
return (return_value);
}
i n t
tw_pthread_cond_signal (pthread_cond_t * c)
{
int return_value;
if ((return_value = pthread_cond_signal (c)) == -1)
do_error ("pthread_cond_signal");
return (return_value);
}
/ *
* function just prints an error message and exits
*
* /
v o i d
do_error (char *msg)
{
perror (msg);
exit (1);
}

 

 

 

[目录]



代码例子

 

[目录]



newthread


/***********************************************************************
    Case study source code from the book `The Linux A to Z'
    by Phil Cornes. Published by Prentice Hall, 1996.
    Copyright (C) 1996 Phil Cornes

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/

new_thread(int (*start_addr)(void), int stack_size)
{
    struct context *ptr;
    int esp;

    /* 1 */
    if (!(ptr = (struct context *)malloc(sizeof(struct context))))
        return 0;

    /* 2 */
    if (!(ptr->stack = (char *)malloc(stack_size)))
        return 0;

    /* 3 */
    esp = (int)(ptr->stack+(stack_size-4));
    *(int *)esp = (int)exit_thread;
    *(int *)(esp-4) = (int)start_addr;
    *(int *)(esp-8) = esp-4;
    ptr->ebp = esp-8;

    /* 4 */
    if (thread_count++)
    {
        /* 5 */
        ptr->next = current->next;
        ptr->prev = current;
        current->next->prev = ptr;
        current->next = ptr;
    }
    else
    {
        /* 6 */
        ptr->next = ptr;
        ptr->prev = ptr;
        current = ptr;
        switch_context(&main_thread, current);
    }

    return 1;
}

 

 

 

[目录]



exitthead


/***********************************************************************
    Case study source code from the book `The Linux A to Z'
    by Phil Cornes. Published by Prentice Hall, 1996.
    Copyright (C) 1996 Phil Cornes

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/

static exit_thread(void)
{
    struct context dump, *ptr;

    /* 1 */
    if (--thread_count)
    {
        /* 2 */
        ptr = current;
        current->prev->next = current->next;
        current->next->prev = current->prev;
        current = current->next;
        free(ptr->stack);
        free(ptr);
        switch_context(&dump, current);
    }
    else
    {
        /* 3 */
        free(current->stack);
        free(current);
        switch_context(&dump, &main_thread);
    }
}

 

 

 

[目录]



getchannel


/***********************************************************************
    Case study source code from the book `The Linux A to Z'
    by Phil Cornes. Published by Prentice Hall, 1996.
    Copyright (C) 1996 Phil Cornes

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/

get_channel(int number)
{
    struct channel *ptr;

    /* 1 */
    for (ptr = channel_list; ptr; ptr = ptr->link)
        if (ptr->number==number)
            return((int)ptr);

    /* 2 */
    if (!(ptr = (struct channel *)malloc(sizeof(struct channel))))
        return 0;

    /* 3 */
    ptr->number = number;
    ptr->message_list = 0;
    ptr->message_tail = 0;
    ptr->sr_flag = 0;
    ptr->link = channel_list;
    channel_list = ptr;
    return((int)ptr);
}

 

 

 

[目录]



def


/***********************************************************************
    Case study source code from the book `The Linux A to Z'
    by Phil Cornes. Published by Prentice Hall, 1996.
    Copyright (C) 1996 Phil Cornes

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/

#include <string.h>

struct context                     /* One structure for each thread */
{
    int ebp;            /* Base pointer (stack frame pointer) store */
    char *stack;        /* Pointer to memory block for thread stack */
    struct context *next;      /* Round robin circular list pointer */
    struct context *prev;      /* Round robin circular list pointer */
};

struct channel      /* One structure for each communication channel */
{
    int number;                                   /* Channel number */
    int sr_flag;     /* 0=no queue, 1=send queued, 2=receive queued */
    struct channel *link;           /* Link to next channel in list */
    struct message *message_list;          /* Head of message queue */
    struct message *message_tail;          /* Tail of message queue */
};

struct message       /* One structure for each pending send/receive */
{
    int size;                           /* Size of message in bytes */
    char *addr;                      /* Pointer to start of message */
    struct message *link;          /* Link to next message in queue */
    struct context *thread;   /* Which thread blocks on this struct */
};

static struct context main_thread;    /* Storage for main() details */
static struct context *current;       /* Currently executing thread */
static int thread_count = 0;       /* Number of threads to schedule */
static struct channel *channel_list = 0;    /* List of all channels */

static int switch_context(struct context *, struct context *);
static int exit_thread(void);
static int rendezvous(struct channel *, char *, int, int);

 

 

 

[目录]



release


/***********************************************************************
    Case study source code from the book `The Linux A to Z'
    by Phil Cornes. Published by Prentice Hall, 1996.
    Copyright (C) 1996 Phil Cornes

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/

release(void)
{
    /* 1 */
    if (thread_count<=1)
        return 0;

    /* 2 */
    current = current->next;
    switch_context(current->prev, current);
    return 1;
}


static switch_context(struct context *from, struct context *to)
{
    /* 3 */
    __asm__
    (
        "movl 8(%ebp),%eax/n/t"
        "movl %ebp,(%eax)/n/t"
        "movl 12(%ebp),%eax/n/t"
        "movl (%eax),%ebp/n/t"
    );
}

 

 

 

[目录]



redezvous


/***********************************************************************
    Case study source code from the book `The Linux A to Z'
    by Phil Cornes. Published by Prentice Hall, 1996.
    Copyright (C) 1996 Phil Cornes

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/

send(int chan, char *addr, int size)
{
    /* 1 */
    return(rendezvous((struct channel *)chan, addr, size, 1));
}


receive(int chan, char *addr, int size)
{
    /* 2 */
    return(rendezvous((struct channel *)chan, addr, size, 2));
}


static int rendezvous(struct channel *chan, char *addr,
int size, int sr_flag)
{
    struct message *ptr;
    int nbytes;

    /* 3 */
    if (sr_flag==3-chan->sr_flag)
    {
        /* 4 */
        ptr = chan->message_list;
        chan->message_list = ptr->link;
        ptr->thread->next = current->next;
        ptr->thread->prev = current;
        current->next->prev = ptr->thread;
        current->next = ptr->thread;
        ++thread_count;
        /* 5 */
        nbytes = (size<ptr->size)?size:ptr->size;
        ptr->size = nbytes;

        /* 6 */
        if (sr_flag==1)
            memcpy(ptr->addr, addr, nbytes);
        else
            memcpy(addr, ptr->addr, nbytes);

        /* 7 */
        if (!chan->message_list)
            chan->sr_flag = 0;

        return(nbytes);
    }
    else
    {
        /* 8 */
        ptr = (struct message *)malloc(sizeof(struct message));

        if (!chan->message_list)
            chan->message_list = ptr;
        else
            chan->message_tail->link = ptr;

        chan->message_tail = ptr;
        ptr->link = 0;
        ptr->size = size;
        ptr->addr = addr;
        chan->sr_flag = sr_flag;
        ptr->thread = current;
        current->prev->next = current->next;
        current->next->prev = current->prev;

        /* 9 */
        if (--thread_count)
        {
            current = current->next;
            switch_context(ptr->thread, current);
        }
        else
            switch_context(ptr->thread, &main_thread);

        /* 10 */
        nbytes = ptr->size;
        free(ptr);
        return(nbytes);
    }
}

 

 

 

[目录]



unbouded


/***********************************************************************
    Case study source code from the book `The Linux A to Z'
    by Phil Cornes. Published by Prentice Hall, 1996.
    Copyright (C) 1996 Phil Cornes

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/

int buffer(void);
int start(void);

int ch_desc1, ch_desc2;

main(void)
{
    ch_desc1 = get_channel(1);
    ch_desc2 = get_channel(2);
    new_thread(start, 1024);
}

start(void)
{
    int i, n;

    new_thread(buffer, 1024);

    for (i = 0; i<10, ++i)
    {
        send(ch_desc1, &i, sizeof(int));
        release();
    }

    for (i = 0; i<10, ++i)
    {
        receive(ch_desc2, &n, sizeof(int));
        printf("i=%d n=%d/n", i, n);
        release();
    }
}

buffer(void)
{
    int i;

    receive(ch_desc1, &i, sizeof(int));
    new_thread(buffer, 1024);
    send(ch_desc2, &i, sizeof(int));
}

 

 

 

[目录]

关于Linux编程5:进程和线程 ex:编写一个程序,将当前系统中所有进程的PID、PPID、Name信息记录到record.txt文件中。(/proc目录下,每个纯数字组成的文件夹就是一个进程,也是该进程的PID号)所有能够被用户看到的地址均为虚拟地址,表示用户可以寻址的范围。实际地址和物理地址之间的映射由内存映射单元(MMU)完成。父进程先结束,子进程会成为孤儿进程,被int进程收养。内存存放数据对应的实际硬件物理地址。2. getpid和getppid。3. exit与_exit。4. 后台执行进程任务。 阅读详情

相关推荐

Python 进程和线程编程

在Unix/Linux下,可以使用fork()调用实现多进程。要实现跨平台的多进程,可以使用multiprocessing模块。进程间通信是通过Queue、Pipes等实现的。

悦分享 101

转载:进程和线程编程

  转载他人:  进程和线程编程 目 录 进程和线程编程 原始管道 pipe() dup()

gonghc的专栏 770

资产监测设备中130dBm的GPS信号注入天线端口,C/N0一般是多少?

笔者的团队一直从事于资产监测设备的开发,在开发过程中,涉及到很多细节的问题,针对新入职的同事来说较为陌生。同时,在团队外,还有需要对智能硬件终端开发感兴趣的用户。基于此背景下,笔者将在下面的内容主要介绍资产监测设备以及对设备中的GPS相关的问题:130dBm的GPS信号注入天线端口,C/N0一般是多少?作出详细的解答。 资产监测设备概述: 资产监测设备是基于物联网通信技术基础上的智能硬件终端,其主要作用是帮助用户监测资产在仓库、运输途中、交付整个流通环节中,提供资产的实时位置与状态参数。用户只需要登录资产

瑞奇聊通信模组 1229

多进程编程和多线程编程

多进程编程forking工作原理什么是forking进程的生命周期僵尸进程forking编程forking编程基本思路解决zombie问题 多进程编程 forking工作原理 什么是forking • fork(分岔)在Linux系统中使用非常广泛 • 当某一命令执行时,父进程(当前进程)fork出一个子进程 • 父进程将自身资源拷贝一份,命令在子进程中运行时,就具有和父进程完全一样的运行环境...

刘思成的博客 1374

统信UOS系统盘扩容保姆级教程:用GParten-分区编辑器5分钟搞定

本文提供统信UOS系统盘扩容的详细图形化教程。针对系统盘空间不足问题,重点介绍如何使用系统自带的GParten-分区编辑器,在5-10分钟内安全、直观地完成扩容操作,无需记忆复杂命令,适合所有用户快速解决存储空间告急的困扰。

e3f4g5的博客 645

空地多无人平台协同路径规划复现:从建模到Matlab仿真全解析

路径规划是机器人领域的核心基础技术,从单机避障到多平台协同,问题复杂度显著提升。空地协同规划融合无人机与无人车的异构优势,通过将地图分层建模为空中与地面栅格,并采用A*算法生成初始路径、B样条平滑以及改进遗传算法进行任务分配与时间同步优化,能够有效处理几何约束、运动学约束和避碰约束等。该技术广泛应用于区域巡逻、目标搜索、物资转运等任务场景,也是高校论文复现和工程仿真的常见课题。本文从问题建模、算法设计到Matlab代码结构,系统梳理了一套空地多无人平台协同路径规划的复现思路,同时给出坐标系校准、遗传算法收敛

weixin_32175665的博客 252

了解Python中的多线程和多进程编程

在Python中,多线程和多进程编程是非常常见的技术,它们可以帮助我们提高程序的运行效率和性能。本文将深入探讨Python中的多线程和多进程编程,包括其基本概念、使用方法以及示例代码。

2401_82881413的博客 1008

TCN(Temporal Convolutional Networks)时序卷积网络详解及代码

TCN(Temporal Convolutional Networks)时序卷积网络详解及代码

zhulewen的博客 7884

网络编程:多进程和多线程编程

初始化时,shm_lpid、shm_nattach、shm_atime、shm_dtime 设置为 0,shm_ctime 设置为当前时间;创建共享内存,所有字节都被初始化为 0,与之关联的内核数据结构为 shmid_ds 被创建和初始化;作用是创建并初始化它,会将 sem_otime 设为 0,sem_ctime 设为当前系统时间;num_sems 指定信号量集中信号量的数目;操作,会更新进程的 semadj 变量,,来跟踪进程对信号量的修改情况;创建一个新的信号量集,或者获取一个已经存在的信号量集;

weixin_44835655的博客 490

ResNet改进(51):基于轴向注意力机制的改进ResNet模型

本文介绍了一种结合轴向注意力机制的改进ResNet模型。 通过在传统CNN中引入轴向注意力,我们能够在保持计算效率的同时,增强模型对图像全局上下文的理解能力。这种混合架构结合了CNN的局部特征提取优势和注意力机制的全局建模能力,在图像识别任务中表现出色。 轴向注意力机制的应用不仅限于ResNet,也可以灵活地集成到其他CNN架构中。 未来可以探索更多注意力变体,或将其应用于特定领域任务,如医学图像分析、自动驾驶等需要精确空间理解的场景。 希望本文能帮助你理解如何将注意力机制与传统CNN结合,为你的计

2401_82355416的博客 361

华为od 面试八股文_Python_05_含答案

华为od 面试八股文_Python_05_含答案

MISAYAONE的博客 2569

Linux编程之进程和线程编程

  进程和线程编程 目 录1.      进程和线程编程 1.      原始管道 1.      pipe() 2.      dup() 3.      dup2() 4.      popen()和pclo

第三只眼的专栏 7891

华为OD机考 新系统真题 【区分奇偶的数组排序】

华为OD机试 新系统真题【区分奇偶的数组排序】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题【区分奇偶的数组排序】多语言题解。

qq_45776114的博客 515

python多线程、多进程和线程池编程

Lock:适用于需要简单互斥的场景。RLock:适用于同一个线程需要多次获取同一锁的场景,例如递归调用。正确使用这些锁可以帮助避免多线程编程中的竞态条件和死锁问题,保证数据的一致性和程序的稳定性。

NickDeCodes的博客 1043

零基础基于U-Net网络实战眼底图像血管提取

文章目录1 前言2 血管提取任务概述3 U-Net架构简介4 眼底图像血管分割代码5 结果评估可视化(ROC曲线)6 改进U-Net网络完成眼底图像血管提取任务思路 1 前言 本文基于U-Net架构完成了视网膜血管的提取任务,基于Keras框架更简单的实现分割,网络、工具类、训练和预测的代码一目了然

NJU phd 在读。 5204

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

多线程、多进程和线程池编程

GIL (global interpreter lock)全局解释器锁 基于cpython的介绍: python中的一个线程是对应于c语言中的一个线程,python在早期的时候为了简单,会在解释器上加一把非常大的锁,允许我们同一时刻只有一个线程运行在一个cpu上执行字节码。在某种程度上保证了我们的线程是安全的。它无法将多个线程映射到多个cpu上执行,使得我们无法利用多核优势。 gil使得同一时刻只...

qq_41805981的博客 344

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

进程和线程及Linux下的编程

进程和线程及Linux下的编程 一、概述 进程和线程网络上有一堆解释,我不喜欢抄袭,也不喜欢套用太教科书的说法。就以我自己的理解来说说进程和线程吧,当然自己的理解肯定不是很严谨,但是理解起来应该会比教科书快一点。进程和线程都可以认为是并发执行程序,但是只有多处理器下的多线程才可以真正实现并发(多个线程在同一个时间片同时运行),其他的实际上并不是真正的并发,都是交替在cpu上运行,只是每个程序运行的...

小强的博客 278

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

关于进程,线程,多进程和多线程的网络编程

进程线程网络 多任务编程 : 可以有效的利用计算机资源,同时执行多个任务 进程 : 进程就是程序在计算机中一次执行的过程 进程和程序的区别: 程序是一个静态文件的描述,不占计算机的系统资源 进程是一个动态的过程,占有cpu内存等资源,有一定的生命周期 * 同一个程序的不同执行过程即为不同的进程 问题1 什么决定了进程的创建 用户通过应用层程序进行进程的创建申请 ----》 调...

180

Python 并发编程全面指南(多线程 多进程 进程池 线程池 协程和异步编程) 队列

Python 并发编程指南:全面解析多线程、多进程与异步编程 本文系统介绍了 Python 中的并发编程技术,包括多线程、多进程、进程池、线程池以及协程与异步编程的区别。多线程适合 I/O 密集型任务但受 GIL 限制,多进程适合 CPU 密集型任务但开销较大。进程池和线程池提供了更高级的抽象,简化了并发管理。协程和异步编程则通过非阻塞方式高效处理 I/O 操作。文章通过代码示例详细演示了各种技术的实现方式,包括线程同步、进程间通信以及池化技术,帮助开发者根据任务类型选择最合适的并发方案。

m0_48853274的博客 1665

[并发编程]什么是进程和线程

(一)什么是进程和线程 最早的计算器其实没操作系统,只能输入,计算和输出功能,用户输入一个指令,计算机完成操作,大部分时候计算机都在等待用户输入指令,这样处理性能很低效,因为人的输入速度远远比不上计算机的运行速度. 为了解决手工操作带来的低效,批处理操作系统应运而生,批处理就是先把要执行的指令预先写下来,(写到纸上,磁盘,磁带等等),形成一个指令清单,这个指令清单就是我们说的任务,然后将任务交给计算机去执行,批处理操作系统负责读取”任务”中的指令清单并进行处理,计算机执行的过程中无需等待人手工操作,这样性能

张俊杰 的博客 503

10-多线程、多进程和线程池编程

一、多线程、多进程和线程池编程 1.1、Python中的GIL锁   CPython中,global interpreter lock(简称GIL)是一个互斥体,用于保护对Python对象的访问,从而防止多个线程一次执行Python字节码(也就是说,GIL锁每次只能允许一个线程工作,无法多个线程同时在CPU上工作)。锁定是必要的,主要是因为CPython的内存管理不是线程安全的。(但是,由于存...

微赚淘客系统开发者博客 212

java并发编程:进程和线程

java并发编程:进程和线程java并发编程:进程和线程进程的由来线程的由来java并发的问题其他参考 java并发编程:进程和线程 进程的由来 为什么有进程:现在计算机的功能已经十分丰富了,可以一边看视屏,一边浏览网页等等。但是在计算机开始出现的时候,计算机的功能十分简单,人们输入特定的指令,计算机就执行该指令,但是当人们休息或者思考的时候计算机就不工作,效率十分低下。 为了更好的利用计算机,...

gentlezuo的博客 397

Python---进程和线程、邮件编程

概念 进程 计算机程序只不过是磁盘中可执行的二进制(或去他类型)的数据 进程(有时被称为重量级进程)是程序的一次执行 每个进程都有自己的地址空间、内存以及其它记录其运行轨迹的辅助数据(称之为上下文 context) 操作系统管理在其上运行的所有进程,并为这些进程公平分配时间(CPU) 线程 线程(有时被称为轻量级进程)跟进程有些类似,不同的是所有线程运行在同一个进程中,共享相同的运行环境 一个进程中的各个线程之间共享同一片数据空间,所以线程之间可以比进程之间更方便的共享数据以及相互通讯

我不是程序猿的博客 1427

linux进程与线程6,6.1 linux进程和线程编程

《6.1 linux进程和线程编程》由会员分享,可在线阅读,更多相关《6.1 linux进程和线程编程(21页珍藏版)》请在人人文库网上搜索。1、Linux进程和线程编程 Linux 进程环境,6.1 进程,进程可以看做是一个正在执行的程序,进程需要一定的资源,如cpu时间、内存、文件和I/O设备等,来完成其任务,这些资源是在创建进程或者进程执行时分配的。 进程在多数系统是工作单元,这样的系统由一...

weixin_29630465的博客 186
上一篇: linux技巧之查找文件
下一篇: 使用system()函数要谨慎
zhsp1029
博客等级 码龄21年 115粉丝 89原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值