大三时候的系统结构实验,为了实现多进程的同步,由于变量都是进程的单独拷贝,所以超级傻的用了一个文件做标志,不同的进程对同一文件进行读写不同的数做锁。

其实POSIX_THREAD_PROCESS_SHARED支持就可以直接利用pthread_mutex_t进行进程间上锁

#include "unpheader.h"
#include <pthread.h>
#include <sys/mman.h>

static pthread_mutex_t *mptr;

void my_lock_init(void)               //锁初始化,PTHREAD_PROCESS_SHARED属性
{
int    fd;
pthread_mutexattr_t    mattr;

fd=open("/dev/zero",O_RDWR,0);
mptr = mmap(0,sizeof(pthread_mutex_t),PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);

close(fd);

pthread_mutexattr_init(&mattr);
pthread_mutexattr_setpshared(&mattr,PTHREAD_PROCESS_SHARED);
pthread_mutex_init(mptr,&mattr);
}

int main(void)
{
pid_t pid;
my_lock_init();
if((pid = fork()) == 0)            //child process
{
int i=0;
for(i=0;i<10;i++)
{
pthread_mutex_lock(mptr);
printf("child get mutex!\n");
sleep(1);
pthread_mutex_unlock(mptr);
sleep(1);
}
}
else                                    //father process
{
int i=0;
for(i=0;i<10;i++)
{
pthread_mutex_lock(mptr);
printf("father get mutex!\n");
sleep(1);
pthread_mutex_unlock(mptr);
sleep(1);
}
}
return 0;
}

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <alloca.h>

#define MAXLINE 1024

struct addrinfo *host_serv(const char *hostname,const char *service,int family,int sockettype)
{
  int n;
  struct addrinfo  hints,*res;

  bzero(&hints,sizeof(struct addrinfo));
  hints.ai_flags = AI_CANONNAME;
  hints.ai_family = family;
  hints.ai_socktype = sockettype;

  if((n = getaddrinfo(hostname,service,&hints,&res))!=0)
   return NULL;
 
 return res;
}

int tcp_connect(const char* host,const char *serv)
{
 int sockfd,n;
 struct addrinfo *res,*ressave;
 if((res=host_serv(host,serv,AF_UNSPEC,SOCK_STREAM)) == NULL)
 {
  printf("tcp_connect error for %s,%s:%s\n",host,serv,strerror(errno));
  exit(-1);
 }
 ressave=res;
 while(res != NULL)
 {
  sockfd = socket(res->ai_family,res->ai_socktype,res->ai_protocol);

  if(sockfd < 0)
   continue;
  if(connect(sockfd,res->ai_addr,res->ai_addrlen) == 0)
   break;

  close(sockfd);
  res = res -> ai_next;
 }
 if(res == NULL)
 {
  printf("tcp_connect error for %s,%s",host,serv);
  exit(-1);
 }
 freeaddrinfo(ressave);
 return sockfd;
}

int read_html(const char *host,const char *file,int fd)
{
 int sockfd,n;
 char recvline[256];
 char line[256];
 sockfd = tcp_connect(host,"80");
 int m=0;
 if(file != NULL)     
  m=snprintf(line,sizeof(line),"GET /%s HTTP/1.0\r\n\r\n",file);
 else                   //if there is no file specfied, the index.html will be got.
  m=snprintf(line,sizeof(line),"GET / HTTP/1.0\r\n\r\n");
 printf("%s\n",line);
 write(sockfd,line,m);
 while((n=read(sockfd,recvline,255)) >0)
 {
  recvline[n]=0;
  write(fd,recvline,sizeof(recvline));
 }
 close(sockfd);
 return 0;
}

void *printaddr_ip(struct addrinfo *test)
{
 if(test == NULL)
 {
  printf("the input parameter addrinfo is NULL");
  return;
 }
 while(test != NULL)
 {
  if( test -> ai_family == AF_INET)
  {
   struct sockaddr_in *sin = (struct sockaddr_in *) test-> ai_addr;
   printf("canonname:%s  IP:%s\n",test->ai_canonname,inet_ntoa( sin -> sin_addr));
   test = test -> ai_next;
  }
 }
}

int getlength(const char* str)
{
 const char *start = str;
 int i=0;
 while(*str != '\0')
 {
  i++;
  str++;
 }
 str = start;
 return i;

}

char *gethost(const char* hostfile,char *result,char *file)
{
 if( result == NULL)
  return NULL;
 const char *start = hostfile;
 const char *protocol[]={"http://","ftp://"};  //example protocol prefix
 
 int i=0;
 
 for(i=0;i< (sizeof(protocol)/sizeof(char *));i++)
 {
  if(strncmp(hostfile,protocol[i],sizeof(protocol[i])) == 0)
  {
   break;
  }
 }

 if( i != (sizeof(protocol)/sizeof(char *)) )   //find procotol match
 {
   hostfile = hostfile + getlength(protocol[i]) ;
 }

 strcpy(result,hostfile);
 char *end = strstr(result,"/");
 if( end != NULL)        //find file seperate
 {
  if( file != NULL)
   strcpy(file,end+1);
  *end = '\0';
 }
 else
  file[0]='\0';

 hostfile = start;
 return result;
}
int main(int argc,char *argv[])
{
 if(argc != 2 )
 {
  printf("usage: ./gethtml htmlpath\n");
  return -1;
 }
 
 char result[MAXLINE],file[MAXLINE];
 gethost(argv[1],result,file);
  read_html(result,file,STDOUT_FILENO);
 return 0;
}

参考unp第11章的 hostserv程序

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>

struct addrinfo *host_serv(const char *hostname,const char *service,int family,int sockettype)
{
  int n;
  struct addrinfo  hints,*res;

  bzero(&hints,sizeof(struct addrinfo));
  hints.ai_flags = AI_CANONNAME;
  hints.ai_family = family;
  hints.ai_socktype = sockettype;

  if((n = getaddrinfo(hostname,service,&hints,&res))!=0)
   return NULL;
 
 return res;
}

int main(int argc,char *argv[])
{
 if(argc != 2)
 {
  printf("usage: hostserv hostname\n");
  return -1;
 }
 struct addrinfo *test;
 test = host_serv(argv[1],NULL,AF_INET,SOCK_STREAM);
 while(test != NULL)
 {
  if( test -> ai_family == AF_INET)
  {
   struct sockaddr_in *sin = (struct sockaddr_in *) test-> ai_addr;
   printf("%s\n", inet_ntoa( sin -> sin_addr));
   test = test -> ai_next;
  }
 }
 return 0;
}

unix域协议是在单个主机上执行客户服务器通信方法,其实就是IPC的一种,但是程序的编写方式与TCP很类似,但比TCP套接口快出一倍的速度。

unix域套接口需要注意的是在UDP的unix套接口send之前需要显示的bind到一个pathname,这样服务器才有回答的路径。

unix域套接口传递描述符字采用sendmsg和recvmsg这两个函数的辅助数据msghdr进行传递,值得注意的是,在一方已经sendmsg的描述符字但是另外一端没有recvmsg之前(in flight),即使发送端关闭描述符字,接收方收到的描述符也是打开的。同时传递的是描述符字而不是一个字号,因此在接收进程中的描述符字号可能不同于发送进程。

socketpair是一个很有用的函数,很方便的创建了一个全双工的unix域套接口。

守护进程是UNIX系统中非常重要的一种进程,简单来说就是在后台运行且不与任何终端交互的进程,此种程序的的登记消息方式采用syslog函数由syslogd进程来执行。

让一个进程成为守护进程的方法为:

fork生成子进程,_exit父进程,对子进程调用setsid使得子进程成为一个新会话的会话首进程,接着屏蔽SIGHUP信号(会话首进程结束时会话组中所有进程都会收到),再次fork,依旧结束父进程,这样最后的孙子进程就不是会话首进程,然后关闭所有的描述字,并将0,1,2三个描述字都打开到/dev/null,最后openlog促使消息登记(但如果不采用LOG_NDELAY选项则unix域套接口一直到首次调用syslog才打开)。

linux下提供的将进程转为守护进程的函数为daemon, 可参考man手册。

inetd是一种常用的守护进程,它可以将很多需要服务的程序由自己一个人处理,根据/etc/inetd.conf和/etc/service的配置,inetd创建FTP,TELNET等各种等待端口,当有客户连接时根据/etc/inetd.conf的参数创建子进程或者直接调用程序服务。

经常在工作中会碰到需要一些配置文件在不同的操作系统之间转移,最基本的就是win和unix下,有时候unix服务器我们没办法在上面进行编辑,就会在win下编辑后用工具传送过去,这样就引起了一个问题。

打开unix系统本身的一些文件,执行set ff?可以看到提示fileformat=unix,而我们传递过去的是fileformat=dos,这两种系统在换行符上处理稍有不同,如果一些配置文件依赖不同的行数是不同的配置,可能就会引起错误,尤其执行的时候可能不会报警就更加隐藏了问题。我们有时候用vim打开一些文档发现里面带^M字符其实就是这个问题。

解决问题就是set ff=unix 命令,很简单。

UNP第三十章。

a) 迭代型服务器(-)

已经不具备广泛实用性,仅在各个范式的比较中起基准作用

b) 并发性服务器(子进程)

主程序进行accept等待,为每一个客户fork一个子线程进行处理。

客户数目的限制是操作系统对运行服务器程序的用户ID能同时拥有子进程数的限制。面对当前的web站点每天上百万的请求,采用多主机平摊负载。(TCPv3讨论采用DNS轮询策略)。

性能方面,由于现场fork的开销导致性能较为低下。

c) 预先派生子进程(进程池)

必须预估会有多少并发客户访问,多余的会被忽略,因此当剩余空闲进程较多时要收回,较少时要额外分配,代价过高。

i. 子进程分别accept并不进行上锁保护:

预先派生进程池,当来客户连接时由子进程进行竞争,会引起”accept惊群现象,即所有阻塞在accept的子进程都会被唤醒来竞争,但只有一个进程会返回并取得连接,其他子进程则继续阻塞等待,这种太多的子进程被唤醒会导致性能受损。

Btw:让子进程在accept(实体)上竞争的效果要远好于select上,在select上会引发select冲突。

ii. 子进程分别accept进行上锁保护

此种方式让所有进程阻塞在锁的获取而不是accept套接口上,这样就会避免当连接到来时的accept的惊群作用。

上锁方式有两种,使用文件上锁,利用fcntl函数(SETLKGETLK),另外一种采用线程上锁,需要将pthread_mutex_tmmap映射到共享的内存区,然后设置pthread_mutexattr_tPTHREAD_PROCESS_SHARED即可。后者比较方便并且不涉及文件操作,性能上会提升不少。

iii. 主线程等待并进行描述字分配。

这种技术导致父进程必须跟踪子进程的状态并采用unix域或者pipe进行描述符传送,UNP上采用socketpair()函数。如果为了使得各个子进程处理平衡,还得涉及算法平衡各个子进程的负载均衡,但是通常这样做没有必要,对于多个空闲子进程而言,谁处理连接并没有效率影响。

并且描述符的传递的效率要远低于采用锁的机制,除了父进程传递描述字,子进程处理完后还得传递状态。

d) 并发性服务器(线程)

一般线程操作会比进程更快一点,因此在支持线程的系统上采用线程是较好的方式。简单的现场生成新线程的范式也要快于进程池的版本(SolarisDigital Unix上,根据UNP的描述)

e) 并发性服务器(线程池)

i. 每个线程各自accept

同样预先分配线程也会快于现场生成新线程,并且使用互斥锁将线程阻塞在锁上,此种方式的各个线程的负载均衡性是由线程调度算法导致,会在互斥锁上轮询所有线程。

此种方式的速度是所有范式中最快的。

ii. 主线程accept并分配描述符字给子线程

实际上采用的是一个描述符字数组,主线程accept一个就往队列中添加一个,子线程取出一个就从队列中删除一个。对队列的添加和删除操作进行加锁保护。

速度慢于上面各线程分别accept的版本。

就是短信的时间是接受的时间而不是发送的时间,本来问题不大,主要是关机,断点或者网络延迟时候收到短信却不知道对方是什么时候发的,苦恼啊….

貌似是所有的symbian s60v3rd都有的问题,但好像不确定,论坛上同一机型有些人有问题有些人没有,即使是同一版本的系统。

n78 欧水, 12.046版,谁有解决方案给来一个。